From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 403 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 404 insertions(+)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 67dcd26a0..b2fa9c373 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -62,6 +62,86 @@ enum bitcode_abbrev_type ABBREV_BLOB = 5, };
+enum bitcode_address_space +{ + ADDRESS_SPACE_DEFAULT, + ADDRESS_SPACE_DEVICEMEM, + ADDRESS_SPACE_CBUFFER, + ADDRESS_SPACE_GROUPSHARED, +}; + +enum bitcode_type_code +{ + TYPE_CODE_NUMENTRY = 1, + TYPE_CODE_VOID = 2, + TYPE_CODE_FLOAT = 3, + TYPE_CODE_DOUBLE = 4, + TYPE_CODE_LABEL = 5, + TYPE_CODE_INTEGER = 7, + TYPE_CODE_POINTER = 8, + TYPE_CODE_HALF = 10, + TYPE_CODE_ARRAY = 11, + TYPE_CODE_VECTOR = 12, + TYPE_CODE_METADATA = 16, + TYPE_CODE_STRUCT_ANON = 18, + TYPE_CODE_STRUCT_NAME = 19, + TYPE_CODE_STRUCT_NAMED = 20, + TYPE_CODE_FUNCTION = 21, +}; + +struct sm6_pointer_info +{ + const struct sm6_type *type; + enum bitcode_address_space addr_space; +}; + +struct sm6_struct_info +{ + const char *name; + unsigned int elem_count; + const struct sm6_type *elem_types[]; +}; + +struct sm6_function_info +{ + const struct sm6_type *ret_type; + unsigned int param_count; + const struct sm6_type *param_types[]; +}; + +struct sm6_array_info +{ + unsigned int count; + const struct sm6_type *elem_type; +}; + +enum sm6_type_class +{ + TYPE_CLASS_VOID, + TYPE_CLASS_INTEGER, + TYPE_CLASS_FLOAT, + TYPE_CLASS_POINTER, + TYPE_CLASS_STRUCT, + TYPE_CLASS_FUNCTION, + TYPE_CLASS_VECTOR, + TYPE_CLASS_ARRAY, + TYPE_CLASS_LABEL, + TYPE_CLASS_METADATA, +}; + +struct sm6_type +{ + enum sm6_type_class class; + union + { + unsigned int width; + struct sm6_pointer_info pointer; + struct sm6_struct_info *struc; + struct sm6_function_info *function; + struct sm6_array_info array; + } u; +}; + struct dxil_record { unsigned int code; @@ -106,6 +186,9 @@ struct sm6_parser size_t abbrev_capacity; size_t abbrev_count;
+ struct sm6_type *types; + unsigned int type_count; + struct vkd3d_shader_parser p; };
@@ -714,6 +797,312 @@ static void dxil_global_abbrevs_cleanup(struct dxil_global_abbrev **abbrevs, uns vkd3d_free(abbrevs); }
+static const struct dxil_block *sm6_parser_get_level_one_block(const struct sm6_parser *sm6, + enum bitcode_block_id id, bool *is_unique) +{ + const struct dxil_block *block, *found = NULL; + unsigned int i; + + for (i = 0, *is_unique = true; i < sm6->root_block.child_block_count; ++i) + { + block = sm6->root_block.child_blocks[i]; + if (block->id != id) + continue; + + if (!found) + found = block; + else + *is_unique = false; + } + + return found; +} + +static char *dxil_record_to_string(const struct dxil_record *record) +{ + unsigned int i; + char *str; + + if (!(str = vkd3d_calloc(record->operand_count + 1, 1))) + return NULL; + + for (i = 0; i < record->operand_count; ++i) + str[i] = record->operands[i]; + + return str; +} + +static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6) +{ + unsigned int i, j, count, type_count, type_index; + const struct dxil_record *record; + const struct dxil_block *block; + char *struct_name = NULL; + struct sm6_type *type; + uint64_t type_id; + bool is_unique; + + if (!(block = sm6_parser_get_level_one_block(sm6, TYPE_BLOCK, &is_unique))) + { + WARN("No type definitions found.\n"); + return VKD3D_OK; + } + if (!is_unique) + FIXME("Ignoring extra type table(s).\n"); + + type_count = 0; + for (i = 0; i < block->record_count; ++i) + type_count += block->records[i]->code != TYPE_CODE_NUMENTRY && block->records[i]->code != TYPE_CODE_STRUCT_NAME; + + /* The type array must not be relocated. */ + if (!(sm6->types = vkd3d_calloc(type_count, sizeof(*sm6->types)))) + { + ERR("Failed to allocate type array.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + for (i = 0; i < block->record_count; ++i) + { + record = block->records[i]; + + type = &sm6->types[sm6->type_count]; + type_index = sm6->type_count; + + switch (record->code) + { + case TYPE_CODE_ARRAY: + case TYPE_CODE_VECTOR: + if (record->operand_count < 2) + { + WARN("Missing operands for array/vector type %u.\n", type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + + type->class = record->code == TYPE_CODE_ARRAY ? TYPE_CLASS_ARRAY : TYPE_CLASS_VECTOR; + + if (!(type->u.array.count = record->operands[0])) + { + TRACE("Setting unbounded for type %u.\n", type_index); + type->u.array.count = UINT_MAX; + } + + if ((type_id = record->operands[1]) >= type_count) + { + WARN("Invalid contained type id %"PRIu64" for type %u.\n", type_id, type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + type->u.array.elem_type = &sm6->types[type_id]; + break; + + case TYPE_CODE_DOUBLE: + type->class = TYPE_CLASS_FLOAT; + type->u.width = 64; + break; + + case TYPE_CODE_FLOAT: + type->class = TYPE_CLASS_FLOAT; + type->u.width = 32; + break; + + case TYPE_CODE_FUNCTION: + if (record->operand_count < 2) + { + WARN("Missing operands for function type %u.\n", type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + if (record->operands[0]) + FIXME("Unhandled vararg function type %u.\n", type_index); + + type->class = TYPE_CLASS_FUNCTION; + + if ((type_id = record->operands[1]) >= type_count) + { + WARN("Invalid return type id %"PRIu64" for type %u.\n", type_id, type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + + count = record->operand_count - 2; + if (!(type->u.function = vkd3d_malloc(offsetof(struct sm6_function_info, param_types[count])))) + { + ERR("Failed to allocate function parameter types.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + type->u.function->ret_type = &sm6->types[type_id]; + type->u.function->param_count = count; + for (j = 0; j < count; ++j) + { + if ((type_id = record->operands[j + 2]) >= type_count) + { + WARN("Invalid parameter type id %"PRIu64" for type %u.\n", type_id, type_index); + vkd3d_free(type->u.function); + return VKD3D_ERROR_INVALID_SHADER; + } + type->u.function->param_types[j] = &sm6->types[type_id]; + } + break; + + case TYPE_CODE_HALF: + type->class = TYPE_CLASS_FLOAT; + type->u.width = 16; + break; + + case TYPE_CODE_INTEGER: + { + uint64_t width; + + if (!record->operand_count) + { + WARN("Missing integer width for type %u.\n", type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + + type->class = TYPE_CLASS_INTEGER; + + width = record->operands[0]; + if (width > 64 || (width > 1 && width < 8) || vkd3d_popcount(width) != 1) + { + WARN("Invalid integer width %"PRIu64" for type %u.\n", width, type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + type->u.width = width; + break; + } + + case TYPE_CODE_LABEL: + type->class = TYPE_CLASS_LABEL; + break; + + case TYPE_CODE_METADATA: + type->class = TYPE_CLASS_METADATA; + break; + + case TYPE_CODE_NUMENTRY: + continue; + + case TYPE_CODE_POINTER: + if (!record->operand_count) + { + WARN("Missing pointee type for pointer type %u.\n", type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + + type->class = TYPE_CLASS_POINTER; + + if ((type_id = record->operands[0]) >= type_count) + { + WARN("Invalid pointee type id %"PRIu64" for type %u.\n", type_id, type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + type->u.pointer.type = &sm6->types[type_id]; + type->u.pointer.addr_space = (record->operand_count > 1) ? record->operands[1] : ADDRESS_SPACE_DEFAULT; + break; + + case TYPE_CODE_STRUCT_ANON: + case TYPE_CODE_STRUCT_NAMED: + if (record->operand_count < 2) + { + WARN("Missing operands for struct type %u.\n", type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + if (record->code == TYPE_CODE_STRUCT_NAMED && !struct_name) + { + WARN("Missing struct name before struct type %u.\n", type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + + type->class = TYPE_CLASS_STRUCT; + + count = record->operand_count - 1; + if (!(type->u.struc = vkd3d_malloc(offsetof(struct sm6_struct_info, elem_types[count])))) + { + ERR("Failed to allocate struct element types.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + if (record->operands[0]) + FIXME("Ignoring struct packed attribute.\n"); + + type->u.struc->elem_count = count; + for (j = 0; j < count; ++j) + { + if ((type_id = record->operands[j + 1]) >= type_count) + { + WARN("Invalid contained type id %"PRIu64" for type %u.\n", type_id, type_index); + vkd3d_free(type->u.struc); + return VKD3D_ERROR_INVALID_SHADER; + } + type->u.struc->elem_types[j] = &sm6->types[type_id]; + } + + if (record->code == TYPE_CODE_STRUCT_ANON) + { + type->u.struc->name = NULL; + break; + } + + type->u.struc->name = struct_name; + struct_name = NULL; + break; + + case TYPE_CODE_STRUCT_NAME: + if (!(struct_name = dxil_record_to_string(record))) + { + ERR("Failed to allocate struct name.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + if (!struct_name[0]) + WARN("Struct name is empty for type %u.\n", type_index); + continue; + + case TYPE_CODE_VOID: + type->class = TYPE_CLASS_VOID; + break; + + default: + FIXME("Unhandled type %u at index %u.\n", record->code, type_index); + return VKD3D_ERROR_INVALID_SHADER; + } + ++sm6->type_count; + } + + assert(sm6->type_count == type_count); + + if (struct_name) + { + WARN("Unused struct name %s.\n", struct_name); + vkd3d_free(struct_name); + } + + return VKD3D_OK; +} + +static void sm6_type_table_cleanup(struct sm6_type *types, unsigned int count) +{ + unsigned int i; + + if (!types) + return; + + for (i = 0; i < count; ++i) + { + switch (types[i].class) + { + case TYPE_CLASS_STRUCT: + vkd3d_free((void *)types[i].u.struc->name); + vkd3d_free(types[i].u.struc); + break; + case TYPE_CLASS_FUNCTION: + vkd3d_free(types[i].u.function); + break; + default: + break; + } + } + + vkd3d_free(types); +} + static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) { struct sm6_parser *sm6 = sm6_parser(parser); @@ -721,6 +1110,7 @@ static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) dxil_block_destroy(&sm6->root_block); dxil_global_abbrevs_cleanup(sm6->abbrevs, sm6->abbrev_count); shader_instruction_array_destroy(&parser->instructions); + sm6_type_table_cleanup(sm6->types, sm6->type_count); free_shader_desc(&parser->shader_desc); vkd3d_free(sm6); } @@ -860,6 +1250,19 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t "Module ends with length %u but indicated length is %u.", length, expected_length); }
+ if ((ret = sm6_parser_type_table_init(sm6)) < 0) + { + 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 type table."); + else if (ret == VKD3D_ERROR_INVALID_SHADER) + vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_TABLE, + "DXIL type table is invalid."); + else + vkd3d_unreachable(); + return ret; + } + dxil_block_destroy(&sm6->root_block);
return VKD3D_OK; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 398139c3e..ade0bbd02 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -155,6 +155,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_OFFSET = 8002, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE = 8003, VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE = 8004, + VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_TABLE = 8005,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER = 8300, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE = 8301,
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 103 +++++++++++++++++++++-- libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 99 insertions(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index b2fa9c373..3efae88d7 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -89,6 +89,12 @@ enum bitcode_type_code TYPE_CODE_FUNCTION = 21, };
+enum bitcode_value_symtab_code +{ + VST_CODE_ENTRY = 1, + VST_CODE_BBENTRY = 2, +}; + struct sm6_pointer_info { const struct sm6_type *type; @@ -149,6 +155,12 @@ struct dxil_record uint64_t operands[]; };
+struct sm6_symbol +{ + unsigned int id; + const char *name; +}; + struct dxil_block { const struct dxil_block *parent; @@ -189,6 +201,9 @@ struct sm6_parser struct sm6_type *types; unsigned int type_count;
+ struct sm6_symbol *global_symbols; + unsigned int global_symbol_count; + struct vkd3d_shader_parser p; };
@@ -818,16 +833,17 @@ static const struct dxil_block *sm6_parser_get_level_one_block(const struct sm6_ return found; }
-static char *dxil_record_to_string(const struct dxil_record *record) +static char *dxil_record_to_string(const struct dxil_record *record, unsigned int offset) { unsigned int i; char *str;
- if (!(str = vkd3d_calloc(record->operand_count + 1, 1))) + assert(offset <= record->operand_count); + if (!(str = vkd3d_calloc(record->operand_count - offset + 1, 1))) return NULL;
- for (i = 0; i < record->operand_count; ++i) - str[i] = record->operands[i]; + for (i = offset; i < record->operand_count; ++i) + str[i - offset] = record->operands[i];
return str; } @@ -1046,7 +1062,7 @@ static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6) break;
case TYPE_CODE_STRUCT_NAME: - if (!(struct_name = dxil_record_to_string(record))) + if (!(struct_name = dxil_record_to_string(record, 0))) { ERR("Failed to allocate struct name.\n"); return VKD3D_ERROR_OUT_OF_MEMORY; @@ -1077,6 +1093,60 @@ static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6) return VKD3D_OK; }
+static enum vkd3d_result sm6_parser_symtab_init(struct sm6_parser *sm6) +{ + const struct dxil_record *record; + const struct dxil_block *block; + struct sm6_symbol *symbol; + unsigned int i, count; + bool is_unique; + + if (!(block = sm6_parser_get_level_one_block(sm6, VALUE_SYMTAB_BLOCK, &is_unique))) + { + /* There should always be at least one symbol: the name of the entry point function. */ + WARN("No value symtab block found.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + if (!is_unique) + FIXME("Ignoring extra value symtab block(s).\n"); + + for (i = 0, count = 0; i < block->record_count; ++i) + count += block->records[i]->code == VST_CODE_ENTRY; + + if (!(sm6->global_symbols = vkd3d_calloc(count, sizeof(*sm6->global_symbols)))) + { + ERR("Failed to allocate global symbols.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + for (i = 0; i < block->record_count; ++i) + { + record = block->records[i]; + + if (record->code != VST_CODE_ENTRY) + { + FIXME("Unhandled symtab code %u.\n", record->code); + continue; + } + if (record->operand_count < 2) + { + WARN("Missing operands for function symbol %u.\n", sm6->global_symbol_count); + continue; + } + + symbol = &sm6->global_symbols[sm6->global_symbol_count]; + symbol->id = record->operands[0]; + if (!(symbol->name = dxil_record_to_string(record, 1))) + { + ERR("Failed to allocate symbol name.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + ++sm6->global_symbol_count; + } + + return VKD3D_OK; +} + static void sm6_type_table_cleanup(struct sm6_type *types, unsigned int count) { unsigned int i; @@ -1103,6 +1173,15 @@ static void sm6_type_table_cleanup(struct sm6_type *types, unsigned int count) vkd3d_free(types); }
+static void sm6_symtab_cleanup(struct sm6_symbol *symbols, unsigned int count) +{ + unsigned int i; + + for (i = 0; i < count; ++i) + vkd3d_free((void *)symbols[i].name); + vkd3d_free(symbols); +} + static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) { struct sm6_parser *sm6 = sm6_parser(parser); @@ -1111,6 +1190,7 @@ static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) dxil_global_abbrevs_cleanup(sm6->abbrevs, sm6->abbrev_count); shader_instruction_array_destroy(&parser->instructions); sm6_type_table_cleanup(sm6->types, sm6->type_count); + sm6_symtab_cleanup(sm6->global_symbols, sm6->global_symbol_count); free_shader_desc(&parser->shader_desc); vkd3d_free(sm6); } @@ -1263,6 +1343,19 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t return ret; }
+ if ((ret = sm6_parser_symtab_init(sm6)) < 0) + { + 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 value symbol table."); + else if (ret == VKD3D_ERROR_INVALID_SHADER) + vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_VALUE_SYMTAB, + "DXIL value symbol table is invalid."); + else + vkd3d_unreachable(); + return ret; + } + dxil_block_destroy(&sm6->root_block);
return VKD3D_OK; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index ade0bbd02..d30da3f16 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -156,6 +156,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE = 8003, VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE = 8004, VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_TABLE = 8005, + VKD3D_SHADER_ERROR_DXIL_INVALID_VALUE_SYMTAB = 8006,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER = 8300, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE = 8301,
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 55 ++++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 56 insertions(+)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 3efae88d7..813e94bdf 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -70,6 +70,13 @@ enum bitcode_address_space ADDRESS_SPACE_GROUPSHARED, };
+enum bitcode_module_code +{ + MODULE_CODE_VERSION = 1, + MODULE_CODE_GLOBALVAR = 7, + MODULE_CODE_FUNCTION = 8, +}; + enum bitcode_type_code { TYPE_CODE_NUMENTRY = 1, @@ -1147,6 +1154,48 @@ static enum vkd3d_result sm6_parser_symtab_init(struct sm6_parser *sm6) return VKD3D_OK; }
+static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6) +{ + const struct dxil_block *block = &sm6->root_block; + const struct dxil_record *record; + uint64_t version; + unsigned int i; + + for (i = 0; i < block->record_count; ++i) + { + record = block->records[i]; + switch (record->code) + { + case MODULE_CODE_FUNCTION: + FIXME("Functions are not implemented yet.\n"); + break; + + case MODULE_CODE_GLOBALVAR: + FIXME("Global variables are not implemented yet.\n"); + break; + + case MODULE_CODE_VERSION: + if (!record->operand_count) + { + WARN("Missing version operand.\n"); + } + else if ((version = record->operands[0]) != 1) + { + FIXME("Unsupported format version %#"PRIx64".\n", version); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_UNSUPPORTED_BITCODE_FORMAT, + "Bitcode format version %#"PRIx64" is unsupported.", version); + return VKD3D_ERROR_INVALID_SHADER; + } + break; + + default: + break; + } + } + + return VKD3D_OK; +} + static void sm6_type_table_cleanup(struct sm6_type *types, unsigned int count) { unsigned int i; @@ -1356,6 +1405,12 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t return ret; }
+ if ((ret = sm6_parser_globals_init(sm6)) < 0) + { + WARN("Failed to load global declarations.\n"); + return ret; + } + dxil_block_destroy(&sm6->root_block);
return VKD3D_OK; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index d30da3f16..0edcaaa36 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -157,6 +157,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE = 8004, VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_TABLE = 8005, VKD3D_SHADER_ERROR_DXIL_INVALID_VALUE_SYMTAB = 8006, + VKD3D_SHADER_ERROR_DXIL_UNSUPPORTED_BITCODE_FORMAT = 8007,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER = 8300, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE = 8301,
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 247 ++++++++++++++++++++++- libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 2 files changed, 247 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 813e94bdf..89748ef46 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -155,6 +155,28 @@ struct sm6_type } u; };
+enum sm6_value_type +{ + VALUE_TYPE_FUNCTION, +}; + +struct sm6_function_data +{ + const char *name; + bool is_prototype; + unsigned int attribs_id; +}; + +struct sm6_value +{ + const struct sm6_type *type; + enum sm6_value_type value_type; + union + { + struct sm6_function_data function; + } u; +}; + struct dxil_record { unsigned int code; @@ -211,6 +233,10 @@ struct sm6_parser struct sm6_symbol *global_symbols; unsigned int global_symbol_count;
+ struct sm6_value *values; + unsigned int value_count; + unsigned int value_capacity; + struct vkd3d_shader_parser p; };
@@ -810,6 +836,15 @@ static enum vkd3d_result dxil_block_init(struct dxil_block *block, const struct return ret; }
+static unsigned int dxil_block_compute_module_decl_count(const struct dxil_block *block) +{ + unsigned int i, count; + + for (i = 0, count = 0; i < block->record_count; ++i) + count += block->records[i]->code == MODULE_CODE_FUNCTION; + return count; +} + static void dxil_global_abbrevs_cleanup(struct dxil_global_abbrev **abbrevs, unsigned int count) { unsigned int i; @@ -1100,6 +1135,88 @@ static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6) return VKD3D_OK; }
+static inline bool sm6_type_is_void(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_VOID; +} + +static inline bool sm6_type_is_numeric(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_INTEGER || type->class == TYPE_CLASS_FLOAT; +} + +static inline bool sm6_type_is_pointer(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_POINTER; +} + +static bool sm6_type_is_numeric_aggregate(const struct sm6_type *type) +{ + unsigned int i; + + switch (type->class) + { + case TYPE_CLASS_ARRAY: + case TYPE_CLASS_VECTOR: + return sm6_type_is_numeric(type->u.array.elem_type); + + case TYPE_CLASS_STRUCT: + for (i = 0; i < type->u.struc->elem_count; ++i) + if (!sm6_type_is_numeric(type->u.struc->elem_types[i])) + return false; + return true; + + default: + return false; + } +} + +static inline bool sm6_type_is_struct(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_STRUCT; +} + +static inline bool sm6_type_is_function(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_FUNCTION; +} + +static inline bool sm6_type_is_handle(const struct sm6_type *type) +{ + return sm6_type_is_struct(type) && !strcmp(type->u.struc->name, "dx.types.Handle"); +} + +static const struct sm6_type *sm6_type_get_pointer_to_type(const struct sm6_type *type, + enum bitcode_address_space addr_space, struct sm6_parser *sm6) +{ + unsigned int i, start = type - sm6->types; + const struct sm6_type *pointer_type; + + /* DXC seems usually to place the pointer type immediately after its pointee. */ + for (i = (start + 1) % sm6->type_count; i != start; i = (i + 1) % sm6->type_count) + { + pointer_type = &sm6->types[i]; + if (sm6_type_is_pointer(pointer_type) && pointer_type->u.pointer.type == type + && pointer_type->u.pointer.addr_space == addr_space) + return pointer_type; + } + + return NULL; +} + +static const struct sm6_type *sm6_parser_get_type(const struct sm6_parser *sm6, const char *function, uint64_t type_id) +{ + if (type_id >= sm6->type_count) + { + vkd3d_dbg_printf(VKD3D_DBG_LEVEL_WARN, function, "Invalid type index %"PRIu64" at %u.\n", + type_id, sm6->value_count); + return NULL; + } + return &sm6->types[type_id]; +} + +#define sm6_parser_get_type(sm6, id) sm6_parser_get_type(sm6, __FUNCTION__, id) + static enum vkd3d_result sm6_parser_symtab_init(struct sm6_parser *sm6) { const struct dxil_record *record; @@ -1154,6 +1271,119 @@ static enum vkd3d_result sm6_parser_symtab_init(struct sm6_parser *sm6) return VKD3D_OK; }
+static const char *sm6_parser_get_global_symbol_name(const struct sm6_parser *sm6, unsigned int id) +{ + unsigned int i; + + for (i = 0; i < sm6->global_symbol_count; ++i) + { + if (sm6->global_symbols[i].id == id) + return sm6->global_symbols[i].name; + } + return NULL; +} + +static inline bool sm6_value_is_dx_intrinsic_dcl(const struct sm6_value *fn) +{ + assert(fn->value_type == VALUE_TYPE_FUNCTION); + return fn->u.function.is_prototype && !strncmp(fn->u.function.name, "dx.op.", 6); +} + +static inline struct sm6_value *sm6_parser_get_current_value(const struct sm6_parser *sm6) +{ + return &sm6->values[sm6->value_count]; +} + +static unsigned int sm6_parser_compute_max_value_count(struct sm6_parser *sm6, + const struct dxil_block *block, unsigned int value_count) +{ + unsigned int i, old_value_count = value_count; + + if (block->id == MODULE_BLOCK) + value_count += dxil_block_compute_module_decl_count(block); + + for (i = 0; i < block->child_block_count; ++i) + value_count = sm6_parser_compute_max_value_count(sm6, block->child_blocks[i], value_count); + + switch (block->id) + { + case FUNCTION_BLOCK: + sm6->value_capacity = max(sm6->value_capacity, value_count); + /* The value count returns to its previous value after handling a function. */ + value_count = old_value_count; + break; + default: + break; + } + + return value_count; +} + +static bool sm6_parser_declare_function(struct sm6_parser *sm6, const struct dxil_record *record) +{ + const struct sm6_type *ret_type; + struct sm6_value *fn; + unsigned int i, j; + + if (record->operand_count < 5) + { + WARN("Missing operands for function %u.\n", sm6->value_count); + return false; + } + + fn = sm6_parser_get_current_value(sm6); + fn->value_type = VALUE_TYPE_FUNCTION; + if (!(fn->u.function.name = sm6_parser_get_global_symbol_name(sm6, sm6->value_count))) + { + WARN("Missing symbol name for function %u.\n", sm6->value_count); + fn->u.function.name = ""; + } + + if (!(fn->type = sm6_parser_get_type(sm6, record->operands[0]))) + return false; + if (!sm6_type_is_function(fn->type)) + { + WARN("Type is not a function.\n"); + return false; + } + ret_type = fn->type->u.function->ret_type; + + if (!(fn->type = sm6_type_get_pointer_to_type(fn->type, ADDRESS_SPACE_DEFAULT, sm6))) + { + WARN("Failed to get pointer type for type %u.\n", fn->type->class); + return false; + } + + if (record->operands[1]) + WARN("Ignoring calling convention %#"PRIx64".\n", record->operands[1]); + + fn->u.function.is_prototype = !!record->operands[2]; + + if (record->operands[3]) + WARN("Ignoring linkage %#"PRIx64".\n", record->operands[3]); + + if (record->operands[4] > UINT_MAX) + WARN("Invalid attributes id %#"PRIx64".\n", record->operands[4]); + /* 1-based index. */ + if ((fn->u.function.attribs_id = record->operands[4])) + WARN("Ignoring function attributes.\n"); + + for (i = 5, j = 0; i < record->operand_count; ++i) + j += !!record->operands[i]; + if (j) + WARN("Ignoring %u operands.\n", j); + + if (sm6_value_is_dx_intrinsic_dcl(fn) && !sm6_type_is_void(ret_type) && !sm6_type_is_numeric(ret_type) + && !sm6_type_is_numeric_aggregate(ret_type) && !sm6_type_is_handle(ret_type)) + { + WARN("Unexpected return type for dx intrinsic function '%s'.\n", fn->u.function.name); + } + + ++sm6->value_count; + + return true; +} + static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6) { const struct dxil_block *block = &sm6->root_block; @@ -1167,7 +1397,12 @@ static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6) switch (record->code) { case MODULE_CODE_FUNCTION: - FIXME("Functions are not implemented yet.\n"); + if (!sm6_parser_declare_function(sm6, record)) + { + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_FUNCTION_DCL, + "A DXIL function declaration is invalid."); + return VKD3D_ERROR_INVALID_SHADER; + } break;
case MODULE_CODE_GLOBALVAR: @@ -1240,6 +1475,7 @@ static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) shader_instruction_array_destroy(&parser->instructions); sm6_type_table_cleanup(sm6->types, sm6->type_count); sm6_symtab_cleanup(sm6->global_symbols, sm6->global_symbol_count); + vkd3d_free(sm6->values); free_shader_desc(&parser->shader_desc); vkd3d_free(sm6); } @@ -1405,6 +1641,15 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t return ret; }
+ sm6_parser_compute_max_value_count(sm6, &sm6->root_block, 0); + if (!(sm6->values = vkd3d_calloc(sm6->value_capacity, sizeof(*sm6->values)))) + { + ERR("Failed to allocate value array.\n"); + vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating DXIL value array."); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + if ((ret = sm6_parser_globals_init(sm6)) < 0) { WARN("Failed to load global declarations.\n"); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 0edcaaa36..5824dfaf6 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -158,6 +158,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_TABLE = 8005, VKD3D_SHADER_ERROR_DXIL_INVALID_VALUE_SYMTAB = 8006, VKD3D_SHADER_ERROR_DXIL_UNSUPPORTED_BITCODE_FORMAT = 8007, + VKD3D_SHADER_ERROR_DXIL_INVALID_FUNCTION_DCL = 8008,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER = 8300, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE = 8301,
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 282 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 2 + 2 files changed, 284 insertions(+)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 89748ef46..ae0dc0a9e 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -77,6 +77,19 @@ enum bitcode_module_code MODULE_CODE_FUNCTION = 8, };
+enum bitcode_constant_code +{ + CST_CODE_SETTYPE = 1, + CST_CODE_NULL = 2, + CST_CODE_UNDEF = 3, + CST_CODE_INTEGER = 4, + CST_CODE_FLOAT = 6, + CST_CODE_STRING = 8, + CST_CODE_CE_GEP = 12, + CST_CODE_CE_INBOUNDS_GEP = 20, + CST_CODE_DATA = 22, +}; + enum bitcode_type_code { TYPE_CODE_NUMENTRY = 1, @@ -158,6 +171,7 @@ struct sm6_type enum sm6_value_type { VALUE_TYPE_FUNCTION, + VALUE_TYPE_REG, };
struct sm6_function_data @@ -174,6 +188,7 @@ struct sm6_value union { struct sm6_function_data function; + struct vkd3d_shader_register reg; } u; };
@@ -845,6 +860,15 @@ static unsigned int dxil_block_compute_module_decl_count(const struct dxil_block return count; }
+static unsigned int dxil_block_compute_constants_count(const struct dxil_block *block) +{ + unsigned int i, count; + + for (i = 0, count = 0; i < block->record_count; ++i) + count += block->records[i]->code != CST_CODE_SETTYPE; + return count; +} + static void dxil_global_abbrevs_cleanup(struct dxil_global_abbrev **abbrevs, unsigned int count) { unsigned int i; @@ -1140,6 +1164,16 @@ static inline bool sm6_type_is_void(const struct sm6_type *type) return type->class == TYPE_CLASS_VOID; }
+static inline bool sm6_type_is_integer(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_INTEGER; +} + +static inline bool sm6_type_is_floating_point(const struct sm6_type *type) +{ + return type->class == TYPE_CLASS_FLOAT; +} + static inline bool sm6_type_is_numeric(const struct sm6_type *type) { return type->class == TYPE_CLASS_INTEGER || type->class == TYPE_CLASS_FLOAT; @@ -1186,6 +1220,11 @@ static inline bool sm6_type_is_handle(const struct sm6_type *type) return sm6_type_is_struct(type) && !strcmp(type->u.struc->name, "dx.types.Handle"); }
+static inline const struct sm6_type *sm6_type_get_element_type(const struct sm6_type *type) +{ + return (type->class == TYPE_CLASS_ARRAY || type->class == TYPE_CLASS_VECTOR) ? type->u.array.elem_type : type; +} + static const struct sm6_type *sm6_type_get_pointer_to_type(const struct sm6_type *type, enum bitcode_address_space addr_space, struct sm6_parser *sm6) { @@ -1294,6 +1333,39 @@ static inline struct sm6_value *sm6_parser_get_current_value(const struct sm6_pa return &sm6->values[sm6->value_count]; }
+static enum vkd3d_data_type vkd3d_data_type_from_sm6_type(const struct sm6_type *type) +{ + if (type->class == TYPE_CLASS_INTEGER) + { + switch (type->u.width) + { + case 8: + return VKD3D_DATA_UINT8; + case 32: + return VKD3D_DATA_UINT; + default: + FIXME("Unhandled width %u.\n", type->u.width); + return VKD3D_DATA_UINT; + } + } + else if (type->class == TYPE_CLASS_FLOAT) + { + switch (type->u.width) + { + case 32: + return VKD3D_DATA_FLOAT; + case 64: + return VKD3D_DATA_DOUBLE; + default: + FIXME("Unhandled width %u.\n", type->u.width); + return VKD3D_DATA_FLOAT; + } + } + + FIXME("Unhandled type %u.\n", type->class); + return VKD3D_DATA_UINT; +} + static unsigned int sm6_parser_compute_max_value_count(struct sm6_parser *sm6, const struct dxil_block *block, unsigned int value_count) { @@ -1307,6 +1379,10 @@ static unsigned int sm6_parser_compute_max_value_count(struct sm6_parser *sm6,
switch (block->id) { + case CONSTANTS_BLOCK: + /* Function local constants are contained in a child block of the function block. */ + value_count += dxil_block_compute_constants_count(block); + break; case FUNCTION_BLOCK: sm6->value_capacity = max(sm6->value_capacity, value_count); /* The value count returns to its previous value after handling a function. */ @@ -1384,6 +1460,164 @@ static bool sm6_parser_declare_function(struct sm6_parser *sm6, const struct dxi return true; }
+static inline uint64_t decode_rotated_signed_value(uint64_t value) +{ + if (value != 1) + { + bool neg = value & 1; + value >>= 1; + return neg ? -value : value; + } + return value << 63; +} + +static inline float bitcast_uint64_to_float(uint64_t value) +{ + union + { + uint32_t uint32_value; + float float_value; + } u; + + u.uint32_value = value; + return u.float_value; +} + +static inline double bitcast_uint64_to_double(uint64_t value) +{ + union + { + uint64_t uint64_value; + double double_value; + } u; + + u.uint64_value = value; + return u.double_value; +} + +static enum vkd3d_result sm6_parser_constants_init(struct sm6_parser *sm6, const struct dxil_block *block) +{ + enum vkd3d_shader_register_type reg_type = VKD3DSPR_INVALID; + const struct sm6_type *type, *elem_type; + enum vkd3d_data_type reg_data_type; + const struct dxil_record *record; + unsigned int i, value_idx; + struct sm6_value *dst; + uint64_t value; + + for (i = 0, type = NULL; i < block->record_count; ++i) + { + record = block->records[i]; + value_idx = sm6->value_count; + + if (record->code == CST_CODE_SETTYPE) + { + if (!record->operand_count) + { + WARN("Missing type id for constant idx %u.\n", value_idx); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (!(type = sm6_parser_get_type(sm6, record->operands[0]))) + return VKD3D_ERROR_INVALID_SHADER; + + elem_type = sm6_type_get_element_type(type); + if (sm6_type_is_numeric(elem_type)) + { + reg_data_type = vkd3d_data_type_from_sm6_type(elem_type); + reg_type = elem_type->u.width > 32 ? VKD3DSPR_IMMCONST64 : VKD3DSPR_IMMCONST; + } + else + { + reg_data_type = VKD3D_DATA_UNUSED; + reg_type = VKD3DSPR_INVALID; + } + + if (i == block->record_count - 1) + WARN("Unused SETTYPE record.\n"); + + continue; + } + + if (!type) + { + WARN("Constant record %u has no type.\n", value_idx); + return VKD3D_ERROR_INVALID_SHADER; + } + + dst = sm6_parser_get_current_value(sm6); + dst->type = type; + dst->value_type = VALUE_TYPE_REG; + dst->u.reg.type = reg_type; + dst->u.reg.immconst_type = VKD3D_IMMCONST_SCALAR; + dst->u.reg.data_type = reg_data_type; + + switch (record->code) + { + case CST_CODE_NULL: + /* Register constant data is already zero-filled. */ + break; + + case CST_CODE_INTEGER: + if (!record->operand_count) + { + WARN("Missing integer value for constant idx %u.\n", value_idx); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (!sm6_type_is_integer(type)) + { + WARN("Invalid integer of non-integer type %u at constant idx %u.\n", type->class, value_idx); + return VKD3D_ERROR_INVALID_SHADER; + } + + value = decode_rotated_signed_value(record->operands[0]); + if (type->u.width <= 32) + dst->u.reg.u.immconst_uint[0] = value & ((1ull << type->u.width) - 1); + else + dst->u.reg.u.immconst_uint64[0] = value; + + break; + + case CST_CODE_FLOAT: + if (!record->operand_count) + { + WARN("Missing floating point value for constant idx %u.\n", value_idx); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (!sm6_type_is_floating_point(type)) + { + WARN("Invalid float of non-fp type %u at constant idx %u.\n", type->class, value_idx); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (type->u.width == 16) + FIXME("Half float type is not supported yet.\n"); + else if (type->u.width == 32) + dst->u.reg.u.immconst_float[0] = bitcast_uint64_to_float(record->operands[0]); + else if (type->u.width == 64) + dst->u.reg.u.immconst_double[0] = bitcast_uint64_to_double(record->operands[0]); + else + vkd3d_unreachable(); + + break; + + case CST_CODE_DATA: + WARN("Unhandled constant array.\n"); + break; + + default: + FIXME("Unhandled constant code %u.\n", record->code); + break; + } + + ++sm6->value_count; + } + + return VKD3D_OK; +} + static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6) { const struct dxil_block *block = &sm6->root_block; @@ -1431,6 +1665,41 @@ static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6) return VKD3D_OK; }
+static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const struct dxil_block *block, + unsigned int level) +{ + enum vkd3d_result ret; + unsigned int i; + + for (i = 0; i < block->child_block_count; ++i) + { + if ((ret = sm6_parser_module_init(sm6, block->child_blocks[i], level + 1)) < 0) + return ret; + } + + switch (block->id) + { + case CONSTANTS_BLOCK: + return sm6_parser_constants_init(sm6, block); + + case BLOCKINFO_BLOCK: + case MODULE_BLOCK: + case PARAMATTR_BLOCK: + case PARAMATTR_GROUP_BLOCK: + case VALUE_SYMTAB_BLOCK: + case METADATA_BLOCK: + case METADATA_ATTACHMENT_BLOCK: + case TYPE_BLOCK: + break; + + default: + FIXME("Unhandled block id %u.\n", block->id); + break; + } + + return VKD3D_OK; +} + static void sm6_type_table_cleanup(struct sm6_type *types, unsigned int count) { unsigned int i; @@ -1656,6 +1925,19 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t return ret; }
+ if ((ret = sm6_parser_module_init(sm6, &sm6->root_block, 0)) < 0) + { + 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 module."); + else if (ret == VKD3D_ERROR_INVALID_SHADER) + vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE, + "DXIL module is invalid."); + else + vkd3d_unreachable(); + return ret; + } + dxil_block_destroy(&sm6->root_block);
return VKD3D_OK; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 5824dfaf6..3e4d2a703 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -159,6 +159,7 @@ enum vkd3d_shader_error VKD3D_SHADER_ERROR_DXIL_INVALID_VALUE_SYMTAB = 8006, VKD3D_SHADER_ERROR_DXIL_UNSUPPORTED_BITCODE_FORMAT = 8007, VKD3D_SHADER_ERROR_DXIL_INVALID_FUNCTION_DCL = 8008, + VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE = 8009,
VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER = 8300, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE = 8301, @@ -533,6 +534,7 @@ enum vkd3d_data_type VKD3D_DATA_DOUBLE, VKD3D_DATA_CONTINUED, VKD3D_DATA_UNUSED, + VKD3D_DATA_UINT8, };
enum vkd3d_immconst_type
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 260 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 258 insertions(+), 2 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index ae0dc0a9e..5dbef9599 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -90,6 +90,29 @@ enum bitcode_constant_code CST_CODE_DATA = 22, };
+enum bitcode_function_code +{ + FUNC_CODE_DECLAREBLOCKS = 1, + FUNC_CODE_INST_BINOP = 2, + FUNC_CODE_INST_CAST = 3, + FUNC_CODE_INST_RET = 10, + FUNC_CODE_INST_BR = 11, + FUNC_CODE_INST_SWITCH = 12, + FUNC_CODE_INST_PHI = 16, + FUNC_CODE_INST_ALLOCA = 19, + FUNC_CODE_INST_LOAD = 20, + FUNC_CODE_INST_EXTRACTVAL = 26, + FUNC_CODE_INST_CMP2 = 28, + FUNC_CODE_INST_VSELECT = 29, + FUNC_CODE_INST_CALL = 34, + FUNC_CODE_INST_ATOMICRMW = 38, + FUNC_CODE_INST_LOADATOMIC = 41, + FUNC_CODE_INST_GEP = 43, + FUNC_CODE_INST_STORE = 44, + FUNC_CODE_INST_STOREATOMIC = 45, + FUNC_CODE_INST_CMPXCHG = 46, +}; + enum bitcode_type_code { TYPE_CODE_NUMENTRY = 1, @@ -205,6 +228,21 @@ struct sm6_symbol const char *name; };
+struct sm6_block +{ + struct vkd3d_shader_instruction *instructions; + size_t instruction_capacity; + unsigned int instruction_count; +}; + +struct sm6_function +{ + const struct sm6_value *declaration; + + struct sm6_block *blocks[1]; + unsigned int block_count; +}; + struct dxil_block { const struct dxil_block *parent; @@ -248,6 +286,9 @@ struct sm6_parser struct sm6_symbol *global_symbols; unsigned int global_symbol_count;
+ struct sm6_function *functions; + unsigned int function_count; + struct sm6_value *values; unsigned int value_count; unsigned int value_capacity; @@ -851,6 +892,16 @@ static enum vkd3d_result dxil_block_init(struct dxil_block *block, const struct return ret; }
+static unsigned int dxil_block_compute_function_count(const struct dxil_block *root) +{ + unsigned int i, count; + + for (i = 0, count = 0; i < root->child_block_count; ++i) + count += root->child_blocks[i]->id == FUNCTION_BLOCK; + + return count; +} + static unsigned int dxil_block_compute_module_decl_count(const struct dxil_block *block) { unsigned int i, count; @@ -1215,6 +1266,11 @@ static inline bool sm6_type_is_function(const struct sm6_type *type) return type->class == TYPE_CLASS_FUNCTION; }
+static inline bool sm6_type_is_function_pointer(const struct sm6_type *type) +{ + return sm6_type_is_pointer(type) && sm6_type_is_function(type->u.pointer.type); +} + static inline bool sm6_type_is_handle(const struct sm6_type *type) { return sm6_type_is_struct(type) && !strcmp(type->u.struc->name, "dx.types.Handle"); @@ -1384,6 +1440,9 @@ static unsigned int sm6_parser_compute_max_value_count(struct sm6_parser *sm6, value_count += dxil_block_compute_constants_count(block); break; case FUNCTION_BLOCK: + /* A function must start with a block count, which emits no value. This formula is likely to + * overestimate the value count somewhat, but this should be no problem. */ + value_count += max(block->record_count, 1u) - 1; sm6->value_capacity = max(sm6->value_capacity, value_count); /* The value count returns to its previous value after handling a function. */ value_count = old_value_count; @@ -1395,6 +1454,21 @@ static unsigned int sm6_parser_compute_max_value_count(struct sm6_parser *sm6, return value_count; }
+#define DXIL_RECORD_VALIDATE_OPERAND_MIN_COUNT(min_count) do {\ + if (record->operand_count < (min_count))\ + {\ + WARN("Invalid operand count %u.\n", record->operand_count);\ + return;\ + } } while (false) + +#define DXIL_RECORD_VALIDATE_OPERAND_MAX_COUNT(max_count) do {\ + if (record->operand_count > (max_count))\ + WARN("Ignoring %u extra operands.\n", record->operand_count - (max_count)); } while (false) + +#define DXIL_RECORD_VALIDATE_OPERAND_COUNT(min_count, max_count) do {\ + DXIL_RECORD_VALIDATE_OPERAND_MIN_COUNT(min_count);\ + DXIL_RECORD_VALIDATE_OPERAND_MAX_COUNT(max_count); } while (false) + static bool sm6_parser_declare_function(struct sm6_parser *sm6, const struct dxil_record *record) { const struct sm6_type *ret_type; @@ -1665,11 +1739,155 @@ static enum vkd3d_result sm6_parser_globals_init(struct sm6_parser *sm6) return VKD3D_OK; }
+static const struct sm6_value *sm6_parser_next_function_definition(struct sm6_parser *sm6) +{ + unsigned int i, count = sm6->function_count; + + for (i = 0; i < sm6->value_count; ++i) + { + if (sm6_type_is_function_pointer(sm6->values[i].type) && !sm6->values[i].u.function.is_prototype && !count--) + break; + } + if (i == sm6->value_count) + return NULL; + + ++sm6->function_count; + return &sm6->values[i]; +} + +static struct sm6_block *sm6_block_create() +{ + struct sm6_block *block = vkd3d_calloc(1, sizeof(*block)); + return block; +} + +static void sm6_parser_emit_ret(struct sm6_parser *sm6, const struct dxil_record *record, + struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) +{ + DXIL_RECORD_VALIDATE_OPERAND_COUNT(0, 1); + + if (record->operand_count) + FIXME("Non-void return is not implemented.\n"); + + ins->handler_idx = VKD3DSIH_NOP; +} + +static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const struct dxil_block *block, + struct sm6_function *function) +{ + struct vkd3d_shader_instruction *ins; + const struct dxil_record *record; + struct sm6_block *code_block; + unsigned int i, block_idx; + struct sm6_value *dst; + bool ret_found; + enum + { + RESULT_VALUE, + RESULT_TERMINATE, + } result_type; + + if (sm6->function_count) + { + FIXME("Multiple functions are not supported yet.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + if (!(function->declaration = sm6_parser_next_function_definition(sm6))) + { + WARN("Failed to find definition to match function body.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (block->record_count < 2) + { + /* It should contain at least a block count and a RET instruction. */ + WARN("Invalid function block record count %zu.\n", block->record_count); + return VKD3D_ERROR_INVALID_SHADER; + } + if (block->records[0]->code != FUNC_CODE_DECLAREBLOCKS || !block->records[0]->operand_count + || block->records[0]->operands[0] > UINT_MAX) + { + WARN("Block count declaration not found or invalid.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (!(function->block_count = block->records[0]->operands[0])) + { + WARN("Function contains no blocks.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + if (function->block_count > 1) + { + FIXME("Branched shaders are not supported yet.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (!(function->blocks[0] = sm6_block_create())) + { + ERR("Failed to allocate code block.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + code_block = function->blocks[0]; + + for (i = 1, block_idx = 0, ret_found = false; i < block->record_count; ++i) + { + /* block->record_count - 1 is the instruction count, but some instructions + * can emit >1 IR instruction, so extra may be used. */ + if (!vkd3d_array_reserve((void **)&code_block->instructions, &code_block->instruction_capacity, + max(code_block->instruction_count + 1, block->record_count), sizeof(*code_block->instructions))) + { + ERR("Failed to allocate instructions.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + + ins = &code_block->instructions[code_block->instruction_count]; + ins->handler_idx = VKD3DSIH_INVALID; + + dst = sm6_parser_get_current_value(sm6); + dst->type = NULL; + dst->value_type = VALUE_TYPE_REG; + result_type = RESULT_VALUE; + + record = block->records[i]; + switch (record->code) + { + case FUNC_CODE_INST_RET: + sm6_parser_emit_ret(sm6, record, code_block, ins); + result_type = RESULT_TERMINATE; + ret_found = true; + break; + default: + FIXME("Unhandled dxil instruction %u.\n", record->code); + return VKD3D_ERROR_INVALID_SHADER; + } + + if (result_type == RESULT_TERMINATE) + { + ++block_idx; + code_block = (block_idx < function->block_count) ? function->blocks[block_idx] : NULL; + } + if (code_block) + code_block->instruction_count += ins->handler_idx != VKD3DSIH_NOP; + else + assert(ins->handler_idx == VKD3DSIH_NOP); + sm6->value_count += !!dst->type; + } + + if (!ret_found) + { + WARN("Function contains no RET instruction.\n"); + return VKD3D_ERROR_INVALID_SHADER; + } + + return VKD3D_OK; +} + static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const struct dxil_block *block, unsigned int level) { + unsigned int i, old_value_count = sm6->value_count; + struct sm6_function *function; enum vkd3d_result ret; - unsigned int i;
for (i = 0; i < block->child_block_count; ++i) { @@ -1682,6 +1900,17 @@ static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const st case CONSTANTS_BLOCK: return sm6_parser_constants_init(sm6, block);
+ case FUNCTION_BLOCK: + function = &sm6->functions[sm6->function_count]; + if ((ret = sm6_parser_function_init(sm6, block, function)) < 0) + return ret; + /* The value index returns to its previous value after handling a function. It's usually nonzero + * at the start because of global constants/variables/function declarations. Function constants + * occur in a child block, so value_count is already saved before they are emitted. */ + memset(&sm6->values[old_value_count], 0, (sm6->value_count - old_value_count) * sizeof(*sm6->values)); + sm6->value_count = old_value_count; + break; + case BLOCKINFO_BLOCK: case MODULE_BLOCK: case PARAMATTR_BLOCK: @@ -1735,6 +1964,24 @@ static void sm6_symtab_cleanup(struct sm6_symbol *symbols, unsigned int count) vkd3d_free(symbols); }
+static void sm6_block_destroy(struct sm6_block *block) +{ + vkd3d_free(block->instructions); + vkd3d_free(block); +} + +static void sm6_functions_cleanup(struct sm6_function *functions, unsigned int count) +{ + unsigned int i, j; + + for (i = 0; i < count; ++i) + { + for (j = 0; j < functions[i].block_count; ++j) + sm6_block_destroy(functions[i].blocks[j]); + } + vkd3d_free(functions); +} + static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) { struct sm6_parser *sm6 = sm6_parser(parser); @@ -1744,6 +1991,7 @@ static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) shader_instruction_array_destroy(&parser->instructions); 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); vkd3d_free(sm6->values); free_shader_desc(&parser->shader_desc); vkd3d_free(sm6); @@ -1758,8 +2006,8 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t const char *source_name, struct vkd3d_shader_message_context *message_context) { const struct vkd3d_shader_location location = {.source_name = source_name}; + unsigned int count, length, chunk_offset, chunk_size, function_count; uint32_t version_token, dxil_version, token_count, magic; - unsigned int count, length, chunk_offset, chunk_size; enum bitcode_block_abbreviation abbr; struct vkd3d_shader_version version; struct dxil_block *block; @@ -1910,6 +2158,14 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t return ret; }
+ function_count = dxil_block_compute_function_count(&sm6->root_block); + if (!(sm6->functions = vkd3d_calloc(function_count, sizeof(*sm6->functions)))) + { + ERR("Failed to allocate function array.\n"); + vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating DXIL function array."); + return VKD3D_ERROR_OUT_OF_MEMORY; + } sm6_parser_compute_max_value_count(sm6, &sm6->root_block, 0); if (!(sm6->values = vkd3d_calloc(sm6->value_capacity, sizeof(*sm6->values)))) {
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
+{
- unsigned int i, j, count, type_count, type_index;
- const struct dxil_record *record;
- const struct dxil_block *block;
- char *struct_name = NULL;
- struct sm6_type *type;
- uint64_t type_id;
- bool is_unique;
- if (!(block = sm6_parser_get_level_one_block(sm6, TYPE_BLOCK, &is_unique)))
- {
WARN("No type definitions found.\n");
return VKD3D_OK;
- }
- if (!is_unique)
FIXME("Ignoring extra type table(s).\n");
Is this a `FIXME()` because it can be legal to have more than one type table?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
WARN("Missing operands for function type %u.\n", type_index);
return VKD3D_ERROR_INVALID_SHADER;
}
if (record->operands[0])
FIXME("Unhandled vararg function type %u.\n", type_index);
type->class = TYPE_CLASS_FUNCTION;
if ((type_id = record->operands[1]) >= type_count)
{
WARN("Invalid return type id %"PRIu64" for type %u.\n", type_id, type_index);
return VKD3D_ERROR_INVALID_SHADER;
}
count = record->operand_count - 2;
if (!(type->u.function = vkd3d_malloc(offsetof(struct sm6_function_info, param_types[count]))))
I know this is a standard pattern, but it just occurred to me that this could overflow, and I doubt `offsetof()` is protected against that. So in theory, at least for 32 bit builds, I guess this could be a security issue. This also happens for structures.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
break;
case TYPE_CODE_INTEGER:
{
uint64_t width;
if (!record->operand_count)
{
WARN("Missing integer width for type %u.\n", type_index);
return VKD3D_ERROR_INVALID_SHADER;
}
type->class = TYPE_CLASS_INTEGER;
width = record->operands[0];
if (width > 64 || (width > 1 && width < 8) || vkd3d_popcount(width) != 1)
It's not a big deal, but isn't it more readable to just list the five accepted values?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- for (i = 0; i < block->record_count; ++i)
- {
record = block->records[i];
type = &sm6->types[sm6->type_count];
type_index = sm6->type_count;
switch (record->code)
{
case TYPE_CODE_ARRAY:
case TYPE_CODE_VECTOR:
if (record->operand_count < 2)
{
WARN("Missing operands for array/vector type %u.\n", type_index);
return VKD3D_ERROR_INVALID_SHADER;
}
Shouldn't we also `WARN()` if there are too many operands? Here and for all other type classes.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
count = record->operand_count - 2;
if (!(type->u.function = vkd3d_malloc(offsetof(struct sm6_function_info, param_types[count]))))
{
ERR("Failed to allocate function parameter types.\n");
return VKD3D_ERROR_OUT_OF_MEMORY;
}
type->u.function->ret_type = &sm6->types[type_id];
type->u.function->param_count = count;
for (j = 0; j < count; ++j)
{
if ((type_id = record->operands[j + 2]) >= type_count)
{
WARN("Invalid parameter type id %"PRIu64" for type %u.\n", type_id, type_index);
vkd3d_free(type->u.function);
If you free without zeroing the pointer you're prone to double freeing, aren't you? This also happens for structures.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
}
type->u.struc->elem_types[j] = &sm6->types[type_id];
}
if (record->code == TYPE_CODE_STRUCT_ANON)
{
type->u.struc->name = NULL;
break;
}
type->u.struc->name = struct_name;
struct_name = NULL;
break;
case TYPE_CODE_STRUCT_NAME:
if (!(struct_name = dxil_record_to_string(record)))
Maybe we can `WARN()` is the following record is not a named structure? Or reject the shader outright?
So far I just managed to review the first commit. Will continue later.
On Thu Jun 29 12:56:29 2023 +0000, Giovanni Mascellani wrote:
Is this a `FIXME()` because it can be legal to have more than one type table?
Looking at the LLVM source, it's not legal so this can be a WARN.
On Thu Jun 29 12:56:35 2023 +0000, Giovanni Mascellani wrote:
If you free without zeroing the pointer you're prone to double freeing, aren't you? This also happens for structures.
The type count is not incremented yet so freeing is necessary. I figure it's simpler than having the cleanup function deal with partially instantiated types.
On Thu Jun 29 12:56:36 2023 +0000, Giovanni Mascellani wrote:
Maybe we can `WARN()` is the following record is not a named structure? Or reject the shader outright?
The LLVM source doesn't check for this. It would be a bit odd but not invalid.
On Thu Jun 29 13:52:31 2023 +0000, Conor McCarthy wrote:
The type count is not incremented yet so freeing is necessary. I figure it's simpler than having the cleanup function deal with partially instantiated types.
Oh, right.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- {
record = block->records[i];
if (record->code != VST_CODE_ENTRY)
{
FIXME("Unhandled symtab code %u.\n", record->code);
continue;
}
if (record->operand_count < 2)
{
WARN("Missing operands for function symbol %u.\n", sm6->global_symbol_count);
continue;
}
symbol = &sm6->global_symbols[sm6->global_symbol_count];
symbol->id = record->operands[0];
Are IDs supposed to be unique? Should we validate that?
BTW, what's the typical number of symbols you're seeing here? Given that in later patches you're linearly scanning throught this array, would it make sense to sort it from the get-go and then use a binary search?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- }
- return value_count;
+}
+static bool sm6_parser_declare_function(struct sm6_parser *sm6, const struct dxil_record *record) +{
- const struct sm6_type *ret_type;
- struct sm6_value *fn;
- unsigned int i, j;
- if (record->operand_count < 5)
- {
WARN("Missing operands for function %u.\n", sm6->value_count);
return false;
- }
Which reference are you using? Unless I'm mistaken, DXIL is based on LLVM 3.7, and both [in the code](https://github.com/jtcriswell/safecode-llvm37/blob/master/lib/Bitcode/Reader...) and [in the documentation](https://releases.llvm.org/3.7.0/docs/BitCodeFormat.html#module-code-function...) it seems that you need at least 8 operands. The same seems to happen [in Microsoft's own code](https://github.com/microsoft/DirectXShaderCompiler/blob/main/lib/Bitcode/Rea...).
On Sun Jul 2 12:06:26 2023 +0000, Giovanni Mascellani wrote:
Are IDs supposed to be unique? Should we validate that? BTW, what's the typical number of symbols you're seeing here? Given that in later patches you're linearly scanning throught this array, would it make sense to sort it from the get-go and then use a binary search?
HZD shaders have mostly 8-15 but as high as 34. I think initially a sort for validation will suffice, and binary search can be a todo.
On Sun Jul 2 12:06:26 2023 +0000, Giovanni Mascellani wrote:
Which reference are you using? Unless I'm mistaken, DXIL is based on LLVM 3.7, and both [in the code](https://github.com/jtcriswell/safecode-llvm37/blob/master/lib/Bitcode/Reader...) and [in the documentation](https://releases.llvm.org/3.7.0/docs/BitCodeFormat.html#module-code-function...) it seems that you need at least 8 operands. The same seems to happen [in Microsoft's own code](https://github.com/microsoft/DirectXShaderCompiler/blob/main/lib/Bitcode/Rea...).
Not sure where that number came from. I'll use 8.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- for (i = 0; i < block->child_block_count; ++i)
value_count = sm6_parser_compute_max_value_count(sm6, block->child_blocks[i], value_count);
- switch (block->id)
- {
case FUNCTION_BLOCK:
sm6->value_capacity = max(sm6->value_capacity, value_count);
/* The value count returns to its previous value after handling a function. */
value_count = old_value_count;
break;
default:
break;
- }
- return value_count;
+}
I don't understand what this function is supposed to calculate. At least for the moment, unless I'm missing something, the number of values that get allocated should exactly be the number of `MODULE_CODE_FUNCTION` records in the root block: why can't you just compute that thing and need something more complicated?
Relatedly, and even more if we need this complicated function to decide how many values we need to store, maybe `sm6_parser_get_current_value()` could `assert()` that we're not overflowing.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- return type->class == TYPE_CLASS_POINTER;
+}
+static bool sm6_type_is_numeric_aggregate(const struct sm6_type *type) +{
- unsigned int i;
- switch (type->class)
- {
case TYPE_CLASS_ARRAY:
case TYPE_CLASS_VECTOR:
return sm6_type_is_numeric(type->u.array.elem_type);
case TYPE_CLASS_STRUCT:
for (i = 0; i < type->u.struc->elem_count; ++i)
if (!sm6_type_is_numeric(type->u.struc->elem_types[i]))
That means that `struct { struct { int x; }; }` is not a numeric aggregate, is that intended?
On Mon Jul 3 15:32:50 2023 +0000, Giovanni Mascellani wrote:
That means that `struct { struct { int x; }; }` is not a numeric aggregate, is that intended?
Yes, so if a nested struct shows up in a shader I can see what it's used for and check if it causes any issues. I'll add a comment about it.