From e83f4f40f935a4bee6371b16cf6e84e265e93ddd Mon Sep 17 00:00:00 2001 From: Henri Verbeet Date: Fri, 28 Feb 2025 14:53:01 +0100 Subject: [PATCH] vkd3d-shader/spirv: Handle "IdRef" operands. --- libs/vkd3d-shader/make_spirv | 51 ++++++++++++++++++++- libs/vkd3d-shader/spirv.c | 87 +++++++++++++++++++++++++++++++++--- 2 files changed, 131 insertions(+), 7 deletions(-) diff --git a/libs/vkd3d-shader/make_spirv b/libs/vkd3d-shader/make_spirv index 127945d7..9adb8103 100755 --- a/libs/vkd3d-shader/make_spirv +++ b/libs/vkd3d-shader/make_spirv @@ -27,6 +27,16 @@ sub opcode_id($) sprintf "0x%04x", shift; } +sub fix_name($) +{ + shift =~ s/([A-Z]+)/_$1/rg; +} + +sub operand_type_name(_) +{ + "SPIRV_PARSER_OPERAND_TYPE${\uc fix_name shift}"; +} + sub grammar_version($) { my ($grammar) = @_; @@ -34,17 +44,37 @@ sub grammar_version($) "$grammar->{major_version}.$grammar->{minor_version}.$grammar->{revision}"; } +sub instruction_operand(_) +{ + my ($operand) = @_; + + "{${\operand_type_name $operand->{kind}}" . (defined $operand->{quantifier} ? ", '$operand->{quantifier}'}" : "}"); +} + sub print_opcode_info(_) { my ($instruction) = @_; my $operand_count = @{$instruction->{operands} // []}; - print " {${\opcode_id $instruction->{opcode}}, \"$instruction->{opname}\", $operand_count},\n"; + if (!$operand_count) + { + print " {${\opcode_id $instruction->{opcode}}, \"$instruction->{opname}\"},\n"; + return; + } + + print " {\n"; + print " ${\opcode_id $instruction->{opcode}}, \"$instruction->{opname}\", $operand_count,\n"; + print " (struct spirv_parser_instruction_operand[])\n"; + print " {\n"; + print " ${\instruction_operand},\n" foreach @{$instruction->{operands}}; + print " }\n"; + print " },\n"; } sub print_header($) { my ($grammar) = @_; + my @operand_types = sort {$a->{kind} cmp $b->{kind}} @{$grammar->{operand_kinds}}; print "/* This file is automatically generated from version ${\grammar_version $grammar} of the\n"; print " * machine-readable SPIR-V grammar.\n"; @@ -54,11 +84,30 @@ sub print_header($) print map {" * $_" =~ s/ +$//r . "\n"} @{$grammar->{copyright}}; print " */\n\n"; + print "enum spirv_parser_operand_type\n"; + print "{\n"; + print " ${\operand_type_name $_->{kind}},\n" foreach @operand_types; + print "};\n\n"; + + print "static const struct spirv_parser_operand_type_info\n"; + print "{\n"; + print " const char *name;\n"; + print "}\n"; + print "spirv_parser_operand_type_info[] =\n"; + print "{\n"; + print " [${\operand_type_name $_->{kind}}] = {\"$_->{kind}\"},\n" foreach @operand_types; + print "};\n\n"; + print "static const struct spirv_parser_opcode_info\n"; print "{\n"; print " uint16_t op;\n"; print " const char *name;\n"; print " size_t operand_count;\n"; + print " const struct spirv_parser_instruction_operand\n"; + print " {\n"; + print " enum spirv_parser_operand_type type;\n"; + print " char quantifier;\n"; + print " } *operands;\n"; print "}\n"; print "spirv_parser_opcode_info[] =\n"; print "{\n"; diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 0e7fb740..591197c6 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -216,6 +216,7 @@ struct spirv_colours const char *comment; const char *literal; const char *opcode; + const char *id; }; struct spirv_parser @@ -312,6 +313,13 @@ static void spirv_parser_print_immediate_word(struct spirv_parser *parser, prefix, parser->colours.literal, w, parser->colours.reset, suffix); } +static void spirv_parser_print_id(struct spirv_parser *parser, + struct vkd3d_string_buffer *buffer, const char *prefix, uint32_t id, const char *suffix) +{ + vkd3d_string_buffer_printf(buffer, "%s%%%s%u%s%s", + prefix, parser->colours.id, id, parser->colours.reset, suffix); +} + static void spirv_parser_print_opcode(struct spirv_parser *parser, struct vkd3d_string_buffer *buffer, const char *name) { @@ -325,6 +333,13 @@ static void spirv_parser_print_instruction_offset(struct spirv_parser *parser, parser->colours.comment, offset * sizeof(uint32_t), parser->colours.reset, suffix); } +static const struct spirv_parser_operand_type_info *spirv_parser_get_operand_type_info(enum spirv_parser_operand_type t) +{ + if (t >= ARRAY_SIZE(spirv_parser_operand_type_info)) + return NULL; + return &spirv_parser_operand_type_info[t]; +} + static int spirv_parser_opcode_info_compare(const void *key, const void *element) { const struct spirv_parser_opcode_info *e = element; @@ -406,6 +421,37 @@ static enum vkd3d_result spirv_parser_read_header(struct spirv_parser *parser) return VKD3D_OK; } +static bool spirv_parser_parse_operand(struct spirv_parser *parser, struct vkd3d_string_buffer *buffer, + const char *opcode_name, enum spirv_parser_operand_type type, size_t end) +{ + const struct spirv_parser_operand_type_info *info; + + if (parser->pos >= end) + { + spirv_parser_warning(parser, VKD3D_SHADER_ERROR_SPV_NOT_IMPLEMENTED, + "Insufficient words remaining while parsing operands for instruction \"%s\".", opcode_name); + return false; + } + + if (!(info = spirv_parser_get_operand_type_info(type))) + { + ERR("Invalid operand type %#x.\n", type); + return false; + } + + switch (type) + { + case SPIRV_PARSER_OPERAND_TYPE_ID_REF: + spirv_parser_print_id(parser, buffer, " ", spirv_parser_read_u32(parser), ""); + return true; + + default: + spirv_parser_warning(parser, VKD3D_SHADER_ERROR_SPV_NOT_IMPLEMENTED, + "Unhandled operand type \"%s\".", info->name); + return false; + } +} + static void spirv_parser_parse_raw_instruction(struct spirv_parser *parser, uint16_t count) { size_t pos = parser->pos; @@ -424,17 +470,19 @@ static void spirv_parser_parse_raw_instruction(struct spirv_parser *parser, uint static enum vkd3d_result spirv_parser_parse_instruction(struct spirv_parser *parser) { + const struct spirv_parser_instruction_operand *operand; const struct spirv_parser_opcode_info *info; - uint16_t op, count; + struct vkd3d_string_buffer *operands; + uint16_t op, count, rem; + size_t end, pos, i; uint32_t word; - size_t pos; pos = parser->pos; word = spirv_parser_read_u32(parser); count = (word & VKD3D_SPIRV_INSTRUCTION_WORD_COUNT_MASK) >> VKD3D_SPIRV_INSTRUCTION_WORD_COUNT_SHIFT; op = (word & VKD3D_SPIRV_INSTRUCTION_OP_MASK) >> VKD3D_SPIRV_INSTRUCTION_OP_SHIFT; - if (!count) + if (!count || count > parser->size - pos) { spirv_parser_error(parser, VKD3D_SHADER_ERROR_SPV_INVALID_SHADER, "Invalid word count %u.", count); @@ -448,20 +496,45 @@ static enum vkd3d_result spirv_parser_parse_instruction(struct spirv_parser *par goto raw; } - if (info->operand_count) + operands = vkd3d_string_buffer_get(&parser->string_buffers); + + for (i = 0, end = pos + count; i < info->operand_count; ++i) + { + operand = &info->operands[i]; + + do + { + if (parser->pos >= end && (operand->quantifier == '?' || operand->quantifier == '*')) + break; + + if (!spirv_parser_parse_operand(parser, operands, info->name, operand->type, end)) + { + vkd3d_string_buffer_release(&parser->string_buffers, operands); + goto raw; + } + } while (operand->quantifier == '*' && parser->pos < end); + } + + if ((rem = end - parser->pos)) { spirv_parser_warning(parser, VKD3D_SHADER_ERROR_SPV_NOT_IMPLEMENTED, - "Operands for instruction \"%s\" not handled.", info->name); - goto raw; + "%u word(s) remaining after parsing all operands for instruction \"%s\"", rem, info->name); + for (i = 0; i < rem; ++i) + { + spirv_parser_print_immediate_word(parser, operands, " ", spirv_parser_read_u32(parser), ""); + } } if (parser->formatting & VKD3D_SHADER_COMPILE_OPTION_FORMATTING_INDENT) vkd3d_string_buffer_printf(parser->text, "%*s", VKD3D_SPIRV_INDENT, ""); spirv_parser_print_opcode(parser, parser->text, info->name); + vkd3d_string_buffer_printf(parser->text, "%s", operands->buffer); if (parser->formatting & VKD3D_SHADER_COMPILE_OPTION_FORMATTING_OFFSETS) spirv_parser_print_instruction_offset(parser, parser->text, " ", pos, ""); vkd3d_string_buffer_printf(parser->text, "\n"); + vkd3d_string_buffer_release(&parser->string_buffers, operands); + return VKD3D_OK; raw: @@ -523,6 +596,7 @@ static enum vkd3d_result spirv_parser_init(struct spirv_parser *parser, const st .comment = "", .literal = "", .opcode = "", + .id = "", }; static const struct spirv_colours colours = { @@ -530,6 +604,7 @@ static enum vkd3d_result spirv_parser_init(struct spirv_parser *parser, const st .comment = "\x1b[36m", .literal = "\x1b[95m", .opcode = "\x1b[96;1m", + .id = "\x1b[96m", }; memset(parser, 0, sizeof(*parser));