From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 381 ++++++++++++++++++++++- libs/vkd3d-shader/vkd3d_shader_private.h | 2 + 2 files changed, 382 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 1ae4c31ac..57eda5e8a 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -20,6 +20,7 @@
#define VKD3D_SM6_VERSION_MAJOR(version) (((version) >> 4) & 0xf) #define VKD3D_SM6_VERSION_MINOR(version) (((version) >> 0) & 0xf) +#define VKD3D_SM6_MAX_METADATA_TABLES 4
#define BITCODE_MAGIC VKD3D_MAKE_TAG('B', 'C', 0xc0, 0xde) #define DXIL_OP_MAX_OPERANDS 17 @@ -114,6 +115,19 @@ enum bitcode_function_code FUNC_CODE_INST_CMPXCHG = 46, };
+enum bitcode_metadata_code +{ + METADATA_STRING = 1, + METADATA_VALUE = 2, + METADATA_NODE = 3, + METADATA_NAME = 4, + METADATA_DISTINCT_NODE = 5, + METADATA_KIND = 6, + METADATA_LOCATION = 7, + METADATA_NAMED_NODE = 10, + METADATA_ATTACHMENT = 11, +}; + enum bitcode_type_code { TYPE_CODE_NUMENTRY = 1, @@ -278,6 +292,46 @@ struct dxil_block size_t record_count; };
+enum sm6_metadata_type +{ + VKD3D_METADATA_KIND, + VKD3D_METADATA_NODE, + VKD3D_METADATA_STRING, + VKD3D_METADATA_VALUE, +}; + +struct sm6_metadata_node +{ + bool is_distinct; + unsigned int operand_count; + struct sm6_metadata_value *operands[]; +}; + +struct sm6_metadata_kind +{ + uint64_t id; + char *name; +}; + +struct sm6_metadata_value +{ + enum sm6_metadata_type type; + const struct sm6_type *value_type; + union + { + char *string_value; + const struct sm6_value *value; + struct sm6_metadata_node *node; + struct sm6_metadata_kind kind; + } u; +}; + +struct sm6_named_metadata +{ + char *name; + struct sm6_metadata_value value; +}; + struct sm6_parser { const uint32_t *ptr, *start, *end; @@ -292,6 +346,7 @@ struct sm6_parser
struct sm6_type *types; size_t type_count; + struct sm6_type *metadata_type;
struct sm6_symbol *global_symbols; size_t global_symbol_count; @@ -302,6 +357,11 @@ struct sm6_parser struct sm6_function *functions; size_t function_count;
+ struct sm6_metadata_value *metadata_tables[VKD3D_SM6_MAX_METADATA_TABLES]; + unsigned int metadata_counts[VKD3D_SM6_MAX_METADATA_TABLES]; + struct sm6_named_metadata *named_metadata; + unsigned int named_metadata_count; + struct sm6_value *values; size_t value_count; size_t value_capacity; @@ -1173,6 +1233,7 @@ static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6)
case TYPE_CODE_METADATA: type->class = TYPE_CLASS_METADATA; + sm6->metadata_type = type; break;
case TYPE_CODE_NUMENTRY: @@ -1552,6 +1613,11 @@ static inline bool sm6_value_is_undef(const struct sm6_value *value) return sm6_value_is_register(value) && value->u.reg.type == VKD3DSPR_UNDEF; }
+static bool sm6_value_is_icb(const struct sm6_value *value) +{ + return sm6_value_is_register(value) && value->u.reg.type == VKD3DSPR_IMMCONSTBUFFER; +} + static inline unsigned int sm6_value_get_constant_uint(const struct sm6_value *value) { if (!sm6_value_is_constant(value)) @@ -1782,6 +1848,17 @@ static size_t sm6_parser_get_value_index(struct sm6_parser *sm6, uint64_t idx) return i; }
+static const struct sm6_value *sm6_parser_get_value_unsafe(struct sm6_parser *sm6, unsigned int idx) +{ + if (idx < sm6->value_count) + return &sm6->values[idx]; + + WARN("Invalid value index %u.\n", idx); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "Invalid value index %u.", idx); + return NULL; +} + static size_t sm6_parser_get_value_idx_by_ref(struct sm6_parser *sm6, const struct dxil_record *record, const struct sm6_type *fwd_type, unsigned int *rec_idx) { @@ -2621,6 +2698,11 @@ static void sm6_parser_emit_ret(struct sm6_parser *sm6, const struct dxil_record ins->handler_idx = VKD3DSIH_NOP; }
+static bool sm6_metadata_value_is_node(const struct sm6_metadata_value *m) +{ + return m && m->type == VKD3D_METADATA_NODE; +} + static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const struct dxil_block *block, struct sm6_function *function) { @@ -2816,6 +2898,266 @@ static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const st return VKD3D_OK; }
+static bool sm6_parser_allocate_named_metadata(struct sm6_parser *sm6) +{ + struct dxil_block *block; + unsigned int i, j, count; + + for (i = 0, count = 0; i < sm6->root_block.child_block_count; ++i) + { + block = sm6->root_block.child_blocks[i]; + if (block->id != METADATA_BLOCK) + continue; + for (j = 0; j < block->record_count; ++j) + count += block->records[j]->code == METADATA_NAMED_NODE; + } + + if (!count) + return true; + + return !!(sm6->named_metadata = vkd3d_calloc(count, sizeof(*sm6->named_metadata))); +} + +static enum vkd3d_result metadata_value_create_node(struct sm6_metadata_value *m, struct sm6_metadata_value *table, + unsigned int table_idx, unsigned int count, const struct dxil_record *record, struct sm6_parser *sm6) +{ + struct sm6_metadata_node *node; + unsigned int i, offset; + + m->type = VKD3D_METADATA_NODE; + if (!(m->value_type = sm6->metadata_type)) + { + WARN("Metadata type not found.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + if (!(node = vkd3d_malloc(offsetof(struct sm6_metadata_node, operands[record->operand_count])))) + { + ERR("Failed to allocate metadata node with %u operands.\n", record->operand_count); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a metadata node with %u operands.", record->operand_count); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + m->u.node = node; + + node->is_distinct = record->code == METADATA_DISTINCT_NODE; + + offset = record->code != METADATA_NAMED_NODE; + + for (i = 0; i < record->operand_count; ++i) + { + size_t ref; + + if (record->operands[i] >= offset && record->operands[i] - offset >= count) + { + WARN("Invalid metadata index %"PRIu64".\n", record->operands[i]); + vkd3d_free(node); + return VKD3D_ERROR_INVALID_SHADER; + } + ref = record->operands[i] - offset; + + if (!node->is_distinct && ref == table_idx) + { + WARN("Metadata self-reference at index %u.\n", table_idx); + vkd3d_free(node); + return VKD3D_ERROR_INVALID_SHADER; + } + + node->operands[i] = (record->operands[i] >= offset) ? &table[ref] : NULL; + if (record->code == METADATA_NAMED_NODE && !sm6_metadata_value_is_node(node->operands[i])) + { + WARN("Named node operand is not a node.\n"); + vkd3d_free(node); + return VKD3D_ERROR_INVALID_SHADER; + } + } + + node->operand_count = record->operand_count; + + return VKD3D_OK; +} + +static enum vkd3d_result sm6_parser_metadata_init(struct sm6_parser *sm6, const struct dxil_block *block, + unsigned int idx) +{ + unsigned int i, count, table_idx, value_idx; + struct sm6_metadata_value *table, *m; + const struct dxil_record *record; + const struct sm6_value *value; + enum vkd3d_result ret; + char *name; + + for (i = 0, count = 0; i < block->record_count; ++i) + count += block->records[i]->code != METADATA_NAME; + + if (!(table = vkd3d_calloc(count, sizeof(*sm6->metadata_tables[0])))) + { + ERR("Failed to allocate metadata tables.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating metadata tables."); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + sm6->metadata_tables[idx] = table; + + for (i = 0, name = NULL; i < block->record_count; ++i) + { + record = block->records[i]; + + table_idx = sm6->metadata_counts[idx]; + m = &table[table_idx]; + + if (name && record->code != METADATA_NAMED_NODE) + { + WARN("Name '%s' is not followed by a named node.\n", name); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA, + "Metadata node name '%s' is not followed by a named node.", name); + vkd3d_free(name); + return VKD3D_ERROR_INVALID_SHADER; + } + + switch (record->code) + { + case METADATA_NAMED_NODE: + if (!name) + { + WARN("Named node has no name.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA, + "A metadata named node has no name."); + return VKD3D_ERROR_INVALID_SHADER; + } + + m = &sm6->named_metadata[sm6->named_metadata_count].value; + sm6->named_metadata[sm6->named_metadata_count].name = name; + name = NULL; + + if ((ret = metadata_value_create_node(m, table, UINT_MAX, count, record, sm6)) < 0) + return ret; + ++sm6->named_metadata_count; + + continue; + + case METADATA_DISTINCT_NODE: + case METADATA_NODE: + if ((ret = metadata_value_create_node(m, table, table_idx, count, record, sm6)) < 0) + return ret; + break; + + case METADATA_KIND: + if (!dxil_record_validate_operand_min_count(record, 2, sm6)) + return VKD3D_ERROR_INVALID_SHADER; + + m->type = VKD3D_METADATA_KIND; + m->u.kind.id = record->operands[0]; + if (!(m->u.kind.name = dxil_record_to_string(record, 1, sm6))) + { + ERR("Failed to allocate name of a kind.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + break; + + case METADATA_NAME: + /* LLVM allows an empty string here. */ + if (!(name = dxil_record_to_string(record, 0, sm6))) + { + ERR("Failed to allocate name.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + continue; + + case METADATA_STRING: + /* LLVM allows an empty string here. */ + m->type = VKD3D_METADATA_STRING; + if (!(m->u.string_value = dxil_record_to_string(record, 0, sm6))) + { + ERR("Failed to allocate string.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + break; + + case METADATA_VALUE: + if (!dxil_record_validate_operand_count(record, 2, 2, sm6)) + return VKD3D_ERROR_INVALID_SHADER; + + m->type = VKD3D_METADATA_VALUE; + if (!(m->value_type = sm6_parser_get_type(sm6, record->operands[0]))) + return VKD3D_ERROR_INVALID_SHADER; + + if (record->operands[1] > UINT_MAX) + WARN("Truncating value index %"PRIu64".\n", record->operands[1]); + value_idx = record->operands[1]; + if (!(value = sm6_parser_get_value_unsafe(sm6, value_idx))) + return VKD3D_ERROR_INVALID_SHADER; + + if (!sm6_value_is_constant(value) && !sm6_value_is_undef(value) && !sm6_value_is_icb(value) + && !sm6_value_is_function_dcl(value)) + { + WARN("Value at index %u is not a constant or a function declaration.\n", value_idx); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA, + "Metadata value at index %u is not a constant or a function declaration.", value_idx); + return VKD3D_ERROR_INVALID_SHADER; + } + m->u.value = value; + + if (value->type != m->value_type) + { + WARN("Type mismatch.\n"); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "The type of a metadata value does not match its referenced value at index %u.", value_idx); + } + + break; + + default: + FIXME("Unhandled metadata type %u.\n", record->code); + return VKD3D_ERROR_INVALID_SHADER; + } + ++sm6->metadata_counts[idx]; + } + + if (name) + { + WARN("Unused metadata name '%s'.\n", name); + vkd3d_free(name); + } + + return VKD3D_OK; +} + +static void sm6_metadata_value_destroy(struct sm6_metadata_value *m) +{ + switch (m->type) + { + case VKD3D_METADATA_NODE: + vkd3d_free(m->u.node); + break; + case VKD3D_METADATA_KIND: + vkd3d_free(m->u.kind.name); + break; + case VKD3D_METADATA_STRING: + vkd3d_free(m->u.string_value); + break; + default: + break; + } +} + +static void sm6_parser_metadata_cleanup(struct sm6_parser *sm6) +{ + unsigned int i, j; + + for (i = 0; i < ARRAY_SIZE(sm6->metadata_tables); ++i) + { + for (j = 0; j < sm6->metadata_counts[i]; ++j) + sm6_metadata_value_destroy(&sm6->metadata_tables[i][j]); + vkd3d_free(sm6->metadata_tables[i]); + } + for (i = 0; i < sm6->named_metadata_count; ++i) + { + sm6_metadata_value_destroy(&sm6->named_metadata[i].value); + vkd3d_free(sm6->named_metadata[i].name); + } + vkd3d_free(sm6->named_metadata); +} + static void sm6_type_table_cleanup(struct sm6_type *types, size_t count) { size_t i; @@ -2879,6 +3221,7 @@ static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) sm6_type_table_cleanup(sm6->types, sm6->type_count); sm6_symtab_cleanup(sm6->global_symbols, sm6->global_symbol_count); sm6_functions_cleanup(sm6->functions, sm6->function_count); + sm6_parser_metadata_cleanup(sm6); vkd3d_free(sm6->values); free_shader_desc(&parser->shader_desc); vkd3d_free(sm6); @@ -2902,7 +3245,7 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t struct vkd3d_shader_version version; struct dxil_block *block; enum vkd3d_result ret; - unsigned int i; + unsigned int i, j;
count = byte_code_size / sizeof(*byte_code); if (count < 6) @@ -3089,6 +3432,42 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t return ret; }
+ if (!sm6_parser_allocate_named_metadata(sm6)) + { + ERR("Failed to allocate named metadata array.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + for (i = 0, j = 0; i < sm6->root_block.child_block_count; ++i) + { + block = sm6->root_block.child_blocks[i]; + if (block->id != METADATA_BLOCK) + continue; + + if (j == ARRAY_SIZE(sm6->metadata_tables)) + { + FIXME("Skipping metadata table.\n"); + ret = VKD3D_ERROR_INVALID_SHADER; + } + else + { + ret = sm6_parser_metadata_init(sm6, block, j++); + } + if (ret < 0) + { + WARN("Failed to load metadata.\n"); + if (ret == VKD3D_ERROR_OUT_OF_MEMORY) + vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory parsing DXIL metadata tables."); + else if (ret == VKD3D_ERROR_INVALID_SHADER) + vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA, + "A DXIL metadata table is invalid."); + else + vkd3d_unreachable(); + return ret; + } + } + sm6_parser_init_output_signature(sm6, output_signature); sm6_parser_init_input_signature(sm6, input_signature);
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index c233cd32a..79e455ce5 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -176,12 +176,14 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE = 8011, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND = 8012, VKD3D_SHADER_ERROR_DXIL_UNHANDLED_INTRINSIC = 8013, + VKD3D_SHADER_ERROR_DXIL_INVALID_METADATA = 8014,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER = 8300, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE = 8301, VKD3D_SHADER_WARNING_DXIL_INVALID_BLOCK_LENGTH = 8302, VKD3D_SHADER_WARNING_DXIL_INVALID_MODULE_LENGTH = 8303, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS = 8304, + VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH = 8305,
VKD3D_SHADER_ERROR_VSIR_NOT_IMPLEMENTED = 9000, VKD3D_SHADER_ERROR_VSIR_INVALID_HANDLER = 9001,