From 8493f61af5f79b519de42725d69cd4f519ae5cee Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Sat, 22 Jul 2023 10:12:24 +1000 Subject: [PATCH] Updated vkd3d-latest patchset --- ...-D3DXSaveTextureToFile-to-save-simpl.patch | 26 +- ...771e442af16228a977eebba82224f06f6d02.patch | 4 +- ...-2a3413e0f01524f2068bce12100906eb220.patch | 5 +- ...-d4d960cb8b4f503ce3de51d9f29267ca938.patch | 5 +- ...-3bafd036bb53bf211cb8b05651835aba6fb.patch | 3733 +++++++++++++++++ 5 files changed, 3752 insertions(+), 21 deletions(-) create mode 100644 patches/vkd3d-latest/0004-Updated-vkd3d-to-3bafd036bb53bf211cb8b05651835aba6fb.patch diff --git a/patches/d3dx9_36-DDS/0002-d3dx9_36-Improve-D3DXSaveTextureToFile-to-save-simpl.patch b/patches/d3dx9_36-DDS/0002-d3dx9_36-Improve-D3DXSaveTextureToFile-to-save-simpl.patch index f5976f50..55686ecb 100644 --- a/patches/d3dx9_36-DDS/0002-d3dx9_36-Improve-D3DXSaveTextureToFile-to-save-simpl.patch +++ b/patches/d3dx9_36-DDS/0002-d3dx9_36-Improve-D3DXSaveTextureToFile-to-save-simpl.patch @@ -1,4 +1,4 @@ -From f22ce02d880a9a6723d202eaf959d1eaa7a4c3df Mon Sep 17 00:00:00 2001 +From 28b0030ac5f486875dff6f5f34779844f1a031cf Mon Sep 17 00:00:00 2001 From: Christian Costa Date: Sun, 11 Jan 2015 16:29:30 +0100 Subject: [PATCH] d3dx9_36: Improve D3DXSaveTextureToFile to save simple @@ -11,23 +11,23 @@ Subject: [PATCH] d3dx9_36: Improve D3DXSaveTextureToFile to save simple 3 files changed, 65 insertions(+), 4 deletions(-) diff --git a/dlls/d3dx9_36/d3dx9_private.h b/dlls/d3dx9_36/d3dx9_private.h -index 9497343f422..679b57b2626 100644 +index 5d8f5b332f8..f1f41c6e4d7 100644 --- a/dlls/d3dx9_36/d3dx9_private.h +++ b/dlls/d3dx9_36/d3dx9_private.h -@@ -125,6 +125,8 @@ HRESULT lock_surface(IDirect3DSurface9 *surface, const RECT *surface_rect, D3DLO - IDirect3DSurface9 **temp_surface, BOOL write) DECLSPEC_HIDDEN; +@@ -127,6 +127,8 @@ HRESULT lock_surface(IDirect3DSurface9 *surface, const RECT *surface_rect, D3DLO + IDirect3DSurface9 **temp_surface, BOOL write); HRESULT unlock_surface(IDirect3DSurface9 *surface, const RECT *surface_rect, - IDirect3DSurface9 *temp_surface, BOOL update) DECLSPEC_HIDDEN; + IDirect3DSurface9 *temp_surface, BOOL update); +HRESULT save_dds_texture_to_memory(ID3DXBuffer **dst_buffer, IDirect3DBaseTexture9 *src_texture, -+ const PALETTEENTRY *src_palette) DECLSPEC_HIDDEN; ++ const PALETTEENTRY *src_palette); - unsigned short float_32_to_16(const float in) DECLSPEC_HIDDEN; - float float_16_to_32(const unsigned short in) DECLSPEC_HIDDEN; + unsigned short float_32_to_16(const float in); + float float_16_to_32(const unsigned short in); diff --git a/dlls/d3dx9_36/surface.c b/dlls/d3dx9_36/surface.c -index aa9ee891b54..9871d211a7c 100644 +index ca40018af24..9ff5b4901b8 100644 --- a/dlls/d3dx9_36/surface.c +++ b/dlls/d3dx9_36/surface.c -@@ -636,6 +636,68 @@ static HRESULT save_dds_surface_to_memory(ID3DXBuffer **dst_buffer, IDirect3DSur +@@ -650,6 +650,68 @@ static HRESULT save_dds_surface_to_memory(ID3DXBuffer **dst_buffer, IDirect3DSur return D3D_OK; } @@ -97,10 +97,10 @@ index aa9ee891b54..9871d211a7c 100644 const D3DBOX *dst_box, const void *src_data, const D3DBOX *src_box, DWORD filter, D3DCOLOR color_key, const D3DXIMAGE_INFO *src_info) diff --git a/dlls/d3dx9_36/texture.c b/dlls/d3dx9_36/texture.c -index 26d07ca9f66..b743d5cfad0 100644 +index 2ee79b51f79..7ceef158d07 100644 --- a/dlls/d3dx9_36/texture.c +++ b/dlls/d3dx9_36/texture.c -@@ -1904,10 +1904,7 @@ HRESULT WINAPI D3DXSaveTextureToFileInMemory(ID3DXBuffer **dst_buffer, D3DXIMAGE +@@ -1858,10 +1858,7 @@ HRESULT WINAPI D3DXSaveTextureToFileInMemory(ID3DXBuffer **dst_buffer, D3DXIMAGE if (!dst_buffer || !src_texture) return D3DERR_INVALIDCALL; if (file_format == D3DXIFF_DDS) @@ -113,5 +113,5 @@ index 26d07ca9f66..b743d5cfad0 100644 type = IDirect3DBaseTexture9_GetType(src_texture); switch (type) -- -2.17.1 +2.40.1 diff --git a/patches/vkd3d-latest/0001-Update-vkd3d-to-771e442af16228a977eebba82224f06f6d02.patch b/patches/vkd3d-latest/0001-Update-vkd3d-to-771e442af16228a977eebba82224f06f6d02.patch index de395956..a54d7616 100644 --- a/patches/vkd3d-latest/0001-Update-vkd3d-to-771e442af16228a977eebba82224f06f6d02.patch +++ b/patches/vkd3d-latest/0001-Update-vkd3d-to-771e442af16228a977eebba82224f06f6d02.patch @@ -1,7 +1,7 @@ -From 32b3870975bb6f3f08ea8722a7c500275c98864a Mon Sep 17 00:00:00 2001 +From 708e3732ca7672cdde41f3662664265de7f4129e Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Wed, 17 May 2023 08:35:40 +1000 -Subject: [PATCH 1/3] Update vkd3d to 771e442af16228a977eebba82224f06f6d0202fe +Subject: [PATCH] Update vkd3d to 771e442af16228a977eebba82224f06f6d0202fe (1.8) --- diff --git a/patches/vkd3d-latest/0002-Updated-vkd3d-to-2a3413e0f01524f2068bce12100906eb220.patch b/patches/vkd3d-latest/0002-Updated-vkd3d-to-2a3413e0f01524f2068bce12100906eb220.patch index 77ed553f..4fed832b 100644 --- a/patches/vkd3d-latest/0002-Updated-vkd3d-to-2a3413e0f01524f2068bce12100906eb220.patch +++ b/patches/vkd3d-latest/0002-Updated-vkd3d-to-2a3413e0f01524f2068bce12100906eb220.patch @@ -1,8 +1,7 @@ -From 950b3671fefac301c6346f2b1067070eeb46b2ac Mon Sep 17 00:00:00 2001 +From 420b1f91c16ada4b3e8183cced1d4b60310b85cf Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Wed, 28 Jun 2023 16:27:03 +1000 -Subject: [PATCH 2/3] Updated vkd3d to - 2a3413e0f01524f2068bce12100906eb2200c965. +Subject: [PATCH] Updated vkd3d to 2a3413e0f01524f2068bce12100906eb2200c965. --- include/d3d12.idl | 4 +- diff --git a/patches/vkd3d-latest/0003-Updated-vkd3d-to-d4d960cb8b4f503ce3de51d9f29267ca938.patch b/patches/vkd3d-latest/0003-Updated-vkd3d-to-d4d960cb8b4f503ce3de51d9f29267ca938.patch index 21942f0b..acec92b9 100644 --- a/patches/vkd3d-latest/0003-Updated-vkd3d-to-d4d960cb8b4f503ce3de51d9f29267ca938.patch +++ b/patches/vkd3d-latest/0003-Updated-vkd3d-to-d4d960cb8b4f503ce3de51d9f29267ca938.patch @@ -1,8 +1,7 @@ -From a1cfa7f60bd15cc0f6df8ecddfb05204d64ef60e Mon Sep 17 00:00:00 2001 +From 4f0d4cfe7cfe53c49aadb4b04ed852dc66d8ea2c Mon Sep 17 00:00:00 2001 From: Alistair Leslie-Hughes Date: Tue, 11 Jul 2023 09:11:10 +1000 -Subject: [PATCH 3/3] Updated vkd3d to - d4d960cb8b4f503ce3de51d9f29267ca938f3183. +Subject: [PATCH] Updated vkd3d to d4d960cb8b4f503ce3de51d9f29267ca938f3183. --- libs/vkd3d/libs/vkd3d-shader/spirv.c | 5 +++++ diff --git a/patches/vkd3d-latest/0004-Updated-vkd3d-to-3bafd036bb53bf211cb8b05651835aba6fb.patch b/patches/vkd3d-latest/0004-Updated-vkd3d-to-3bafd036bb53bf211cb8b05651835aba6fb.patch new file mode 100644 index 00000000..5dd1c800 --- /dev/null +++ b/patches/vkd3d-latest/0004-Updated-vkd3d-to-3bafd036bb53bf211cb8b05651835aba6fb.patch @@ -0,0 +1,3733 @@ +From dbc2b39e89483d17a08439c8eb2b7d415ece362b Mon Sep 17 00:00:00 2001 +From: Alistair Leslie-Hughes +Date: Sat, 22 Jul 2023 09:56:45 +1000 +Subject: [PATCH] Updated vkd3d to 3bafd036bb53bf211cb8b05651835aba6fb47ebc. + +--- + libs/vkd3d/include/private/vkd3d_common.h | 5 + + libs/vkd3d/libs/vkd3d-shader/dxil.c | 1640 +++++++++++++++-- + libs/vkd3d/libs/vkd3d-shader/hlsl.c | 102 +- + libs/vkd3d/libs/vkd3d-shader/hlsl.h | 13 +- + libs/vkd3d/libs/vkd3d-shader/hlsl.y | 241 ++- + libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c | 195 +- + libs/vkd3d/libs/vkd3d-shader/tpf.c | 335 ++-- + .../libs/vkd3d-shader/vkd3d_shader_private.h | 9 + + 8 files changed, 2120 insertions(+), 420 deletions(-) + +diff --git a/libs/vkd3d/include/private/vkd3d_common.h b/libs/vkd3d/include/private/vkd3d_common.h +index da15ee23fd3..0263fc47297 100644 +--- a/libs/vkd3d/include/private/vkd3d_common.h ++++ b/libs/vkd3d/include/private/vkd3d_common.h +@@ -173,6 +173,11 @@ static inline bool vkd3d_bound_range(size_t start, size_t count, size_t limit) + #endif + } + ++static inline bool vkd3d_object_range_overflow(size_t start, size_t count, size_t size) ++{ ++ return (~(size_t)0 - start) / size < count; ++} ++ + static inline uint16_t vkd3d_make_u16(uint8_t low, uint8_t high) + { + return low | ((uint16_t)high << 8); +diff --git a/libs/vkd3d/libs/vkd3d-shader/dxil.c b/libs/vkd3d/libs/vkd3d-shader/dxil.c +index 67dcd26a0e0..53a4c2da4ba 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/dxil.c ++++ b/libs/vkd3d/libs/vkd3d-shader/dxil.c +@@ -62,6 +62,159 @@ 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_module_code ++{ ++ MODULE_CODE_VERSION = 1, ++ MODULE_CODE_GLOBALVAR = 7, ++ 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_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, ++ 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, ++}; ++ ++enum bitcode_value_symtab_code ++{ ++ VST_CODE_ENTRY = 1, ++ VST_CODE_BBENTRY = 2, ++}; ++ ++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; ++}; ++ ++enum sm6_value_type ++{ ++ VALUE_TYPE_FUNCTION, ++ VALUE_TYPE_REG, ++}; ++ ++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; ++ struct vkd3d_shader_register reg; ++ } u; ++}; ++ + struct dxil_record + { + unsigned int code; +@@ -69,6 +222,27 @@ struct dxil_record + uint64_t operands[]; + }; + ++struct sm6_symbol ++{ ++ unsigned int id; ++ const char *name; ++}; ++ ++struct sm6_block ++{ ++ struct vkd3d_shader_instruction *instructions; ++ size_t instruction_capacity; ++ size_t instruction_count; ++}; ++ ++struct sm6_function ++{ ++ const struct sm6_value *declaration; ++ ++ struct sm6_block *blocks[1]; ++ size_t block_count; ++}; ++ + struct dxil_block + { + const struct dxil_block *parent; +@@ -106,6 +280,19 @@ struct sm6_parser + size_t abbrev_capacity; + size_t abbrev_count; + ++ struct sm6_type *types; ++ size_t type_count; ++ ++ struct sm6_symbol *global_symbols; ++ size_t global_symbol_count; ++ ++ struct sm6_function *functions; ++ size_t function_count; ++ ++ struct sm6_value *values; ++ size_t value_count; ++ size_t value_capacity; ++ + struct vkd3d_shader_parser p; + }; + +@@ -128,6 +315,12 @@ struct dxil_global_abbrev + struct dxil_abbrev abbrev; + }; + ++static size_t size_add_with_overflow_check(size_t a, size_t b) ++{ ++ size_t i = a + b; ++ return (i < a) ? SIZE_MAX : i; ++} ++ + static struct sm6_parser *sm6_parser(struct vkd3d_shader_parser *parser) + { + return CONTAINING_RECORD(parser, struct sm6_parser, p); +@@ -628,10 +821,10 @@ static enum vkd3d_result dxil_block_read(struct dxil_block *parent, struct sm6_p + return VKD3D_ERROR_INVALID_SHADER; + } + +-static unsigned int sm6_parser_compute_global_abbrev_count_for_block_id(struct sm6_parser *sm6, ++static size_t sm6_parser_compute_global_abbrev_count_for_block_id(struct sm6_parser *sm6, + unsigned int block_id) + { +- unsigned int i, count; ++ size_t i, count; + + for (i = 0, count = 0; i < sm6->abbrev_count; ++i) + count += sm6->abbrevs[i]->block_id == block_id; +@@ -641,7 +834,7 @@ static unsigned int sm6_parser_compute_global_abbrev_count_for_block_id(struct s + + static void dxil_block_destroy(struct dxil_block *block) + { +- unsigned int i; ++ size_t i; + + for (i = 0; i < block->record_count; ++i) + vkd3d_free(block->records[i]); +@@ -663,7 +856,7 @@ static void dxil_block_destroy(struct dxil_block *block) + static enum vkd3d_result dxil_block_init(struct dxil_block *block, const struct dxil_block *parent, + struct sm6_parser *sm6) + { +- unsigned int i, abbrev_count = 0; ++ size_t i, abbrev_count = 0; + enum vkd3d_result ret; + + block->parent = parent; +@@ -705,159 +898,1360 @@ static enum vkd3d_result dxil_block_init(struct dxil_block *block, const struct + return ret; + } + +-static void dxil_global_abbrevs_cleanup(struct dxil_global_abbrev **abbrevs, unsigned int count) ++static size_t dxil_block_compute_function_count(const struct dxil_block *root) + { +- unsigned int i; ++ size_t i, count; ++ ++ for (i = 0, count = 0; i < root->child_block_count; ++i) ++ count += root->child_blocks[i]->id == FUNCTION_BLOCK; ++ ++ return count; ++} ++ ++static size_t dxil_block_compute_module_decl_count(const struct dxil_block *block) ++{ ++ size_t i, count; ++ ++ for (i = 0, count = 0; i < block->record_count; ++i) ++ count += block->records[i]->code == MODULE_CODE_FUNCTION; ++ return count; ++} ++ ++static size_t dxil_block_compute_constants_count(const struct dxil_block *block) ++{ ++ size_t 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, size_t count) ++{ ++ size_t i; + + for (i = 0; i < count; ++i) + vkd3d_free(abbrevs[i]); + vkd3d_free(abbrevs); + } + +-static void sm6_parser_destroy(struct vkd3d_shader_parser *parser) ++static const struct dxil_block *sm6_parser_get_level_one_block(const struct sm6_parser *sm6, ++ enum bitcode_block_id id, bool *is_unique) + { +- struct sm6_parser *sm6 = sm6_parser(parser); ++ const struct dxil_block *block, *found = NULL; ++ size_t i; + +- dxil_block_destroy(&sm6->root_block); +- dxil_global_abbrevs_cleanup(sm6->abbrevs, sm6->abbrev_count); +- shader_instruction_array_destroy(&parser->instructions); +- free_shader_desc(&parser->shader_desc); +- vkd3d_free(sm6); ++ 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 const struct vkd3d_shader_parser_ops sm6_parser_ops = ++static char *dxil_record_to_string(const struct dxil_record *record, unsigned int offset) + { +- .parser_destroy = sm6_parser_destroy, +-}; ++ unsigned int i; ++ char *str; + +-static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t *byte_code, size_t byte_code_size, +- const char *source_name, struct vkd3d_shader_message_context *message_context) ++ assert(offset <= record->operand_count); ++ if (!(str = vkd3d_calloc(record->operand_count - offset + 1, 1))) ++ return NULL; ++ ++ for (i = offset; i < record->operand_count; ++i) ++ str[i - offset] = record->operands[i]; ++ ++ return str; ++} ++ ++static bool dxil_record_validate_operand_min_count(const struct dxil_record *record, unsigned int min_count, ++ struct sm6_parser *sm6) + { +- const struct vkd3d_shader_location location = {.source_name = source_name}; +- 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; +- enum vkd3d_result ret; ++ if (record->operand_count >= min_count) ++ return true; + +- count = byte_code_size / sizeof(*byte_code); +- if (count < 6) +- { +- WARN("Invalid data size %zu.\n", byte_code_size); +- vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_SIZE, +- "DXIL chunk size %zu is smaller than the DXIL header size.", byte_code_size); +- return VKD3D_ERROR_INVALID_SHADER; +- } ++ WARN("Invalid operand count %u for code %u.\n", record->operand_count, record->code); ++ vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, ++ "Invalid operand count %u for record code %u.", record->operand_count, record->code); ++ return false; ++} + +- version_token = byte_code[0]; +- TRACE("Compiler version: 0x%08x.\n", version_token); +- token_count = byte_code[1]; +- TRACE("Token count: %u.\n", token_count); ++static void dxil_record_validate_operand_max_count(const struct dxil_record *record, unsigned int max_count, ++ struct sm6_parser *sm6) ++{ ++ if (record->operand_count <= max_count) ++ return; + +- if (token_count < 6 || count < token_count) ++ WARN("Ignoring %u extra operands for code %u.\n", record->operand_count - max_count, record->code); ++ vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_IGNORING_OPERANDS, ++ "Ignoring %u extra operands for record code %u.", record->operand_count - max_count, record->code); ++} ++ ++static bool dxil_record_validate_operand_count(const struct dxil_record *record, unsigned int min_count, ++ unsigned int max_count, struct sm6_parser *sm6) ++{ ++ dxil_record_validate_operand_max_count(record, max_count, sm6); ++ return dxil_record_validate_operand_min_count(record, min_count, sm6); ++} ++ ++static enum vkd3d_result sm6_parser_type_table_init(struct sm6_parser *sm6) ++{ ++ const struct dxil_record *record; ++ size_t i, type_count, type_index; ++ const struct dxil_block *block; ++ char *struct_name = NULL; ++ unsigned int j, count; ++ struct sm6_type *type; ++ uint64_t type_id; ++ bool is_unique; ++ ++ sm6->p.location.line = 0; ++ sm6->p.location.column = 0; ++ ++ if (!(block = sm6_parser_get_level_one_block(sm6, TYPE_BLOCK, &is_unique))) + { +- WARN("Invalid token count %u (word count %u).\n", token_count, count); +- vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE, +- "DXIL chunk token count %#x is invalid (word count %u).", token_count, count); +- return VKD3D_ERROR_INVALID_SHADER; ++ WARN("No type definitions found.\n"); ++ return VKD3D_OK; + } ++ if (!is_unique) ++ WARN("Ignoring invalid extra type table(s).\n"); + +- if (byte_code[2] != TAG_DXIL) +- WARN("Unknown magic number 0x%08x.\n", byte_code[2]); ++ sm6->p.location.line = block->id; + +- dxil_version = byte_code[3]; +- if (dxil_version > 0x102) +- WARN("Unknown DXIL version: 0x%08x.\n", dxil_version); +- else +- TRACE("DXIL version: 0x%08x.\n", dxil_version); ++ 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; + +- chunk_offset = byte_code[4]; +- if (chunk_offset < 16 || chunk_offset >= byte_code_size) +- { +- WARN("Invalid bitcode chunk offset %#x (data size %zu).\n", chunk_offset, byte_code_size); +- vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_OFFSET, +- "DXIL bitcode chunk has invalid offset %#x (data size %#zx).", chunk_offset, byte_code_size); +- return VKD3D_ERROR_INVALID_SHADER; +- } +- chunk_size = byte_code[5]; +- if (chunk_size > byte_code_size - chunk_offset) ++ /* The type array must not be relocated. */ ++ if (!(sm6->types = vkd3d_calloc(type_count, sizeof(*sm6->types)))) + { +- WARN("Invalid bitcode chunk size %#x (data size %zu, chunk offset %#x).\n", +- chunk_size, byte_code_size, chunk_offset); +- vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE, +- "DXIL bitcode chunk has invalid size %#x (data size %#zx, chunk offset %#x).", +- chunk_size, byte_code_size, chunk_offset); +- return VKD3D_ERROR_INVALID_SHADER; ++ ERR("Failed to allocate type array.\n"); ++ return VKD3D_ERROR_OUT_OF_MEMORY; + } + +- sm6->start = (const uint32_t *)((const char*)&byte_code[2] + chunk_offset); +- if ((magic = sm6->start[0]) != BITCODE_MAGIC) ++ for (i = 0; i < block->record_count; ++i) + { +- WARN("Unknown magic number 0x%08x.\n", magic); +- vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER, +- "DXIL bitcode chunk magic number 0x%08x is not the expected 0x%08x.", magic, BITCODE_MAGIC); +- } ++ sm6->p.location.column = i; ++ record = block->records[i]; + +- sm6->end = &sm6->start[(chunk_size + sizeof(*sm6->start) - 1) / sizeof(*sm6->start)]; ++ type = &sm6->types[sm6->type_count]; ++ type_index = sm6->type_count; + +- if ((version.type = version_token >> 16) >= VKD3D_SHADER_TYPE_COUNT) +- { +- FIXME("Unknown shader type %#x.\n", version.type); +- vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE, +- "Unknown shader type %#x.", version.type); +- } ++ switch (record->code) ++ { ++ case TYPE_CODE_ARRAY: ++ case TYPE_CODE_VECTOR: ++ if (!dxil_record_validate_operand_count(record, 2, 2, sm6)) ++ return VKD3D_ERROR_INVALID_SHADER; + +- version.major = VKD3D_SM6_VERSION_MAJOR(version_token); +- version.minor = VKD3D_SM6_VERSION_MINOR(version_token); ++ type->class = record->code == TYPE_CODE_ARRAY ? TYPE_CLASS_ARRAY : TYPE_CLASS_VECTOR; + +- if ((abbr = sm6->start[1] & 3) != ENTER_SUBBLOCK) +- { +- WARN("Initial block abbreviation %u is not ENTER_SUBBLOCK.\n", abbr); +- vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE, +- "DXIL bitcode chunk has invalid initial block abbreviation %u.", abbr); +- return VKD3D_ERROR_INVALID_SHADER; +- } ++ if (!(type->u.array.count = record->operands[0])) ++ { ++ TRACE("Setting unbounded for type %zu.\n", type_index); ++ type->u.array.count = UINT_MAX; ++ } + +- /* Estimate instruction count to avoid reallocation in most shaders. */ +- count = max(token_count, 400) - 400; +- vkd3d_shader_parser_init(&sm6->p, message_context, source_name, &version, &sm6_parser_ops, +- (count + (count >> 2)) / 2u + 10); +- sm6->ptr = &sm6->start[1]; +- sm6->bitpos = 2; ++ if ((type_id = record->operands[1]) >= type_count) ++ { ++ WARN("Invalid contained type id %"PRIu64" for type %zu.\n", type_id, type_index); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ type->u.array.elem_type = &sm6->types[type_id]; ++ break; + +- block = &sm6->root_block; +- if ((ret = dxil_block_init(block, NULL, 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 bitcode chunk."); +- else if (ret == VKD3D_ERROR_INVALID_SHADER) +- vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE, +- "DXIL bitcode chunk has invalid bitcode."); +- else +- vkd3d_unreachable(); +- return ret; +- } ++ case TYPE_CODE_DOUBLE: ++ dxil_record_validate_operand_max_count(record, 0, sm6); ++ type->class = TYPE_CLASS_FLOAT; ++ type->u.width = 64; ++ break; + +- dxil_global_abbrevs_cleanup(sm6->abbrevs, sm6->abbrev_count); +- sm6->abbrevs = NULL; +- sm6->abbrev_count = 0; ++ case TYPE_CODE_FLOAT: ++ dxil_record_validate_operand_max_count(record, 0, sm6); ++ type->class = TYPE_CLASS_FLOAT; ++ type->u.width = 32; ++ break; + +- length = sm6->ptr - sm6->start - block->start; +- if (length != block->length) +- { +- WARN("Invalid block length %u; expected %u.\n", length, block->length); +- vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_BLOCK_LENGTH, +- "Root block ends with length %u but indicated length is %u.", length, block->length); +- } +- if (sm6->ptr != sm6->end) +- { +- unsigned int expected_length = sm6->end - sm6->start; +- length = sm6->ptr - sm6->start; +- WARN("Invalid module length %u; expected %u.\n", length, expected_length); +- vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_MODULE_LENGTH, +- "Module ends with length %u but indicated length is %u.", length, expected_length); ++ case TYPE_CODE_FUNCTION: ++ if (!dxil_record_validate_operand_min_count(record, 2, sm6)) ++ return VKD3D_ERROR_INVALID_SHADER; ++ if (record->operands[0]) ++ FIXME("Unhandled vararg function type %zu.\n", type_index); ++ ++ type->class = TYPE_CLASS_FUNCTION; ++ ++ if ((type_id = record->operands[1]) >= type_count) ++ { ++ WARN("Invalid return type id %"PRIu64" for type %zu.\n", type_id, type_index); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ ++ count = record->operand_count - 2; ++ if (vkd3d_object_range_overflow(sizeof(type->u.function), count, sizeof(type->u.function->param_types[0])) ++ || !(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 %zu.\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: ++ dxil_record_validate_operand_max_count(record, 0, sm6); ++ type->class = TYPE_CLASS_FLOAT; ++ type->u.width = 16; ++ break; ++ ++ case TYPE_CODE_INTEGER: ++ { ++ uint64_t width; ++ ++ if (!dxil_record_validate_operand_count(record, 1, 1, sm6)) ++ return VKD3D_ERROR_INVALID_SHADER; ++ ++ type->class = TYPE_CLASS_INTEGER; ++ ++ switch ((width = record->operands[0])) ++ { ++ case 1: ++ case 8: ++ case 16: ++ case 32: ++ case 64: ++ break; ++ default: ++ WARN("Invalid integer width %"PRIu64" for type %zu.\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 (!dxil_record_validate_operand_count(record, 1, 2, sm6)) ++ 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 %zu.\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 (!dxil_record_validate_operand_min_count(record, 2, sm6)) ++ return VKD3D_ERROR_INVALID_SHADER; ++ if (record->code == TYPE_CODE_STRUCT_NAMED && !struct_name) ++ { ++ WARN("Missing struct name before struct type %zu.\n", type_index); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ ++ type->class = TYPE_CLASS_STRUCT; ++ ++ count = record->operand_count - 1; ++ if (vkd3d_object_range_overflow(sizeof(type->u.struc), count, sizeof(type->u.struc->elem_types[0])) ++ || !(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 %zu.\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, 0))) ++ { ++ ERR("Failed to allocate struct name.\n"); ++ return VKD3D_ERROR_OUT_OF_MEMORY; ++ } ++ if (!struct_name[0]) ++ WARN("Struct name is empty for type %zu.\n", type_index); ++ continue; ++ ++ case TYPE_CODE_VOID: ++ dxil_record_validate_operand_max_count(record, 0, sm6); ++ type->class = TYPE_CLASS_VOID; ++ break; ++ ++ default: ++ FIXME("Unhandled type %u at index %zu.\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 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; ++} ++ ++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: ++ /* Do not handle nested structs. Support can be added if they show up. */ ++ 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_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"); ++} ++ ++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) ++{ ++ size_t 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(struct sm6_parser *sm6, uint64_t type_id) ++{ ++ if (type_id >= sm6->type_count) ++ { ++ WARN("Invalid type index %"PRIu64" at %zu.\n", type_id, sm6->value_count); ++ vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_ID, ++ "DXIL type id %"PRIu64" is invalid.", type_id); ++ return NULL; ++ } ++ return &sm6->types[type_id]; ++} ++ ++static int global_symbol_compare(const void *a, const void *b) ++{ ++ return vkd3d_u32_compare(((const struct sm6_symbol *)a)->id, ((const struct sm6_symbol *)b)->id); ++} ++ ++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; ++ size_t i, count; ++ bool is_unique; ++ ++ sm6->p.location.line = 0; ++ sm6->p.location.column = 0; ++ ++ 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"); ++ ++ sm6->p.location.line = block->id; ++ ++ 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) ++ { ++ sm6->p.location.column = i; ++ record = block->records[i]; ++ ++ if (record->code != VST_CODE_ENTRY) ++ { ++ FIXME("Unhandled symtab code %u.\n", record->code); ++ continue; ++ } ++ if (!dxil_record_validate_operand_min_count(record, 1, sm6)) ++ 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; ++ } ++ ++ sm6->p.location.column = block->record_count; ++ ++ qsort(sm6->global_symbols, sm6->global_symbol_count, sizeof(*sm6->global_symbols), global_symbol_compare); ++ for (i = 1; i < sm6->global_symbol_count; ++i) ++ { ++ if (sm6->global_symbols[i].id == sm6->global_symbols[i - 1].id) ++ { ++ WARN("Invalid duplicate symbol id %u.\n", sm6->global_symbols[i].id); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ } ++ ++ return VKD3D_OK; ++} ++ ++static const char *sm6_parser_get_global_symbol_name(const struct sm6_parser *sm6, size_t id) ++{ ++ size_t i, start; ++ ++ /* id == array index is normally true */ ++ i = start = id % sm6->global_symbol_count; ++ do ++ { ++ if (sm6->global_symbols[i].id == id) ++ return sm6->global_symbols[i].name; ++ i = (i + 1) % sm6->global_symbol_count; ++ } while (i != start); ++ ++ 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) ++{ ++ assert(sm6->value_count < sm6->value_capacity); ++ 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; ++} ++ ++/* Recurse through the block tree while maintaining a current value count. The current ++ * count is the sum of the global count plus all declarations within the current function. ++ * Store into value_capacity the highest count seen. */ ++static size_t sm6_parser_compute_max_value_count(struct sm6_parser *sm6, ++ const struct dxil_block *block, size_t value_count) ++{ ++ size_t i, old_value_count = value_count; ++ ++ if (block->id == MODULE_BLOCK) ++ value_count = size_add_with_overflow_check(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 CONSTANTS_BLOCK: ++ /* Function local constants are contained in a child block of the function block. */ ++ value_count = size_add_with_overflow_check(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 = size_add_with_overflow_check(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. */ ++ if (value_count < SIZE_MAX) ++ 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 unsigned int max_count = 15; ++ const struct sm6_type *ret_type; ++ struct sm6_value *fn; ++ unsigned int i, j; ++ ++ if (!dxil_record_validate_operand_count(record, 8, max_count, sm6)) ++ 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 %zu.\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])) ++ TRACE("Ignoring function attributes.\n"); ++ ++ /* These always seem to be zero. */ ++ for (i = 5, j = 0; i < min(record->operand_count, max_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 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; ++ struct sm6_value *dst; ++ size_t i, value_idx; ++ uint64_t value; ++ ++ for (i = 0, type = NULL; i < block->record_count; ++i) ++ { ++ sm6->p.location.column = i; ++ record = block->records[i]; ++ value_idx = sm6->value_count; ++ ++ if (record->code == CST_CODE_SETTYPE) ++ { ++ if (!dxil_record_validate_operand_count(record, 1, 1, sm6)) ++ 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 %zu 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 (!dxil_record_validate_operand_count(record, 1, 1, sm6)) ++ return VKD3D_ERROR_INVALID_SHADER; ++ ++ if (!sm6_type_is_integer(type)) ++ { ++ WARN("Invalid integer of non-integer type %u at constant idx %zu.\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 (!dxil_record_validate_operand_count(record, 1, 1, sm6)) ++ return VKD3D_ERROR_INVALID_SHADER; ++ ++ if (!sm6_type_is_floating_point(type)) ++ { ++ WARN("Invalid float of non-fp type %u at constant idx %zu.\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; ++ const struct dxil_record *record; ++ uint64_t version; ++ size_t i; ++ ++ sm6->p.location.line = block->id; ++ sm6->p.location.column = 0; ++ ++ for (i = 0; i < block->record_count; ++i) ++ { ++ sm6->p.location.column = i; ++ record = block->records[i]; ++ switch (record->code) ++ { ++ case MODULE_CODE_FUNCTION: ++ 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: ++ FIXME("Global variables are not implemented yet.\n"); ++ break; ++ ++ case MODULE_CODE_VERSION: ++ dxil_record_validate_operand_count(record, 1, 1, sm6); ++ 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 const struct sm6_value *sm6_parser_next_function_definition(struct sm6_parser *sm6) ++{ ++ size_t 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) ++{ ++ if (!dxil_record_validate_operand_count(record, 0, 1, sm6)) ++ return; ++ ++ 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; ++ struct sm6_value *dst; ++ size_t i, block_idx; ++ 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) ++ { ++ sm6->p.location.column = 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) ++{ ++ size_t i, old_value_count = sm6->value_count; ++ struct sm6_function *function; ++ enum vkd3d_result ret; ++ ++ 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; ++ } ++ ++ sm6->p.location.line = block->id; ++ sm6->p.location.column = 0; ++ ++ switch (block->id) ++ { ++ 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: ++ 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, size_t count) ++{ ++ size_t 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_symtab_cleanup(struct sm6_symbol *symbols, size_t count) ++{ ++ size_t i; ++ ++ for (i = 0; i < count; ++i) ++ vkd3d_free((void *)symbols[i].name); ++ 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, size_t count) ++{ ++ size_t 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); ++ ++ 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); ++ 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); ++} ++ ++static const struct vkd3d_shader_parser_ops sm6_parser_ops = ++{ ++ .parser_destroy = sm6_parser_destroy, ++}; ++ ++static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t *byte_code, size_t byte_code_size, ++ const char *source_name, struct vkd3d_shader_message_context *message_context) ++{ ++ const struct vkd3d_shader_location location = {.source_name = source_name}; ++ uint32_t version_token, dxil_version, token_count, magic; ++ unsigned int chunk_offset, chunk_size; ++ size_t count, length, function_count; ++ enum bitcode_block_abbreviation abbr; ++ struct vkd3d_shader_version version; ++ struct dxil_block *block; ++ enum vkd3d_result ret; ++ ++ count = byte_code_size / sizeof(*byte_code); ++ if (count < 6) ++ { ++ WARN("Invalid data size %zu.\n", byte_code_size); ++ vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_SIZE, ++ "DXIL chunk size %zu is smaller than the DXIL header size.", byte_code_size); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ ++ version_token = byte_code[0]; ++ TRACE("Compiler version: 0x%08x.\n", version_token); ++ token_count = byte_code[1]; ++ TRACE("Token count: %u.\n", token_count); ++ ++ if (token_count < 6 || count < token_count) ++ { ++ WARN("Invalid token count %u (word count %zu).\n", token_count, count); ++ vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE, ++ "DXIL chunk token count %#x is invalid (word count %zu).", token_count, count); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ ++ if (byte_code[2] != TAG_DXIL) ++ WARN("Unknown magic number 0x%08x.\n", byte_code[2]); ++ ++ dxil_version = byte_code[3]; ++ if (dxil_version > 0x102) ++ WARN("Unknown DXIL version: 0x%08x.\n", dxil_version); ++ else ++ TRACE("DXIL version: 0x%08x.\n", dxil_version); ++ ++ chunk_offset = byte_code[4]; ++ if (chunk_offset < 16 || chunk_offset >= byte_code_size) ++ { ++ WARN("Invalid bitcode chunk offset %#x (data size %zu).\n", chunk_offset, byte_code_size); ++ vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_OFFSET, ++ "DXIL bitcode chunk has invalid offset %#x (data size %#zx).", chunk_offset, byte_code_size); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ chunk_size = byte_code[5]; ++ if (chunk_size > byte_code_size - chunk_offset) ++ { ++ WARN("Invalid bitcode chunk size %#x (data size %zu, chunk offset %#x).\n", ++ chunk_size, byte_code_size, chunk_offset); ++ vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_CHUNK_SIZE, ++ "DXIL bitcode chunk has invalid size %#x (data size %#zx, chunk offset %#x).", ++ chunk_size, byte_code_size, chunk_offset); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ ++ sm6->start = (const uint32_t *)((const char*)&byte_code[2] + chunk_offset); ++ if ((magic = sm6->start[0]) != BITCODE_MAGIC) ++ { ++ WARN("Unknown magic number 0x%08x.\n", magic); ++ vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_MAGIC_NUMBER, ++ "DXIL bitcode chunk magic number 0x%08x is not the expected 0x%08x.", magic, BITCODE_MAGIC); ++ } ++ ++ sm6->end = &sm6->start[(chunk_size + sizeof(*sm6->start) - 1) / sizeof(*sm6->start)]; ++ ++ if ((version.type = version_token >> 16) >= VKD3D_SHADER_TYPE_COUNT) ++ { ++ FIXME("Unknown shader type %#x.\n", version.type); ++ vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_UNKNOWN_SHADER_TYPE, ++ "Unknown shader type %#x.", version.type); ++ } ++ ++ version.major = VKD3D_SM6_VERSION_MAJOR(version_token); ++ version.minor = VKD3D_SM6_VERSION_MINOR(version_token); ++ ++ if ((abbr = sm6->start[1] & 3) != ENTER_SUBBLOCK) ++ { ++ WARN("Initial block abbreviation %u is not ENTER_SUBBLOCK.\n", abbr); ++ vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE, ++ "DXIL bitcode chunk has invalid initial block abbreviation %u.", abbr); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ ++ /* Estimate instruction count to avoid reallocation in most shaders. */ ++ count = max(token_count, 400) - 400; ++ vkd3d_shader_parser_init(&sm6->p, message_context, source_name, &version, &sm6_parser_ops, ++ (count + (count >> 2)) / 2u + 10); ++ sm6->ptr = &sm6->start[1]; ++ sm6->bitpos = 2; ++ ++ block = &sm6->root_block; ++ if ((ret = dxil_block_init(block, NULL, 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 bitcode chunk."); ++ else if (ret == VKD3D_ERROR_INVALID_SHADER) ++ vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_BITCODE, ++ "DXIL bitcode chunk has invalid bitcode."); ++ else ++ vkd3d_unreachable(); ++ return ret; ++ } ++ ++ dxil_global_abbrevs_cleanup(sm6->abbrevs, sm6->abbrev_count); ++ sm6->abbrevs = NULL; ++ sm6->abbrev_count = 0; ++ ++ length = sm6->ptr - sm6->start - block->start; ++ if (length != block->length) ++ { ++ WARN("Invalid block length %zu; expected %u.\n", length, block->length); ++ vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_BLOCK_LENGTH, ++ "Root block ends with length %zu but indicated length is %u.", length, block->length); ++ } ++ if (sm6->ptr != sm6->end) ++ { ++ size_t expected_length = sm6->end - sm6->start; ++ length = sm6->ptr - sm6->start; ++ WARN("Invalid module length %zu; expected %zu.\n", length, expected_length); ++ vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_INVALID_MODULE_LENGTH, ++ "Module ends with length %zu but indicated length is %zu.", 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; ++ } ++ ++ 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; ++ } ++ ++ 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; ++ } ++ ++ if (sm6_parser_compute_max_value_count(sm6, &sm6->root_block, 0) == SIZE_MAX) ++ { ++ WARN("Value array count overflowed.\n"); ++ vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE, ++ "Overflow occurred in the DXIL module value count."); ++ return VKD3D_ERROR_INVALID_SHADER; ++ } ++ 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"); ++ 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); +diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl.c b/libs/vkd3d/libs/vkd3d-shader/hlsl.c +index 4e9af15c1be..ab508502623 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/hlsl.c ++++ b/libs/vkd3d/libs/vkd3d-shader/hlsl.c +@@ -430,6 +430,51 @@ struct hlsl_type *hlsl_type_get_component_type(struct hlsl_ctx *ctx, struct hlsl + return type; + } + ++unsigned int hlsl_type_get_component_offset(struct hlsl_ctx *ctx, struct hlsl_type *type, ++ enum hlsl_regset regset, unsigned int index) ++{ ++ struct hlsl_type *next_type; ++ unsigned int offset = 0; ++ unsigned int idx; ++ ++ while (!type_is_single_component(type)) ++ { ++ next_type = type; ++ idx = traverse_path_from_component_index(ctx, &next_type, &index); ++ ++ switch (type->class) ++ { ++ case HLSL_CLASS_SCALAR: ++ case HLSL_CLASS_VECTOR: ++ case HLSL_CLASS_MATRIX: ++ if (regset == HLSL_REGSET_NUMERIC) ++ offset += idx; ++ break; ++ ++ case HLSL_CLASS_STRUCT: ++ offset += type->e.record.fields[idx].reg_offset[regset]; ++ break; ++ ++ case HLSL_CLASS_ARRAY: ++ if (regset == HLSL_REGSET_NUMERIC) ++ offset += idx * align(type->e.array.type->reg_size[regset], 4); ++ else ++ offset += idx * type->e.array.type->reg_size[regset]; ++ break; ++ ++ case HLSL_CLASS_OBJECT: ++ assert(idx == 0); ++ break; ++ ++ default: ++ vkd3d_unreachable(); ++ } ++ type = next_type; ++ } ++ ++ return offset; ++} ++ + static bool init_deref(struct hlsl_ctx *ctx, struct hlsl_deref *deref, struct hlsl_ir_var *var, + unsigned int path_len) + { +@@ -524,7 +569,9 @@ struct hlsl_type *hlsl_deref_get_type(struct hlsl_ctx *ctx, const struct hlsl_de + unsigned int i; + + assert(deref); +- assert(!deref->offset.node); ++ ++ if (deref->offset.node) ++ return deref->data_type; + + type = deref->var->data_type; + for (i = 0; i < deref->path_len; ++i) +@@ -626,6 +673,7 @@ struct hlsl_type *hlsl_new_array_type(struct hlsl_ctx *ctx, struct hlsl_type *ba + type->e.array.type = basic_type; + type->dimx = basic_type->dimx; + type->dimy = basic_type->dimy; ++ type->sampler_dim = basic_type->sampler_dim; + hlsl_type_calculate_reg_size(ctx, type); + + list_add_tail(&ctx->types, &type->entry); +@@ -992,20 +1040,31 @@ struct hlsl_ir_var *hlsl_new_synthetic_var(struct hlsl_ctx *ctx, const char *tem + struct vkd3d_string_buffer *string; + struct hlsl_ir_var *var; + static LONG counter; +- const char *name; + + if (!(string = hlsl_get_string_buffer(ctx))) + return NULL; + vkd3d_string_buffer_printf(string, "<%s-%u>", template, InterlockedIncrement(&counter)); +- if (!(name = hlsl_strdup(ctx, string->buffer))) +- { +- hlsl_release_string_buffer(ctx, string); +- return NULL; +- } +- var = hlsl_new_var(ctx, name, type, loc, NULL, 0, NULL); ++ var = hlsl_new_synthetic_var_named(ctx, string->buffer, type, loc, true); + hlsl_release_string_buffer(ctx, string); ++ return var; ++} ++ ++struct hlsl_ir_var *hlsl_new_synthetic_var_named(struct hlsl_ctx *ctx, const char *name, ++ struct hlsl_type *type, const struct vkd3d_shader_location *loc, bool dummy_scope) ++{ ++ struct hlsl_ir_var *var; ++ const char *name_copy; ++ ++ if (!(name_copy = hlsl_strdup(ctx, name))) ++ return NULL; ++ var = hlsl_new_var(ctx, name_copy, type, loc, NULL, 0, NULL); + if (var) +- list_add_tail(&ctx->dummy_scope->vars, &var->scope_entry); ++ { ++ if (dummy_scope) ++ list_add_tail(&ctx->dummy_scope->vars, &var->scope_entry); ++ else ++ list_add_tail(&ctx->globals->vars, &var->scope_entry); ++ } + return var; + } + +@@ -2066,6 +2125,31 @@ struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const stru + } + } + ++struct vkd3d_string_buffer *hlsl_component_to_string(struct hlsl_ctx *ctx, const struct hlsl_ir_var *var, ++ unsigned int index) ++{ ++ struct hlsl_type *type = var->data_type, *current_type; ++ struct vkd3d_string_buffer *buffer; ++ unsigned int element_index; ++ ++ if (!(buffer = hlsl_get_string_buffer(ctx))) ++ return NULL; ++ ++ vkd3d_string_buffer_printf(buffer, "%s", var->name); ++ ++ while (!type_is_single_component(type)) ++ { ++ current_type = type; ++ element_index = traverse_path_from_component_index(ctx, &type, &index); ++ if (current_type->class == HLSL_CLASS_STRUCT) ++ vkd3d_string_buffer_printf(buffer, ".%s", current_type->e.record.fields[element_index].name); ++ else ++ vkd3d_string_buffer_printf(buffer, "[%u]", element_index); ++ } ++ ++ return buffer; ++} ++ + const char *debug_hlsl_type(struct hlsl_ctx *ctx, const struct hlsl_type *type) + { + struct vkd3d_string_buffer *string; +diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl.h b/libs/vkd3d/libs/vkd3d-shader/hlsl.h +index 17ac36a57c6..1a4b995abbf 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/hlsl.h ++++ b/libs/vkd3d/libs/vkd3d-shader/hlsl.h +@@ -422,6 +422,7 @@ struct hlsl_ir_var + uint32_t is_output_semantic : 1; + uint32_t is_uniform : 1; + uint32_t is_param : 1; ++ uint32_t is_separated_resource : 1; + }; + + /* Sized array of variables representing a function's parameters. */ +@@ -607,9 +608,11 @@ struct hlsl_deref + * components, within the pertaining regset), from the start of the variable, of the part + * referenced. + * The path is lowered to this single offset -- whose value may vary between SM1 and SM4 -- +- * before writing the bytecode. */ ++ * before writing the bytecode. ++ * Since the type information cannot longer be retrieved from the offset alone, the type is ++ * stored in the data_type field. */ + struct hlsl_src offset; +- enum hlsl_regset offset_regset; ++ struct hlsl_type *data_type; + }; + + struct hlsl_ir_load +@@ -1066,6 +1069,8 @@ const char *debug_hlsl_writemask(unsigned int writemask); + const char *debug_hlsl_swizzle(unsigned int swizzle, unsigned int count); + + struct vkd3d_string_buffer *hlsl_type_to_string(struct hlsl_ctx *ctx, const struct hlsl_type *type); ++struct vkd3d_string_buffer *hlsl_component_to_string(struct hlsl_ctx *ctx, const struct hlsl_ir_var *var, ++ unsigned int index); + struct vkd3d_string_buffer *hlsl_modifiers_to_string(struct hlsl_ctx *ctx, unsigned int modifiers); + const char *hlsl_node_type_to_string(enum hlsl_ir_node_type type); + +@@ -1169,6 +1174,8 @@ struct hlsl_ir_node *hlsl_new_swizzle(struct hlsl_ctx *ctx, DWORD s, unsigned in + struct hlsl_ir_node *val, const struct vkd3d_shader_location *loc); + struct hlsl_ir_var *hlsl_new_synthetic_var(struct hlsl_ctx *ctx, const char *template, + struct hlsl_type *type, const struct vkd3d_shader_location *loc); ++struct hlsl_ir_var *hlsl_new_synthetic_var_named(struct hlsl_ctx *ctx, const char *name, ++ struct hlsl_type *type, const struct vkd3d_shader_location *loc, bool dummy_scope); + struct hlsl_type *hlsl_new_texture_type(struct hlsl_ctx *ctx, enum hlsl_sampler_dim dim, struct hlsl_type *format, + unsigned int sample_count); + struct hlsl_type *hlsl_new_uav_type(struct hlsl_ctx *ctx, enum hlsl_sampler_dim dim, struct hlsl_type *format); +@@ -1200,6 +1207,8 @@ unsigned int hlsl_type_component_count(const struct hlsl_type *type); + unsigned int hlsl_type_get_array_element_reg_size(const struct hlsl_type *type, enum hlsl_regset regset); + struct hlsl_type *hlsl_type_get_component_type(struct hlsl_ctx *ctx, struct hlsl_type *type, + unsigned int index); ++unsigned int hlsl_type_get_component_offset(struct hlsl_ctx *ctx, struct hlsl_type *type, ++ enum hlsl_regset regset, unsigned int index); + bool hlsl_type_is_row_major(const struct hlsl_type *type); + unsigned int hlsl_type_minor_size(const struct hlsl_type *type); + unsigned int hlsl_type_major_size(const struct hlsl_type *type); +diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl.y b/libs/vkd3d/libs/vkd3d-shader/hlsl.y +index 42fa2129e40..6bf87f8f916 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/hlsl.y ++++ b/libs/vkd3d/libs/vkd3d-shader/hlsl.y +@@ -133,11 +133,6 @@ static void yyerror(YYLTYPE *loc, void *scanner, struct hlsl_ctx *ctx, const cha + hlsl_error(ctx, loc, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "%s", s); + } + +-static struct hlsl_ir_node *node_from_list(struct list *list) +-{ +- return LIST_ENTRY(list_tail(list), struct hlsl_ir_node, entry); +-} +- + static struct hlsl_ir_node *node_from_block(struct hlsl_block *block) + { + return LIST_ENTRY(list_tail(&block->instrs), struct hlsl_ir_node, entry); +@@ -719,7 +714,7 @@ struct hlsl_ir_node *hlsl_add_load_component(struct hlsl_ctx *ctx, struct list * + return load; + } + +-static bool add_record_access(struct hlsl_ctx *ctx, struct list *instrs, struct hlsl_ir_node *record, ++static bool add_record_access(struct hlsl_ctx *ctx, struct hlsl_block *block, struct hlsl_ir_node *record, + unsigned int idx, const struct vkd3d_shader_location *loc) + { + struct hlsl_ir_node *index, *c; +@@ -728,11 +723,11 @@ static bool add_record_access(struct hlsl_ctx *ctx, struct list *instrs, struct + + if (!(c = hlsl_new_uint_constant(ctx, idx, loc))) + return false; +- list_add_tail(instrs, &c->entry); ++ hlsl_block_add_instr(block, c); + + if (!(index = hlsl_new_index(ctx, record, c, loc))) + return false; +- list_add_tail(instrs, &index->entry); ++ hlsl_block_add_instr(block, index); + + return true; + } +@@ -1170,7 +1165,7 @@ static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, str + return 0; + hlsl_block_add_block(&expr, block); + +- if (!add_implicit_conversion(ctx, &expr.instrs, node_from_list(&expr.instrs), ++ if (!add_implicit_conversion(ctx, &expr.instrs, node_from_block(&expr), + hlsl_get_scalar_type(ctx, HLSL_TYPE_UINT), loc)) + { + hlsl_block_cleanup(&expr); +@@ -1183,7 +1178,7 @@ static unsigned int evaluate_static_expression_as_uint(struct hlsl_ctx *ctx, str + progress |= hlsl_copy_propagation_execute(ctx, &expr); + } while (progress); + +- node = node_from_list(&expr.instrs); ++ node = node_from_block(&expr); + if (node->type == HLSL_IR_CONSTANT) + { + constant = hlsl_ir_constant(node); +@@ -1417,15 +1412,15 @@ static struct hlsl_ir_node *add_unary_arithmetic_expr(struct hlsl_ctx *ctx, stru + return add_expr(ctx, block_to_list(block), op, args, arg->data_type, loc); + } + +-static struct hlsl_ir_node *add_unary_bitwise_expr(struct hlsl_ctx *ctx, struct list *instrs, ++static struct hlsl_ir_node *add_unary_bitwise_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, + enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, const struct vkd3d_shader_location *loc) + { + check_integer_type(ctx, arg); + +- return add_unary_arithmetic_expr(ctx, list_to_block(instrs), op, arg, loc); ++ return add_unary_arithmetic_expr(ctx, block, op, arg, loc); + } + +-static struct hlsl_ir_node *add_unary_logical_expr(struct hlsl_ctx *ctx, struct list *instrs, ++static struct hlsl_ir_node *add_unary_logical_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, + enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg, const struct vkd3d_shader_location *loc) + { + struct hlsl_ir_node *args[HLSL_MAX_OPERANDS] = {0}; +@@ -1434,10 +1429,10 @@ static struct hlsl_ir_node *add_unary_logical_expr(struct hlsl_ctx *ctx, struct + bool_type = hlsl_get_numeric_type(ctx, arg->data_type->class, HLSL_TYPE_BOOL, + arg->data_type->dimx, arg->data_type->dimy); + +- if (!(args[0] = add_implicit_conversion(ctx, instrs, arg, bool_type, loc))) ++ if (!(args[0] = add_implicit_conversion(ctx, block_to_list(block), arg, bool_type, loc))) + return NULL; + +- return add_expr(ctx, instrs, op, args, bool_type, loc); ++ return add_expr(ctx, block_to_list(block), op, args, bool_type, loc); + } + + static struct hlsl_type *get_common_numeric_type(struct hlsl_ctx *ctx, const struct hlsl_ir_node *arg1, +@@ -1471,17 +1466,17 @@ static struct hlsl_ir_node *add_binary_arithmetic_expr(struct hlsl_ctx *ctx, str + return add_expr(ctx, block_to_list(block), op, args, common_type, loc); + } + +-static struct hlsl_ir_node *add_binary_bitwise_expr(struct hlsl_ctx *ctx, struct list *instrs, ++static struct hlsl_ir_node *add_binary_bitwise_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, + enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, + const struct vkd3d_shader_location *loc) + { + check_integer_type(ctx, arg1); + check_integer_type(ctx, arg2); + +- return add_binary_arithmetic_expr(ctx, list_to_block(instrs), op, arg1, arg2, loc); ++ return add_binary_arithmetic_expr(ctx, block, op, arg1, arg2, loc); + } + +-static struct hlsl_ir_node *add_binary_comparison_expr(struct hlsl_ctx *ctx, struct list *instrs, ++static struct hlsl_ir_node *add_binary_comparison_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, + enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, + const struct vkd3d_shader_location *loc) + { +@@ -1497,16 +1492,16 @@ static struct hlsl_ir_node *add_binary_comparison_expr(struct hlsl_ctx *ctx, str + common_type = hlsl_get_numeric_type(ctx, type, base, dimx, dimy); + return_type = hlsl_get_numeric_type(ctx, type, HLSL_TYPE_BOOL, dimx, dimy); + +- if (!(args[0] = add_implicit_conversion(ctx, instrs, arg1, common_type, loc))) ++ if (!(args[0] = add_implicit_conversion(ctx, block_to_list(block), arg1, common_type, loc))) + return NULL; + +- if (!(args[1] = add_implicit_conversion(ctx, instrs, arg2, common_type, loc))) ++ if (!(args[1] = add_implicit_conversion(ctx, block_to_list(block), arg2, common_type, loc))) + return NULL; + +- return add_expr(ctx, instrs, op, args, return_type, loc); ++ return add_expr(ctx, block_to_list(block), op, args, return_type, loc); + } + +-static struct hlsl_ir_node *add_binary_logical_expr(struct hlsl_ctx *ctx, struct list *instrs, ++static struct hlsl_ir_node *add_binary_logical_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, + enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, + const struct vkd3d_shader_location *loc) + { +@@ -1520,16 +1515,16 @@ static struct hlsl_ir_node *add_binary_logical_expr(struct hlsl_ctx *ctx, struct + + common_type = hlsl_get_numeric_type(ctx, type, HLSL_TYPE_BOOL, dimx, dimy); + +- if (!(args[0] = add_implicit_conversion(ctx, instrs, arg1, common_type, loc))) ++ if (!(args[0] = add_implicit_conversion(ctx, block_to_list(block), arg1, common_type, loc))) + return NULL; + +- if (!(args[1] = add_implicit_conversion(ctx, instrs, arg2, common_type, loc))) ++ if (!(args[1] = add_implicit_conversion(ctx, block_to_list(block), arg2, common_type, loc))) + return NULL; + +- return add_expr(ctx, instrs, op, args, common_type, loc); ++ return add_expr(ctx, block_to_list(block), op, args, common_type, loc); + } + +-static struct hlsl_ir_node *add_binary_shift_expr(struct hlsl_ctx *ctx, struct list *instrs, ++static struct hlsl_ir_node *add_binary_shift_expr(struct hlsl_ctx *ctx, struct hlsl_block *block, + enum hlsl_ir_expr_op op, struct hlsl_ir_node *arg1, struct hlsl_ir_node *arg2, + const struct vkd3d_shader_location *loc) + { +@@ -1551,13 +1546,13 @@ static struct hlsl_ir_node *add_binary_shift_expr(struct hlsl_ctx *ctx, struct l + return_type = hlsl_get_numeric_type(ctx, type, base, dimx, dimy); + integer_type = hlsl_get_numeric_type(ctx, type, HLSL_TYPE_INT, dimx, dimy); + +- if (!(args[0] = add_implicit_conversion(ctx, instrs, arg1, return_type, loc))) ++ if (!(args[0] = add_implicit_conversion(ctx, block_to_list(block), arg1, return_type, loc))) + return NULL; + +- if (!(args[1] = add_implicit_conversion(ctx, instrs, arg2, integer_type, loc))) ++ if (!(args[1] = add_implicit_conversion(ctx, block_to_list(block), arg2, integer_type, loc))) + return NULL; + +- return add_expr(ctx, instrs, op, args, return_type, loc); ++ return add_expr(ctx, block_to_list(block), op, args, return_type, loc); + } + + static struct hlsl_ir_node *add_binary_dot_expr(struct hlsl_ctx *ctx, struct hlsl_block *instrs, +@@ -1613,13 +1608,13 @@ static struct hlsl_ir_node *add_binary_dot_expr(struct hlsl_ctx *ctx, struct hls + return add_expr(ctx, block_to_list(instrs), op, args, ret_type, loc); + } + +-static struct list *add_binary_expr_merge(struct hlsl_ctx *ctx, struct list *list1, struct list *list2, +- enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc) ++static struct hlsl_block *add_binary_expr_merge(struct hlsl_ctx *ctx, struct hlsl_block *block1, ++ struct hlsl_block *block2, enum hlsl_ir_expr_op op, const struct vkd3d_shader_location *loc) + { +- struct hlsl_ir_node *arg1 = node_from_list(list1), *arg2 = node_from_list(list2); ++ struct hlsl_ir_node *arg1 = node_from_block(block1), *arg2 = node_from_block(block2); + +- list_move_tail(list1, list2); +- vkd3d_free(list2); ++ hlsl_block_add_block(block1, block2); ++ destroy_block(block2); + + switch (op) + { +@@ -1627,37 +1622,37 @@ static struct list *add_binary_expr_merge(struct hlsl_ctx *ctx, struct list *lis + case HLSL_OP2_DIV: + case HLSL_OP2_MOD: + case HLSL_OP2_MUL: +- add_binary_arithmetic_expr(ctx, list_to_block(list1), op, arg1, arg2, loc); ++ add_binary_arithmetic_expr(ctx, block1, op, arg1, arg2, loc); + break; + + case HLSL_OP2_BIT_AND: + case HLSL_OP2_BIT_OR: + case HLSL_OP2_BIT_XOR: +- add_binary_bitwise_expr(ctx, list1, op, arg1, arg2, loc); ++ add_binary_bitwise_expr(ctx, block1, op, arg1, arg2, loc); + break; + + case HLSL_OP2_LESS: + case HLSL_OP2_GEQUAL: + case HLSL_OP2_EQUAL: + case HLSL_OP2_NEQUAL: +- add_binary_comparison_expr(ctx, list1, op, arg1, arg2, loc); ++ add_binary_comparison_expr(ctx, block1, op, arg1, arg2, loc); + break; + + case HLSL_OP2_LOGIC_AND: + case HLSL_OP2_LOGIC_OR: +- add_binary_logical_expr(ctx, list1, op, arg1, arg2, loc); ++ add_binary_logical_expr(ctx, block1, op, arg1, arg2, loc); + break; + + case HLSL_OP2_LSHIFT: + case HLSL_OP2_RSHIFT: +- add_binary_shift_expr(ctx, list1, op, arg1, arg2, loc); ++ add_binary_shift_expr(ctx, block1, op, arg1, arg2, loc); + break; + + default: + vkd3d_unreachable(); + } + +- return list1; ++ return block1; + } + + static enum hlsl_ir_expr_op op_from_assignment(enum parse_assign_op op) +@@ -1882,10 +1877,10 @@ static struct hlsl_ir_node *add_assignment(struct hlsl_ctx *ctx, struct list *in + return copy; + } + +-static bool add_increment(struct hlsl_ctx *ctx, struct list *instrs, bool decrement, bool post, ++static bool add_increment(struct hlsl_ctx *ctx, struct hlsl_block *block, bool decrement, bool post, + const struct vkd3d_shader_location *loc) + { +- struct hlsl_ir_node *lhs = node_from_list(instrs); ++ struct hlsl_ir_node *lhs = node_from_block(block); + struct hlsl_ir_node *one; + + if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST) +@@ -1894,9 +1889,9 @@ static bool add_increment(struct hlsl_ctx *ctx, struct list *instrs, bool decrem + + if (!(one = hlsl_new_int_constant(ctx, 1, loc))) + return false; +- list_add_tail(instrs, &one->entry); ++ hlsl_block_add_instr(block, one); + +- if (!add_assignment(ctx, instrs, lhs, decrement ? ASSIGN_OP_SUB : ASSIGN_OP_ADD, one)) ++ if (!add_assignment(ctx, block_to_list(block), lhs, decrement ? ASSIGN_OP_SUB : ASSIGN_OP_ADD, one)) + return false; + + if (post) +@@ -1905,7 +1900,7 @@ static bool add_increment(struct hlsl_ctx *ctx, struct list *instrs, bool decrem + + if (!(copy = hlsl_new_copy(ctx, lhs))) + return false; +- list_add_tail(instrs, ©->entry); ++ hlsl_block_add_instr(block, copy); + + /* Post increment/decrement expressions are considered const. */ + if (!(copy->data_type = hlsl_type_clone(ctx, copy->data_type, 0, HLSL_MODIFIER_CONST))) +@@ -2495,7 +2490,7 @@ static bool intrinsic_all(struct hlsl_ctx *ctx, + return false; + } + +- return !!add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_NEQUAL, mul, zero, loc); ++ return !!add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_NEQUAL, mul, zero, loc); + } + + static bool intrinsic_any(struct hlsl_ctx *ctx, +@@ -2519,7 +2514,7 @@ static bool intrinsic_any(struct hlsl_ctx *ctx, + if (!(dot = add_binary_dot_expr(ctx, params->instrs, arg, arg, loc))) + return false; + +- return !!add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_NEQUAL, dot, zero, loc); ++ return !!add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_NEQUAL, dot, zero, loc); + } + else if (arg->data_type->base_type == HLSL_TYPE_BOOL) + { +@@ -2535,7 +2530,7 @@ static bool intrinsic_any(struct hlsl_ctx *ctx, + if (!(load = hlsl_add_load_component(ctx, block_to_list(params->instrs), arg, i, loc))) + return false; + +- if (!(or = add_binary_bitwise_expr(ctx, block_to_list(params->instrs), HLSL_OP2_BIT_OR, or, load, loc))) ++ if (!(or = add_binary_bitwise_expr(ctx, params->instrs, HLSL_OP2_BIT_OR, or, load, loc))) + return false; + } + +@@ -2881,7 +2876,7 @@ static bool intrinsic_fmod(struct hlsl_ctx *ctx, const struct parse_initializer + if (!(neg_frac = add_unary_arithmetic_expr(ctx, params->instrs, HLSL_OP1_NEG, frac, loc))) + return false; + +- if (!(ge = add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_GEQUAL, div, zero, loc))) ++ if (!(ge = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_GEQUAL, div, zero, loc))) + return false; + + if (!(select = hlsl_add_conditional(ctx, block_to_list(params->instrs), ge, frac, neg_frac))) +@@ -3035,13 +3030,13 @@ static bool intrinsic_lit(struct hlsl_ctx *ctx, + hlsl_block_add_block(params->instrs, &block); + + /* Specular component. */ +- if (!(n_h_neg = add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_LESS, n_h, zero, loc))) ++ if (!(n_h_neg = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS, n_h, zero, loc))) + return false; + +- if (!(n_l_neg = add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_LESS, n_l, zero, loc))) ++ if (!(n_l_neg = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS, n_l, zero, loc))) + return false; + +- if (!(specular_or = add_binary_logical_expr(ctx, block_to_list(params->instrs), HLSL_OP2_LOGIC_OR, n_l_neg, n_h_neg, loc))) ++ if (!(specular_or = add_binary_logical_expr(ctx, params->instrs, HLSL_OP2_LOGIC_OR, n_l_neg, n_h_neg, loc))) + return false; + + if (!(specular_pow = add_pow_expr(ctx, params->instrs, n_h, m, loc))) +@@ -3330,7 +3325,7 @@ static bool intrinsic_sign(struct hlsl_ctx *ctx, + + /* Check if 0 < arg, cast bool to int */ + +- if (!(lt = add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_LESS, zero, arg, loc))) ++ if (!(lt = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS, zero, arg, loc))) + return false; + + if (!(op1 = add_implicit_conversion(ctx, block_to_list(params->instrs), lt, int_type, loc))) +@@ -3338,7 +3333,7 @@ static bool intrinsic_sign(struct hlsl_ctx *ctx, + + /* Check if arg < 0, cast bool to int and invert (meaning true is -1) */ + +- if (!(lt = add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_LESS, arg, zero, loc))) ++ if (!(lt = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_LESS, arg, zero, loc))) + return false; + + if (!(op2 = add_implicit_conversion(ctx, block_to_list(params->instrs), lt, int_type, loc))) +@@ -3440,7 +3435,7 @@ static bool intrinsic_step(struct hlsl_ctx *ctx, + if (!elementwise_intrinsic_float_convert_args(ctx, params, loc)) + return false; + +- if (!(ge = add_binary_comparison_expr(ctx, block_to_list(params->instrs), HLSL_OP2_GEQUAL, ++ if (!(ge = add_binary_comparison_expr(ctx, params->instrs, HLSL_OP2_GEQUAL, + params->args[1], params->args[0], loc))) + return false; + +@@ -3824,7 +3819,7 @@ fail: + return NULL; + } + +-static struct list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type, ++static struct hlsl_block *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type, + struct parse_initializer *params, const struct vkd3d_shader_location *loc) + { + struct hlsl_ir_load *load; +@@ -3857,7 +3852,7 @@ static struct list *add_constructor(struct hlsl_ctx *ctx, struct hlsl_type *type + hlsl_block_add_instr(params->instrs, &load->node); + + vkd3d_free(params->args); +- return block_to_list(params->instrs); ++ return params->instrs; + } + + static unsigned int hlsl_offset_dim_count(enum hlsl_sampler_dim dim) +@@ -4562,26 +4557,11 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type + %token C_INTEGER + %token PRE_LINE + +-%type add_expr +-%type assignment_expr +-%type bitand_expr +-%type bitor_expr +-%type bitxor_expr +-%type conditional_expr + %type declaration + %type declaration_statement +-%type equality_expr +-%type initializer_expr +-%type logicand_expr +-%type logicor_expr +-%type mul_expr +-%type postfix_expr + %type primary_expr +-%type relational_expr +-%type shift_expr + %type struct_declaration_without_vars + %type type_specs +-%type unary_expr + %type variables_def + %type variables_def_typed + +@@ -4599,15 +4579,30 @@ static void validate_texture_format_type(struct hlsl_ctx *ctx, struct hlsl_type + %type attribute_list + %type attribute_list_optional + ++%type add_expr ++%type assignment_expr ++%type bitand_expr ++%type bitor_expr ++%type bitxor_expr + %type compound_statement ++%type conditional_expr ++%type equality_expr + %type expr + %type expr_optional + %type expr_statement ++%type initializer_expr + %type jump_statement ++%type logicand_expr ++%type logicor_expr + %type loop_statement ++%type mul_expr ++%type postfix_expr ++%type relational_expr ++%type shift_expr + %type selection_statement + %type statement + %type statement_list ++%type unary_expr + + %type boolean + +@@ -5433,7 +5428,7 @@ type_no_void: + struct hlsl_block block; + + hlsl_block_init(&block); +- list_move_tail(&block.instrs, $5); ++ hlsl_block_add_block(&block, $5); + + sample_count = evaluate_static_expression_as_uint(ctx, &block, &@5); + +@@ -5824,11 +5819,11 @@ complex_initializer: + $$.args_count = 1; + if (!($$.args = hlsl_alloc(ctx, sizeof(*$$.args)))) + { +- destroy_instr_list($1); ++ destroy_block($1); + YYABORT; + } +- $$.args[0] = node_from_list($1); +- $$.instrs = list_to_block($1); ++ $$.args[0] = node_from_block($1); ++ $$.instrs = $1; + $$.braces = false; + } + | '{' complex_initializer_list '}' +@@ -5872,11 +5867,11 @@ initializer_expr_list: + $$.args_count = 1; + if (!($$.args = hlsl_alloc(ctx, sizeof(*$$.args)))) + { +- destroy_instr_list($1); ++ destroy_block($1); + YYABORT; + } +- $$.args[0] = node_from_list($1); +- $$.instrs = list_to_block($1); ++ $$.args[0] = node_from_block($1); ++ $$.instrs = $1; + $$.braces = false; + } + | initializer_expr_list ',' initializer_expr +@@ -5887,13 +5882,13 @@ initializer_expr_list: + if (!(new_args = hlsl_realloc(ctx, $$.args, ($$.args_count + 1) * sizeof(*$$.args)))) + { + free_parse_initializer(&$$); +- destroy_instr_list($3); ++ destroy_block($3); + YYABORT; + } + $$.args = new_args; +- $$.args[$$.args_count++] = node_from_list($3); +- list_move_tail(&$$.instrs->instrs, $3); +- vkd3d_free($3); ++ $$.args[$$.args_count++] = node_from_block($3); ++ hlsl_block_add_block($$.instrs, $3); ++ destroy_block($3); + } + + boolean: +@@ -6123,11 +6118,14 @@ primary_expr: + + postfix_expr: + primary_expr ++ { ++ $$ = list_to_block($1); ++ } + | postfix_expr OP_INC + { + if (!add_increment(ctx, $1, false, true, &@2)) + { +- destroy_instr_list($1); ++ destroy_block($1); + YYABORT; + } + $$ = $1; +@@ -6136,14 +6134,14 @@ postfix_expr: + { + if (!add_increment(ctx, $1, true, true, &@2)) + { +- destroy_instr_list($1); ++ destroy_block($1); + YYABORT; + } + $$ = $1; + } + | postfix_expr '.' any_identifier + { +- struct hlsl_ir_node *node = node_from_list($1); ++ struct hlsl_ir_node *node = node_from_block($1); + + if (node->data_type->class == HLSL_CLASS_STRUCT) + { +@@ -6171,7 +6169,7 @@ postfix_expr: + hlsl_error(ctx, &@3, VKD3D_SHADER_ERROR_HLSL_INVALID_SYNTAX, "Invalid swizzle \"%s\".", $3); + YYABORT; + } +- list_add_tail($1, &swizzle->entry); ++ hlsl_block_add_instr($1, swizzle); + $$ = $1; + } + else +@@ -6182,17 +6180,17 @@ postfix_expr: + } + | postfix_expr '[' expr ']' + { +- struct hlsl_ir_node *array = node_from_list($1), *index = node_from_block($3); ++ struct hlsl_ir_node *array = node_from_block($1), *index = node_from_block($3); + +- list_move_head($1, &$3->instrs); +- destroy_block($3); ++ hlsl_block_add_block($3, $1); ++ destroy_block($1); + +- if (!add_array_access(ctx, $1, array, index, &@2)) ++ if (!add_array_access(ctx, block_to_list($3), array, index, &@2)) + { +- destroy_instr_list($1); ++ destroy_block($3); + YYABORT; + } +- $$ = $1; ++ $$ = $3; + } + + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ +@@ -6233,14 +6231,14 @@ postfix_expr: + } + | postfix_expr '.' any_identifier '(' func_arguments ')' + { +- struct hlsl_ir_node *object = node_from_list($1); ++ struct hlsl_ir_node *object = node_from_block($1); + +- list_move_tail($1, &$5.instrs->instrs); ++ hlsl_block_add_block($1, $5.instrs); + vkd3d_free($5.instrs); + +- if (!add_method_call(ctx, $1, object, $3, &$5, &@3)) ++ if (!add_method_call(ctx, block_to_list($1), object, $3, &$5, &@3)) + { +- hlsl_free_instr_list($1); ++ destroy_block($1); + vkd3d_free($5.args); + YYABORT; + } +@@ -6254,7 +6252,7 @@ unary_expr: + { + if (!add_increment(ctx, $2, false, false, &@1)) + { +- destroy_instr_list($2); ++ destroy_block($2); + YYABORT; + } + $$ = $2; +@@ -6263,7 +6261,7 @@ unary_expr: + { + if (!add_increment(ctx, $2, true, false, &@1)) + { +- destroy_instr_list($2); ++ destroy_block($2); + YYABORT; + } + $$ = $2; +@@ -6274,23 +6272,23 @@ unary_expr: + } + | '-' unary_expr + { +- add_unary_arithmetic_expr(ctx, list_to_block($2), HLSL_OP1_NEG, node_from_list($2), &@1); ++ add_unary_arithmetic_expr(ctx, $2, HLSL_OP1_NEG, node_from_block($2), &@1); + $$ = $2; + } + | '~' unary_expr + { +- add_unary_bitwise_expr(ctx, $2, HLSL_OP1_BIT_NOT, node_from_list($2), &@1); ++ add_unary_bitwise_expr(ctx, $2, HLSL_OP1_BIT_NOT, node_from_block($2), &@1); + $$ = $2; + } + | '!' unary_expr + { +- add_unary_logical_expr(ctx, $2, HLSL_OP1_LOGIC_NOT, node_from_list($2), &@1); ++ add_unary_logical_expr(ctx, $2, HLSL_OP1_LOGIC_NOT, node_from_block($2), &@1); + $$ = $2; + } + /* var_modifiers is necessary to avoid shift/reduce conflicts. */ + | '(' var_modifiers type arrays ')' unary_expr + { +- struct hlsl_type *src_type = node_from_list($6)->data_type; ++ struct hlsl_type *src_type = node_from_block($6)->data_type; + struct hlsl_type *dst_type; + unsigned int i; + +@@ -6326,9 +6324,9 @@ unary_expr: + YYABORT; + } + +- if (!add_cast(ctx, $6, node_from_list($6), dst_type, &@3)) ++ if (!add_cast(ctx, block_to_list($6), node_from_block($6), dst_type, &@3)) + { +- hlsl_free_instr_list($6); ++ destroy_block($6); + YYABORT; + } + $$ = $6; +@@ -6359,7 +6357,7 @@ add_expr: + { + struct hlsl_ir_node *neg; + +- if (!(neg = add_unary_arithmetic_expr(ctx, list_to_block($3), HLSL_OP1_NEG, node_from_list($3), &@2))) ++ if (!(neg = add_unary_arithmetic_expr(ctx, $3, HLSL_OP1_NEG, node_from_block($3), &@2))) + YYABORT; + $$ = add_binary_expr_merge(ctx, $1, $3, HLSL_OP2_ADD, &@2); + } +@@ -6444,24 +6442,26 @@ conditional_expr: + logicor_expr + | logicor_expr '?' expr ':' assignment_expr + { +- struct hlsl_ir_node *cond = node_from_list($1), *first = node_from_block($3), *second = node_from_list($5); ++ struct hlsl_ir_node *cond = node_from_block($1); ++ struct hlsl_ir_node *first = node_from_block($3); ++ struct hlsl_ir_node *second = node_from_block($5); + struct hlsl_type *common_type; + +- list_move_tail($1, &$3->instrs); +- list_move_tail($1, $5); ++ hlsl_block_add_block($1, $3); ++ hlsl_block_add_block($1, $5); + destroy_block($3); +- vkd3d_free($5); ++ destroy_block($5); + + if (!(common_type = get_common_numeric_type(ctx, first, second, &@3))) + YYABORT; + +- if (!(first = add_implicit_conversion(ctx, $1, first, common_type, &@3))) ++ if (!(first = add_implicit_conversion(ctx, block_to_list($1), first, common_type, &@3))) + YYABORT; + +- if (!(second = add_implicit_conversion(ctx, $1, second, common_type, &@5))) ++ if (!(second = add_implicit_conversion(ctx, block_to_list($1), second, common_type, &@5))) + YYABORT; + +- if (!hlsl_add_conditional(ctx, $1, cond, first, second)) ++ if (!hlsl_add_conditional(ctx, block_to_list($1), cond, first, second)) + YYABORT; + $$ = $1; + } +@@ -6471,16 +6471,16 @@ assignment_expr: + conditional_expr + | unary_expr assign_op assignment_expr + { +- struct hlsl_ir_node *lhs = node_from_list($1), *rhs = node_from_list($3); ++ struct hlsl_ir_node *lhs = node_from_block($1), *rhs = node_from_block($3); + + if (lhs->data_type->modifiers & HLSL_MODIFIER_CONST) + { + hlsl_error(ctx, &@2, VKD3D_SHADER_ERROR_HLSL_MODIFIES_CONST, "Statement modifies a const expression."); + YYABORT; + } +- list_move_tail($3, $1); +- vkd3d_free($1); +- if (!add_assignment(ctx, $3, lhs, $2, rhs)) ++ hlsl_block_add_block($3, $1); ++ destroy_block($1); ++ if (!add_assignment(ctx, block_to_list($3), lhs, $2, rhs)) + YYABORT; + $$ = $3; + } +@@ -6533,12 +6533,9 @@ assign_op: + + expr: + assignment_expr +- { +- $$ = list_to_block($1); +- } + | expr ',' assignment_expr + { + $$ = $1; +- list_move_tail(&$$->instrs, $3); +- vkd3d_free($3); ++ hlsl_block_add_block($$, $3); ++ destroy_block($3); + } +diff --git a/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c b/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c +index 8927e291183..09a3ea4ca08 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c ++++ b/libs/vkd3d/libs/vkd3d-shader/hlsl_codegen.c +@@ -97,6 +97,7 @@ static struct hlsl_ir_node *new_offset_from_path_index(struct hlsl_ctx *ctx, str + static struct hlsl_ir_node *new_offset_instr_from_deref(struct hlsl_ctx *ctx, struct hlsl_block *block, + const struct hlsl_deref *deref, const struct vkd3d_shader_location *loc) + { ++ enum hlsl_regset regset = hlsl_type_get_regset(deref->data_type); + struct hlsl_ir_node *offset = NULL; + struct hlsl_type *type; + unsigned int i; +@@ -111,7 +112,7 @@ static struct hlsl_ir_node *new_offset_instr_from_deref(struct hlsl_ctx *ctx, st + struct hlsl_block idx_block; + + if (!(offset = new_offset_from_path_index(ctx, &idx_block, type, offset, deref->path[i].node, +- deref->offset_regset, loc))) ++ regset, loc))) + return NULL; + + hlsl_block_add_block(block, &idx_block); +@@ -126,7 +127,7 @@ static struct hlsl_ir_node *new_offset_instr_from_deref(struct hlsl_ctx *ctx, st + static bool replace_deref_path_with_offset(struct hlsl_ctx *ctx, struct hlsl_deref *deref, + struct hlsl_ir_node *instr) + { +- const struct hlsl_type *type; ++ struct hlsl_type *type; + struct hlsl_ir_node *offset; + struct hlsl_block block; + +@@ -145,7 +146,7 @@ static bool replace_deref_path_with_offset(struct hlsl_ctx *ctx, struct hlsl_der + return true; + } + +- deref->offset_regset = hlsl_type_get_regset(type); ++ deref->data_type = type; + + if (!(offset = new_offset_instr_from_deref(ctx, &block, deref, &instr->loc))) + return false; +@@ -1689,7 +1690,7 @@ static bool validate_static_object_references(struct hlsl_ctx *ctx, struct hlsl_ + { + struct hlsl_ir_resource_load *load = hlsl_ir_resource_load(instr); + +- if (!(load->resource.var->storage_modifiers & HLSL_STORAGE_UNIFORM)) ++ if (!load->resource.var->is_uniform) + { + hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF, + "Loaded resource must have a single uniform source."); +@@ -1704,7 +1705,7 @@ static bool validate_static_object_references(struct hlsl_ctx *ctx, struct hlsl_ + + if (load->sampler.var) + { +- if (!(load->sampler.var->storage_modifiers & HLSL_STORAGE_UNIFORM)) ++ if (!load->sampler.var->is_uniform) + { + hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF, + "Resource load sampler must have a single uniform source."); +@@ -1722,7 +1723,7 @@ static bool validate_static_object_references(struct hlsl_ctx *ctx, struct hlsl_ + { + struct hlsl_ir_resource_store *store = hlsl_ir_resource_store(instr); + +- if (!(store->resource.var->storage_modifiers & HLSL_STORAGE_UNIFORM)) ++ if (!store->resource.var->is_uniform) + { + hlsl_error(ctx, &instr->loc, VKD3D_SHADER_ERROR_HLSL_NON_STATIC_OBJECT_REF, + "Accessed resource must have a single uniform source."); +@@ -2066,6 +2067,97 @@ static bool lower_nonconstant_vector_derefs(struct hlsl_ctx *ctx, struct hlsl_ir + return false; + } + ++/* Lower combined samples and sampler variables to synthesized separated textures and samplers. ++ * That is, translate SM1-style samples in the source to SM4-style samples in the bytecode. */ ++static bool lower_combined_samples(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) ++{ ++ struct hlsl_ir_resource_load *load; ++ struct vkd3d_string_buffer *name; ++ struct hlsl_ir_var *var; ++ unsigned int i; ++ ++ if (instr->type != HLSL_IR_RESOURCE_LOAD) ++ return false; ++ load = hlsl_ir_resource_load(instr); ++ ++ switch (load->load_type) ++ { ++ case HLSL_RESOURCE_LOAD: ++ case HLSL_RESOURCE_GATHER_RED: ++ case HLSL_RESOURCE_GATHER_GREEN: ++ case HLSL_RESOURCE_GATHER_BLUE: ++ case HLSL_RESOURCE_GATHER_ALPHA: ++ case HLSL_RESOURCE_SAMPLE_CMP: ++ case HLSL_RESOURCE_SAMPLE_CMP_LZ: ++ case HLSL_RESOURCE_SAMPLE_GRAD: ++ return false; ++ ++ case HLSL_RESOURCE_SAMPLE: ++ case HLSL_RESOURCE_SAMPLE_LOD: ++ case HLSL_RESOURCE_SAMPLE_LOD_BIAS: ++ break; ++ } ++ if (load->sampler.var) ++ return false; ++ ++ if (!hlsl_type_is_resource(load->resource.var->data_type)) ++ { ++ hlsl_fixme(ctx, &instr->loc, "Lower combined samplers within structs."); ++ return false; ++ } ++ ++ assert(hlsl_type_get_regset(load->resource.var->data_type) == HLSL_REGSET_SAMPLERS); ++ ++ if (!(name = hlsl_get_string_buffer(ctx))) ++ return false; ++ vkd3d_string_buffer_printf(name, "%s", load->resource.var->name); ++ ++ TRACE("Lowering to separate resource %s.\n", debugstr_a(name->buffer)); ++ ++ if (!(var = hlsl_get_var(ctx->globals, name->buffer))) ++ { ++ struct hlsl_type *texture_array_type = hlsl_new_texture_type(ctx, load->sampling_dim, ++ hlsl_get_vector_type(ctx, HLSL_TYPE_FLOAT, 4), 0); ++ ++ /* Create (possibly multi-dimensional) texture array type with the same dims as the sampler array. */ ++ struct hlsl_type *arr_type = load->resource.var->data_type; ++ for (i = 0; i < load->resource.path_len; ++i) ++ { ++ assert(arr_type->class == HLSL_CLASS_ARRAY); ++ texture_array_type = hlsl_new_array_type(ctx, texture_array_type, arr_type->e.array.elements_count); ++ arr_type = arr_type->e.array.type; ++ } ++ ++ if (!(var = hlsl_new_synthetic_var_named(ctx, name->buffer, texture_array_type, &instr->loc, false))) ++ { ++ hlsl_release_string_buffer(ctx, name); ++ return false; ++ } ++ var->is_uniform = 1; ++ var->is_separated_resource = true; ++ ++ list_add_tail(&ctx->extern_vars, &var->extern_entry); ++ } ++ hlsl_release_string_buffer(ctx, name); ++ ++ if (load->sampling_dim != var->data_type->sampler_dim) ++ { ++ hlsl_error(ctx, &load->node.loc, VKD3D_SHADER_ERROR_HLSL_INCONSISTENT_SAMPLER, ++ "Cannot split combined samplers from \"%s\" if they have different usage dimensions.", ++ load->resource.var->name); ++ hlsl_note(ctx, &var->loc, VKD3D_SHADER_LOG_ERROR, "First use as combined sampler is here."); ++ return false; ++ ++ } ++ ++ hlsl_copy_deref(ctx, &load->sampler, &load->resource); ++ load->resource.var = var; ++ assert(hlsl_deref_get_type(ctx, &load->resource)->base_type == HLSL_TYPE_TEXTURE); ++ assert(hlsl_deref_get_type(ctx, &load->sampler)->base_type == HLSL_TYPE_SAMPLER); ++ ++ return true; ++} ++ + /* Lower DIV to RCP + MUL. */ + static bool lower_division(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) + { +@@ -3096,7 +3188,7 @@ static const char *debug_register(char class, struct hlsl_reg reg, const struct + return vkd3d_dbg_sprintf("%c%u%s", class, reg.id, debug_hlsl_writemask(reg.writemask)); + } + +-static bool track_object_components_usage(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) ++static bool track_object_components_sampler_dim(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) + { + struct hlsl_ir_resource_load *load; + struct hlsl_ir_var *var; +@@ -3108,15 +3200,16 @@ static bool track_object_components_usage(struct hlsl_ctx *ctx, struct hlsl_ir_n + + load = hlsl_ir_resource_load(instr); + var = load->resource.var; ++ + regset = hlsl_type_get_regset(hlsl_deref_get_type(ctx, &load->resource)); ++ if (!hlsl_regset_index_from_deref(ctx, &load->resource, regset, &index)) ++ return false; + + if (regset == HLSL_REGSET_SAMPLERS) + { + enum hlsl_sampler_dim dim; + + assert(!load->sampler.var); +- if (!hlsl_regset_index_from_deref(ctx, &load->resource, regset, &index)) +- return false; + + dim = var->objects_usage[regset][index].sampler_dim; + if (dim != load->sampling_dim) +@@ -3134,25 +3227,37 @@ static bool track_object_components_usage(struct hlsl_ctx *ctx, struct hlsl_ir_n + return false; + } + } +- var->objects_usage[regset][index].used = true; +- var->objects_usage[regset][index].sampler_dim = load->sampling_dim; + } +- else +- { +- if (!hlsl_regset_index_from_deref(ctx, &load->resource, regset, &index)) +- return false; ++ var->objects_usage[regset][index].sampler_dim = load->sampling_dim; + +- var->objects_usage[regset][index].used = true; +- var->objects_usage[regset][index].sampler_dim = load->sampling_dim; ++ return false; ++} + +- if (load->sampler.var) +- { +- var = load->sampler.var; +- if (!hlsl_regset_index_from_deref(ctx, &load->sampler, HLSL_REGSET_SAMPLERS, &index)) +- return false; ++static bool track_object_components_usage(struct hlsl_ctx *ctx, struct hlsl_ir_node *instr, void *context) ++{ ++ struct hlsl_ir_resource_load *load; ++ struct hlsl_ir_var *var; ++ enum hlsl_regset regset; ++ unsigned int index; + +- var->objects_usage[HLSL_REGSET_SAMPLERS][index].used = true; +- } ++ if (instr->type != HLSL_IR_RESOURCE_LOAD) ++ return false; ++ ++ load = hlsl_ir_resource_load(instr); ++ var = load->resource.var; ++ ++ regset = hlsl_type_get_regset(hlsl_deref_get_type(ctx, &load->resource)); ++ if (!hlsl_regset_index_from_deref(ctx, &load->resource, regset, &index)) ++ return false; ++ ++ var->objects_usage[regset][index].used = true; ++ if (load->sampler.var) ++ { ++ var = load->sampler.var; ++ if (!hlsl_regset_index_from_deref(ctx, &load->sampler, HLSL_REGSET_SAMPLERS, &index)) ++ return false; ++ ++ var->objects_usage[HLSL_REGSET_SAMPLERS][index].used = true; + } + + return false; +@@ -3172,9 +3277,12 @@ static void calculate_resource_register_counts(struct hlsl_ctx *ctx) + { + for (i = 0; i < type->reg_size[k]; ++i) + { +- /* Samplers are only allocated until the last used one. */ ++ bool is_separated = var->is_separated_resource; ++ ++ /* Samplers (and textures separated from them) are only allocated until the last ++ * used one. */ + if (var->objects_usage[k][i].used) +- var->regs[k].bind_count = (k == HLSL_REGSET_SAMPLERS) ? i + 1 : type->reg_size[k]; ++ var->regs[k].bind_count = (k == HLSL_REGSET_SAMPLERS || is_separated) ? i + 1 : type->reg_size[k]; + } + } + } +@@ -3568,7 +3676,7 @@ static void validate_buffer_offsets(struct hlsl_ctx *ctx) + + LIST_FOR_EACH_ENTRY(var1, &ctx->extern_vars, struct hlsl_ir_var, extern_entry) + { +- if (!var1->is_uniform || var1->data_type->class == HLSL_CLASS_OBJECT) ++ if (!var1->is_uniform || hlsl_type_is_resource(var1->data_type)) + continue; + + buffer = var1->buffer; +@@ -3579,7 +3687,7 @@ static void validate_buffer_offsets(struct hlsl_ctx *ctx) + { + unsigned int var1_reg_size, var2_reg_size; + +- if (!var2->is_uniform || var2->data_type->class == HLSL_CLASS_OBJECT) ++ if (!var2->is_uniform || hlsl_type_is_resource(var2->data_type)) + continue; + + if (var1 == var2 || var1->buffer != var2->buffer) +@@ -3629,7 +3737,7 @@ static void allocate_buffers(struct hlsl_ctx *ctx) + + LIST_FOR_EACH_ENTRY(var, &ctx->extern_vars, struct hlsl_ir_var, extern_entry) + { +- if (var->is_uniform && var->data_type->class != HLSL_CLASS_OBJECT) ++ if (var->is_uniform && !hlsl_type_is_resource(var->data_type)) + { + if (var->is_param) + var->buffer = ctx->params_buffer; +@@ -3689,7 +3797,7 @@ static void allocate_buffers(struct hlsl_ctx *ctx) + } + + static const struct hlsl_ir_var *get_allocated_object(struct hlsl_ctx *ctx, enum hlsl_regset regset, +- uint32_t index) ++ uint32_t index, bool allocated_only) + { + const struct hlsl_ir_var *var; + unsigned int start, count; +@@ -3703,6 +3811,9 @@ static const struct hlsl_ir_var *get_allocated_object(struct hlsl_ctx *ctx, enum + * bound there even if the reserved vars aren't used. */ + start = var->reg_reservation.reg_index; + count = var->data_type->reg_size[regset]; ++ ++ if (!var->regs[regset].allocated && allocated_only) ++ continue; + } + else if (var->regs[regset].allocated) + { +@@ -3743,6 +3854,7 @@ static void allocate_objects(struct hlsl_ctx *ctx, enum hlsl_regset regset) + if (count == 0) + continue; + ++ /* The variable was already allocated if it has a reservation. */ + if (var->regs[regset].allocated) + { + const struct hlsl_ir_var *reserved_object, *last_reported = NULL; +@@ -3761,7 +3873,10 @@ static void allocate_objects(struct hlsl_ctx *ctx, enum hlsl_regset regset) + { + index = var->regs[regset].id + i; + +- reserved_object = get_allocated_object(ctx, regset, index); ++ /* get_allocated_object() may return "var" itself, but we ++ * actually want that, otherwise we'll end up reporting the ++ * same conflict between the same two variables twice. */ ++ reserved_object = get_allocated_object(ctx, regset, index, true); + if (reserved_object && reserved_object != var && reserved_object != last_reported) + { + hlsl_error(ctx, &var->loc, VKD3D_SHADER_ERROR_HLSL_OVERLAPPING_RESERVATIONS, +@@ -3780,7 +3895,7 @@ static void allocate_objects(struct hlsl_ctx *ctx, enum hlsl_regset regset) + + while (available < count) + { +- if (get_allocated_object(ctx, regset, index)) ++ if (get_allocated_object(ctx, regset, index, false)) + available = 0; + else + ++available; +@@ -3924,6 +4039,7 @@ bool hlsl_regset_index_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref + bool hlsl_offset_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref, unsigned int *offset) + { + struct hlsl_ir_node *offset_node = deref->offset.node; ++ enum hlsl_regset regset; + unsigned int size; + + if (!offset_node) +@@ -3940,8 +4056,9 @@ bool hlsl_offset_from_deref(struct hlsl_ctx *ctx, const struct hlsl_deref *deref + return false; + + *offset = hlsl_ir_constant(offset_node)->value.u[0].u; ++ regset = hlsl_type_get_regset(deref->data_type); + +- size = deref->var->data_type->reg_size[deref->offset_regset]; ++ size = deref->var->data_type->reg_size[regset]; + if (*offset >= size) + { + hlsl_error(ctx, &deref->offset.node->loc, VKD3D_SHADER_ERROR_HLSL_OFFSET_OUT_OF_BOUNDS, +@@ -3971,7 +4088,8 @@ struct hlsl_reg hlsl_reg_from_deref(struct hlsl_ctx *ctx, const struct hlsl_dere + struct hlsl_reg ret = var->regs[HLSL_REGSET_NUMERIC]; + unsigned int offset = hlsl_offset_from_deref_safe(ctx, deref); + +- assert(deref->offset_regset == HLSL_REGSET_NUMERIC); ++ assert(deref->data_type); ++ assert(deref->data_type->class <= HLSL_CLASS_LAST_NUMERIC); + + ret.id += offset / 4; + +@@ -4169,6 +4287,12 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry + hlsl_transform_ir(ctx, lower_casts_to_bool, body, NULL); + hlsl_transform_ir(ctx, lower_int_dot, body, NULL); + ++ hlsl_transform_ir(ctx, validate_static_object_references, body, NULL); ++ hlsl_transform_ir(ctx, track_object_components_sampler_dim, body, NULL); ++ if (profile->major_version >= 4) ++ hlsl_transform_ir(ctx, lower_combined_samples, body, NULL); ++ hlsl_transform_ir(ctx, track_object_components_usage, body, NULL); ++ + if (profile->major_version < 4) + { + hlsl_transform_ir(ctx, lower_division, body, NULL); +@@ -4182,9 +4306,6 @@ int hlsl_emit_bytecode(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry + hlsl_transform_ir(ctx, lower_abs, body, NULL); + } + +- hlsl_transform_ir(ctx, validate_static_object_references, body, NULL); +- hlsl_transform_ir(ctx, track_object_components_usage, body, NULL); +- + /* TODO: move forward, remove when no longer needed */ + transform_derefs(ctx, replace_deref_path_with_offset, body); + while (hlsl_transform_ir(ctx, hlsl_fold_constant_exprs, body, NULL)); +diff --git a/libs/vkd3d/libs/vkd3d-shader/tpf.c b/libs/vkd3d/libs/vkd3d-shader/tpf.c +index 290fdcb3f62..801c688a297 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/tpf.c ++++ b/libs/vkd3d/libs/vkd3d-shader/tpf.c +@@ -2970,47 +2970,154 @@ static D3D_SRV_DIMENSION sm4_rdef_resource_dimension(const struct hlsl_type *typ + } + } + ++struct extern_resource ++{ ++ /* var is only not NULL if this resource is a whole variable, so it may be responsible for more ++ * than one component. */ ++ const struct hlsl_ir_var *var; ++ ++ char *name; ++ struct hlsl_type *data_type; ++ bool is_user_packed; ++ ++ enum hlsl_regset regset; ++ unsigned int id, bind_count; ++}; ++ + static int sm4_compare_extern_resources(const void *a, const void *b) + { +- const struct hlsl_ir_var *aa = *(const struct hlsl_ir_var **)a; +- const struct hlsl_ir_var *bb = *(const struct hlsl_ir_var **)b; +- enum hlsl_regset aa_regset, bb_regset; ++ const struct extern_resource *aa = (const struct extern_resource *)a; ++ const struct extern_resource *bb = (const struct extern_resource *)b; ++ int r; ++ ++ if ((r = vkd3d_u32_compare(aa->regset, bb->regset))) ++ return r; + +- aa_regset = hlsl_type_get_regset(aa->data_type); +- bb_regset = hlsl_type_get_regset(bb->data_type); ++ return vkd3d_u32_compare(aa->id, bb->id); ++} + +- if (aa_regset != bb_regset) +- return aa_regset - bb_regset; ++static void sm4_free_extern_resources(struct extern_resource *extern_resources, unsigned int count) ++{ ++ unsigned int i; + +- return aa->regs[aa_regset].id - bb->regs[bb_regset].id; ++ for (i = 0; i < count; ++i) ++ vkd3d_free(extern_resources[i].name); ++ vkd3d_free(extern_resources); ++} ++ ++static const char *string_skip_tag(const char *string) ++{ ++ if (!strncmp(string, "", strlen(""))) ++ return string + strlen(""); ++ return string; + } + +-static const struct hlsl_ir_var **sm4_get_extern_resources(struct hlsl_ctx *ctx, unsigned int *count) ++static struct extern_resource *sm4_get_extern_resources(struct hlsl_ctx *ctx, unsigned int *count) + { +- const struct hlsl_ir_var **extern_resources = NULL; ++ bool separate_components = ctx->profile->major_version == 5 && ctx->profile->minor_version == 0; ++ struct extern_resource *extern_resources = NULL; + const struct hlsl_ir_var *var; + enum hlsl_regset regset; + size_t capacity = 0; ++ char *name; + + *count = 0; + + LIST_FOR_EACH_ENTRY(var, &ctx->extern_vars, struct hlsl_ir_var, extern_entry) + { +- if (!hlsl_type_is_resource(var->data_type)) +- continue; +- regset = hlsl_type_get_regset(var->data_type); +- if (!var->regs[regset].allocated) +- continue; +- +- if (!(hlsl_array_reserve(ctx, (void **)&extern_resources, &capacity, *count + 1, +- sizeof(*extern_resources)))) ++ if (separate_components) + { +- *count = 0; +- return NULL; ++ unsigned int component_count = hlsl_type_component_count(var->data_type); ++ unsigned int k, regset_offset; ++ ++ for (k = 0; k < component_count; ++k) ++ { ++ struct hlsl_type *component_type = hlsl_type_get_component_type(ctx, var->data_type, k); ++ struct vkd3d_string_buffer *name_buffer; ++ ++ if (!hlsl_type_is_resource(component_type)) ++ continue; ++ ++ regset = hlsl_type_get_regset(component_type); ++ regset_offset = hlsl_type_get_component_offset(ctx, var->data_type, regset, k); ++ ++ if (regset_offset > var->regs[regset].bind_count) ++ continue; ++ ++ if (var->objects_usage[regset][regset_offset].used) ++ { ++ if (!(hlsl_array_reserve(ctx, (void **)&extern_resources, &capacity, *count + 1, ++ sizeof(*extern_resources)))) ++ { ++ sm4_free_extern_resources(extern_resources, *count); ++ *count = 0; ++ return NULL; ++ } ++ ++ if (!(name_buffer = hlsl_component_to_string(ctx, var, k))) ++ { ++ sm4_free_extern_resources(extern_resources, *count); ++ *count = 0; ++ return NULL; ++ } ++ if (!(name = hlsl_strdup(ctx, string_skip_tag(name_buffer->buffer)))) ++ { ++ sm4_free_extern_resources(extern_resources, *count); ++ *count = 0; ++ hlsl_release_string_buffer(ctx, name_buffer); ++ return NULL; ++ } ++ hlsl_release_string_buffer(ctx, name_buffer); ++ ++ extern_resources[*count].var = NULL; ++ ++ extern_resources[*count].name = name; ++ extern_resources[*count].data_type = component_type; ++ extern_resources[*count].is_user_packed = false; ++ ++ extern_resources[*count].regset = regset; ++ extern_resources[*count].id = var->regs[regset].id + regset_offset; ++ extern_resources[*count].bind_count = 1; ++ ++ ++*count; ++ } ++ } + } ++ else ++ { ++ if (!hlsl_type_is_resource(var->data_type)) ++ continue; ++ regset = hlsl_type_get_regset(var->data_type); ++ if (!var->regs[regset].allocated) ++ continue; ++ ++ if (!(hlsl_array_reserve(ctx, (void **)&extern_resources, &capacity, *count + 1, ++ sizeof(*extern_resources)))) ++ { ++ sm4_free_extern_resources(extern_resources, *count); ++ *count = 0; ++ return NULL; ++ } + +- extern_resources[*count] = var; +- ++*count; ++ if (!(name = hlsl_strdup(ctx, string_skip_tag(var->name)))) ++ { ++ sm4_free_extern_resources(extern_resources, *count); ++ *count = 0; ++ return NULL; ++ } ++ ++ extern_resources[*count].var = var; ++ ++ extern_resources[*count].name = name; ++ extern_resources[*count].data_type = var->data_type; ++ extern_resources[*count].is_user_packed = !!var->reg_reservation.reg_type; ++ ++ extern_resources[*count].regset = regset; ++ extern_resources[*count].id = var->regs[regset].id; ++ extern_resources[*count].bind_count = var->regs[regset].bind_count; ++ ++ ++*count; ++ } + } + + qsort(extern_resources, *count, sizeof(*extern_resources), sm4_compare_extern_resources); +@@ -3023,8 +3130,8 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc) + size_t cbuffers_offset, resources_offset, creator_offset, string_offset; + size_t cbuffer_position, resource_position, creator_position; + const struct hlsl_profile_info *profile = ctx->profile; +- const struct hlsl_ir_var **extern_resources; + struct vkd3d_bytecode_buffer buffer = {0}; ++ struct extern_resource *extern_resources; + const struct hlsl_buffer *cbuffer; + const struct hlsl_ir_var *var; + +@@ -3078,18 +3185,15 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc) + + for (i = 0; i < extern_resources_count; ++i) + { +- enum hlsl_regset regset; ++ const struct extern_resource *resource = &extern_resources[i]; + uint32_t flags = 0; + +- var = extern_resources[i]; +- regset = hlsl_type_get_regset(var->data_type); +- +- if (var->reg_reservation.reg_type) ++ if (resource->is_user_packed) + flags |= D3D_SIF_USERPACKED; + + put_u32(&buffer, 0); /* name */ +- put_u32(&buffer, sm4_resource_type(var->data_type)); +- if (regset == HLSL_REGSET_SAMPLERS) ++ put_u32(&buffer, sm4_resource_type(resource->data_type)); ++ if (resource->regset == HLSL_REGSET_SAMPLERS) + { + put_u32(&buffer, 0); + put_u32(&buffer, 0); +@@ -3097,15 +3201,15 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc) + } + else + { +- unsigned int dimx = hlsl_type_get_component_type(ctx, var->data_type, 0)->e.resource_format->dimx; ++ unsigned int dimx = hlsl_type_get_component_type(ctx, resource->data_type, 0)->e.resource_format->dimx; + +- put_u32(&buffer, sm4_resource_format(var->data_type)); +- put_u32(&buffer, sm4_rdef_resource_dimension(var->data_type)); ++ put_u32(&buffer, sm4_resource_format(resource->data_type)); ++ put_u32(&buffer, sm4_rdef_resource_dimension(resource->data_type)); + put_u32(&buffer, ~0u); /* FIXME: multisample count */ + flags |= (dimx - 1) << VKD3D_SM4_SIF_TEXTURE_COMPONENTS_SHIFT; + } +- put_u32(&buffer, var->regs[regset].id); +- put_u32(&buffer, var->regs[regset].bind_count); ++ put_u32(&buffer, resource->id); ++ put_u32(&buffer, resource->bind_count); + put_u32(&buffer, flags); + } + +@@ -3131,9 +3235,9 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc) + + for (i = 0; i < extern_resources_count; ++i) + { +- var = extern_resources[i]; ++ const struct extern_resource *resource = &extern_resources[i]; + +- string_offset = put_string(&buffer, var->name); ++ string_offset = put_string(&buffer, resource->name); + set_u32(&buffer, resources_offset + i * 8 * sizeof(uint32_t), string_offset); + } + +@@ -3239,7 +3343,7 @@ static void write_sm4_rdef(struct hlsl_ctx *ctx, struct dxbc_writer *dxbc) + + add_section(dxbc, TAG_RDEF, &buffer); + +- vkd3d_free(extern_resources); ++ sm4_free_extern_resources(extern_resources, extern_resources_count); + } + + static enum vkd3d_sm4_resource_type sm4_resource_dimension(const struct hlsl_type *type) +@@ -3349,8 +3453,9 @@ struct sm4_instruction + + static void sm4_register_from_deref(struct hlsl_ctx *ctx, struct sm4_register *reg, + unsigned int *writemask, enum vkd3d_sm4_swizzle_type *swizzle_type, +- const struct hlsl_deref *deref, const struct hlsl_type *data_type) ++ const struct hlsl_deref *deref) + { ++ const struct hlsl_type *data_type = hlsl_deref_get_type(ctx, deref); + const struct hlsl_ir_var *var = deref->var; + + if (var->is_uniform) +@@ -3365,7 +3470,7 @@ static void sm4_register_from_deref(struct hlsl_ctx *ctx, struct sm4_register *r + *swizzle_type = VKD3D_SM4_SWIZZLE_VEC4; + reg->idx[0] = var->regs[HLSL_REGSET_TEXTURES].id; + reg->idx[0] += hlsl_offset_from_deref_safe(ctx, deref); +- assert(deref->offset_regset == HLSL_REGSET_TEXTURES); ++ assert(regset == HLSL_REGSET_TEXTURES); + reg->idx_count = 1; + *writemask = VKD3DSP_WRITEMASK_ALL; + } +@@ -3377,7 +3482,7 @@ static void sm4_register_from_deref(struct hlsl_ctx *ctx, struct sm4_register *r + *swizzle_type = VKD3D_SM4_SWIZZLE_VEC4; + reg->idx[0] = var->regs[HLSL_REGSET_UAVS].id; + reg->idx[0] += hlsl_offset_from_deref_safe(ctx, deref); +- assert(deref->offset_regset == HLSL_REGSET_UAVS); ++ assert(regset == HLSL_REGSET_UAVS); + reg->idx_count = 1; + *writemask = VKD3DSP_WRITEMASK_ALL; + } +@@ -3389,7 +3494,7 @@ static void sm4_register_from_deref(struct hlsl_ctx *ctx, struct sm4_register *r + *swizzle_type = VKD3D_SM4_SWIZZLE_NONE; + reg->idx[0] = var->regs[HLSL_REGSET_SAMPLERS].id; + reg->idx[0] += hlsl_offset_from_deref_safe(ctx, deref); +- assert(deref->offset_regset == HLSL_REGSET_SAMPLERS); ++ assert(regset == HLSL_REGSET_SAMPLERS); + reg->idx_count = 1; + *writemask = VKD3DSP_WRITEMASK_ALL; + } +@@ -3487,11 +3592,11 @@ static void sm4_register_from_deref(struct hlsl_ctx *ctx, struct sm4_register *r + } + + static void sm4_src_from_deref(struct hlsl_ctx *ctx, struct sm4_src_register *src, +- const struct hlsl_deref *deref, const struct hlsl_type *data_type, unsigned int map_writemask) ++ const struct hlsl_deref *deref, unsigned int map_writemask) + { + unsigned int writemask; + +- sm4_register_from_deref(ctx, &src->reg, &writemask, &src->swizzle_type, deref, data_type); ++ sm4_register_from_deref(ctx, &src->reg, &writemask, &src->swizzle_type, deref); + if (src->swizzle_type == VKD3D_SM4_SWIZZLE_VEC4) + src->swizzle = hlsl_map_swizzle(hlsl_swizzle_from_writemask(writemask), map_writemask); + } +@@ -3692,9 +3797,11 @@ static void write_sm4_dcl_constant_buffer(struct vkd3d_bytecode_buffer *buffer, + write_sm4_instruction(buffer, &instr); + } + +-static void write_sm4_dcl_samplers(struct vkd3d_bytecode_buffer *buffer, const struct hlsl_ir_var *var) ++static void write_sm4_dcl_samplers(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buffer, ++ const struct extern_resource *resource) + { +- unsigned int i, count = var->data_type->reg_size[HLSL_REGSET_SAMPLERS]; ++ struct hlsl_type *component_type; ++ unsigned int i; + struct sm4_instruction instr = + { + .opcode = VKD3D_SM4_OP_DCL_SAMPLER, +@@ -3704,38 +3811,44 @@ static void write_sm4_dcl_samplers(struct vkd3d_bytecode_buffer *buffer, const s + .dst_count = 1, + }; + +- if (var->data_type->sampler_dim == HLSL_SAMPLER_DIM_COMPARISON) ++ component_type = hlsl_type_get_component_type(ctx, resource->data_type, 0); ++ ++ if (component_type->sampler_dim == HLSL_SAMPLER_DIM_COMPARISON) + instr.opcode |= VKD3D_SM4_SAMPLER_COMPARISON << VKD3D_SM4_SAMPLER_MODE_SHIFT; + +- for (i = 0; i < count; ++i) ++ assert(resource->regset == HLSL_REGSET_SAMPLERS); ++ ++ for (i = 0; i < resource->bind_count; ++i) + { +- if (!var->objects_usage[HLSL_REGSET_SAMPLERS][i].used) ++ if (resource->var && !resource->var->objects_usage[HLSL_REGSET_SAMPLERS][i].used) + continue; + +- instr.dsts[0].reg.idx[0] = var->regs[HLSL_REGSET_SAMPLERS].id + i; ++ instr.dsts[0].reg.idx[0] = resource->id + i; + write_sm4_instruction(buffer, &instr); + } + } + + static void write_sm4_dcl_textures(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buffer, +- const struct hlsl_ir_var *var, bool uav) ++ const struct extern_resource *resource, bool uav) + { + enum hlsl_regset regset = uav ? HLSL_REGSET_UAVS : HLSL_REGSET_TEXTURES; +- unsigned int i, count = var->data_type->reg_size[regset]; + struct hlsl_type *component_type; + struct sm4_instruction instr; ++ unsigned int i; + +- component_type = hlsl_type_get_component_type(ctx, var->data_type, 0); ++ assert(resource->regset == regset); + +- for (i = 0; i < count; ++i) ++ component_type = hlsl_type_get_component_type(ctx, resource->data_type, 0); ++ ++ for (i = 0; i < resource->bind_count; ++i) + { +- if (!var->objects_usage[regset][i].used) ++ if (resource->var && !resource->var->objects_usage[regset][i].used) + continue; + + instr = (struct sm4_instruction) + { + .dsts[0].reg.type = uav ? VKD3D_SM5_RT_UAV : VKD3D_SM4_RT_RESOURCE, +- .dsts[0].reg.idx = {var->regs[regset].id + i}, ++ .dsts[0].reg.idx = {resource->id + i}, + .dsts[0].reg.idx_count = 1, + .dst_count = 1, + +@@ -3745,11 +3858,11 @@ static void write_sm4_dcl_textures(struct hlsl_ctx *ctx, struct vkd3d_bytecode_b + + if (uav) + { +- switch (var->data_type->sampler_dim) ++ switch (resource->data_type->sampler_dim) + { + case HLSL_SAMPLER_DIM_STRUCTURED_BUFFER: + instr.opcode = VKD3D_SM5_OP_DCL_UAV_STRUCTURED; +- instr.byte_stride = var->data_type->e.resource_format->reg_size[HLSL_REGSET_NUMERIC] * 4; ++ instr.byte_stride = resource->data_type->e.resource_format->reg_size[HLSL_REGSET_NUMERIC] * 4; + break; + default: + instr.opcode = VKD3D_SM5_OP_DCL_UAV_TYPED; +@@ -4011,11 +4124,11 @@ static void write_sm4_binary_op_with_two_destinations(struct vkd3d_bytecode_buff + } + + static void write_sm4_ld(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buffer, +- const struct hlsl_type *resource_type, const struct hlsl_ir_node *dst, +- const struct hlsl_deref *resource, const struct hlsl_ir_node *coords, +- const struct hlsl_ir_node *sample_index, const struct hlsl_ir_node *texel_offset, +- enum hlsl_sampler_dim dim) ++ const struct hlsl_ir_node *dst, const struct hlsl_deref *resource, ++ const struct hlsl_ir_node *coords, const struct hlsl_ir_node *sample_index, ++ const struct hlsl_ir_node *texel_offset, enum hlsl_sampler_dim dim) + { ++ const struct hlsl_type *resource_type = hlsl_deref_get_type(ctx, resource); + bool multisampled = resource_type->base_type == HLSL_TYPE_TEXTURE + && (resource_type->sampler_dim == HLSL_SAMPLER_DIM_2DMS || resource_type->sampler_dim == HLSL_SAMPLER_DIM_2DMSARRAY); + bool uav = (hlsl_type_get_regset(resource_type) == HLSL_REGSET_UAVS); +@@ -4055,7 +4168,7 @@ static void write_sm4_ld(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buf + + sm4_src_from_node(&instr.srcs[0], coords, coords_writemask); + +- sm4_src_from_deref(ctx, &instr.srcs[1], resource, resource_type, instr.dsts[0].writemask); ++ sm4_src_from_deref(ctx, &instr.srcs[1], resource, instr.dsts[0].writemask); + + instr.src_count = 2; + +@@ -4092,7 +4205,6 @@ static void write_sm4_ld(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buf + static void write_sm4_sample(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buffer, + const struct hlsl_ir_resource_load *load) + { +- const struct hlsl_type *resource_type = load->resource.var->data_type; + const struct hlsl_ir_node *texel_offset = load->texel_offset.node; + const struct hlsl_ir_node *coords = load->coords.node; + const struct hlsl_deref *resource = &load->resource; +@@ -4145,8 +4257,8 @@ static void write_sm4_sample(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer + instr.dst_count = 1; + + sm4_src_from_node(&instr.srcs[0], coords, VKD3DSP_WRITEMASK_ALL); +- sm4_src_from_deref(ctx, &instr.srcs[1], resource, resource_type, instr.dsts[0].writemask); +- sm4_src_from_deref(ctx, &instr.srcs[2], sampler, sampler->var->data_type, VKD3DSP_WRITEMASK_ALL); ++ sm4_src_from_deref(ctx, &instr.srcs[1], resource, instr.dsts[0].writemask); ++ sm4_src_from_deref(ctx, &instr.srcs[2], sampler, VKD3DSP_WRITEMASK_ALL); + instr.src_count = 3; + + if (load->load_type == HLSL_RESOURCE_SAMPLE_LOD +@@ -4316,7 +4428,7 @@ static void write_sm4_store_uav_typed(struct hlsl_ctx *ctx, struct vkd3d_bytecod + memset(&instr, 0, sizeof(instr)); + instr.opcode = VKD3D_SM5_OP_STORE_UAV_TYPED; + +- sm4_register_from_deref(ctx, &instr.dsts[0].reg, &instr.dsts[0].writemask, NULL, dst, dst->var->data_type); ++ sm4_register_from_deref(ctx, &instr.dsts[0].reg, &instr.dsts[0].writemask, NULL, dst); + instr.dst_count = 1; + + sm4_src_from_node(&instr.srcs[0], coords, VKD3DSP_WRITEMASK_ALL); +@@ -4856,7 +4968,7 @@ static void write_sm4_load(struct hlsl_ctx *ctx, + + instr.opcode = VKD3D_SM4_OP_MOVC; + +- sm4_src_from_deref(ctx, &instr.srcs[0], &load->src, type, instr.dsts[0].writemask); ++ sm4_src_from_deref(ctx, &instr.srcs[0], &load->src, instr.dsts[0].writemask); + + memset(&value, 0xff, sizeof(value)); + sm4_src_from_constant_value(&instr.srcs[1], &value, type->dimx, instr.dsts[0].writemask); +@@ -4868,7 +4980,7 @@ static void write_sm4_load(struct hlsl_ctx *ctx, + { + instr.opcode = VKD3D_SM4_OP_MOV; + +- sm4_src_from_deref(ctx, &instr.srcs[0], &load->src, type, instr.dsts[0].writemask); ++ sm4_src_from_deref(ctx, &instr.srcs[0], &load->src, instr.dsts[0].writemask); + instr.src_count = 1; + } + +@@ -4892,8 +5004,7 @@ static void write_sm4_loop(struct hlsl_ctx *ctx, + } + + static void write_sm4_gather(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer *buffer, +- const struct hlsl_type *resource_type, const struct hlsl_ir_node *dst, +- const struct hlsl_deref *resource, const struct hlsl_deref *sampler, ++ const struct hlsl_ir_node *dst, const struct hlsl_deref *resource, const struct hlsl_deref *sampler, + const struct hlsl_ir_node *coords, unsigned int swizzle, const struct hlsl_ir_node *texel_offset) + { + struct sm4_src_register *src; +@@ -4923,10 +5034,10 @@ static void write_sm4_gather(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer + } + } + +- sm4_src_from_deref(ctx, &instr.srcs[instr.src_count++], resource, resource_type, instr.dsts[0].writemask); ++ sm4_src_from_deref(ctx, &instr.srcs[instr.src_count++], resource, instr.dsts[0].writemask); + + src = &instr.srcs[instr.src_count++]; +- sm4_src_from_deref(ctx, src, sampler, sampler->var->data_type, VKD3DSP_WRITEMASK_ALL); ++ sm4_src_from_deref(ctx, src, sampler, VKD3DSP_WRITEMASK_ALL); + src->reg.dim = VKD3D_SM4_DIMENSION_VEC4; + src->swizzle_type = VKD3D_SM4_SWIZZLE_SCALAR; + src->swizzle = swizzle; +@@ -4937,34 +5048,16 @@ static void write_sm4_gather(struct hlsl_ctx *ctx, struct vkd3d_bytecode_buffer + static void write_sm4_resource_load(struct hlsl_ctx *ctx, + struct vkd3d_bytecode_buffer *buffer, const struct hlsl_ir_resource_load *load) + { +- const struct hlsl_type *resource_type = load->resource.var->data_type; + const struct hlsl_ir_node *texel_offset = load->texel_offset.node; + const struct hlsl_ir_node *sample_index = load->sample_index.node; + const struct hlsl_ir_node *coords = load->coords.node; + +- if (!hlsl_type_is_resource(resource_type)) ++ if (load->sampler.var && !load->sampler.var->is_uniform) + { +- hlsl_fixme(ctx, &load->node.loc, "Separate object fields as new variables."); ++ hlsl_fixme(ctx, &load->node.loc, "Sample using non-uniform sampler variable."); + return; + } + +- if (load->sampler.var) +- { +- const struct hlsl_type *sampler_type = load->sampler.var->data_type; +- +- if (!hlsl_type_is_resource(sampler_type)) +- { +- hlsl_fixme(ctx, &load->node.loc, "Separate object fields as new variables."); +- return; +- } +- +- if (!load->sampler.var->is_uniform) +- { +- hlsl_fixme(ctx, &load->node.loc, "Sample using non-uniform sampler variable."); +- return; +- } +- } +- + if (!load->resource.var->is_uniform) + { + hlsl_fixme(ctx, &load->node.loc, "Load from non-uniform resource variable."); +@@ -4974,7 +5067,7 @@ static void write_sm4_resource_load(struct hlsl_ctx *ctx, + switch (load->load_type) + { + case HLSL_RESOURCE_LOAD: +- write_sm4_ld(ctx, buffer, resource_type, &load->node, &load->resource, ++ write_sm4_ld(ctx, buffer, &load->node, &load->resource, + coords, sample_index, texel_offset, load->sampling_dim); + break; + +@@ -4984,32 +5077,29 @@ static void write_sm4_resource_load(struct hlsl_ctx *ctx, + case HLSL_RESOURCE_SAMPLE_LOD: + case HLSL_RESOURCE_SAMPLE_LOD_BIAS: + case HLSL_RESOURCE_SAMPLE_GRAD: +- if (!load->sampler.var) +- { +- hlsl_fixme(ctx, &load->node.loc, "SM4 combined sample expression."); +- return; +- } ++ /* Combined sample expressions were lowered. */ ++ assert(load->sampler.var); + write_sm4_sample(ctx, buffer, load); + break; + + case HLSL_RESOURCE_GATHER_RED: +- write_sm4_gather(ctx, buffer, resource_type, &load->node, &load->resource, +- &load->sampler, coords, HLSL_SWIZZLE(X, X, X, X), texel_offset); ++ write_sm4_gather(ctx, buffer, &load->node, &load->resource, &load->sampler, coords, ++ HLSL_SWIZZLE(X, X, X, X), texel_offset); + break; + + case HLSL_RESOURCE_GATHER_GREEN: +- write_sm4_gather(ctx, buffer, resource_type, &load->node, &load->resource, +- &load->sampler, coords, HLSL_SWIZZLE(Y, Y, Y, Y), texel_offset); ++ write_sm4_gather(ctx, buffer, &load->node, &load->resource, &load->sampler, coords, ++ HLSL_SWIZZLE(Y, Y, Y, Y), texel_offset); + break; + + case HLSL_RESOURCE_GATHER_BLUE: +- write_sm4_gather(ctx, buffer, resource_type, &load->node, &load->resource, +- &load->sampler, coords, HLSL_SWIZZLE(Z, Z, Z, Z), texel_offset); ++ write_sm4_gather(ctx, buffer, &load->node, &load->resource, &load->sampler, coords, ++ HLSL_SWIZZLE(Z, Z, Z, Z), texel_offset); + break; + + case HLSL_RESOURCE_GATHER_ALPHA: +- write_sm4_gather(ctx, buffer, resource_type, &load->node, &load->resource, +- &load->sampler, coords, HLSL_SWIZZLE(W, W, W, W), texel_offset); ++ write_sm4_gather(ctx, buffer, &load->node, &load->resource, &load->sampler, coords, ++ HLSL_SWIZZLE(W, W, W, W), texel_offset); + break; + } + } +@@ -5017,13 +5107,7 @@ static void write_sm4_resource_load(struct hlsl_ctx *ctx, + static void write_sm4_resource_store(struct hlsl_ctx *ctx, + struct vkd3d_bytecode_buffer *buffer, const struct hlsl_ir_resource_store *store) + { +- const struct hlsl_type *resource_type = store->resource.var->data_type; +- +- if (!hlsl_type_is_resource(resource_type)) +- { +- hlsl_fixme(ctx, &store->node.loc, "Separate object fields as new variables."); +- return; +- } ++ struct hlsl_type *resource_type = hlsl_deref_get_type(ctx, &store->resource); + + if (!store->resource.var->is_uniform) + { +@@ -5050,7 +5134,7 @@ static void write_sm4_store(struct hlsl_ctx *ctx, + memset(&instr, 0, sizeof(instr)); + instr.opcode = VKD3D_SM4_OP_MOV; + +- sm4_register_from_deref(ctx, &instr.dsts[0].reg, &writemask, NULL, &store->lhs, rhs->data_type); ++ sm4_register_from_deref(ctx, &instr.dsts[0].reg, &writemask, NULL, &store->lhs); + instr.dsts[0].writemask = hlsl_combine_writemasks(writemask, store->writemask); + instr.dst_count = 1; + +@@ -5161,8 +5245,8 @@ static void write_sm4_shdr(struct hlsl_ctx *ctx, + const struct hlsl_ir_function_decl *entry_func, struct dxbc_writer *dxbc) + { + const struct hlsl_profile_info *profile = ctx->profile; +- const struct hlsl_ir_var **extern_resources; + struct vkd3d_bytecode_buffer buffer = {0}; ++ struct extern_resource *extern_resources; + unsigned int extern_resources_count, i; + const struct hlsl_buffer *cbuffer; + const struct hlsl_ir_var *var; +@@ -5194,17 +5278,14 @@ static void write_sm4_shdr(struct hlsl_ctx *ctx, + + for (i = 0; i < extern_resources_count; ++i) + { +- enum hlsl_regset regset; +- +- var = extern_resources[i]; +- regset = hlsl_type_get_regset(var->data_type); ++ const struct extern_resource *resource = &extern_resources[i]; + +- if (regset == HLSL_REGSET_SAMPLERS) +- write_sm4_dcl_samplers(&buffer, var); +- else if (regset == HLSL_REGSET_TEXTURES) +- write_sm4_dcl_textures(ctx, &buffer, var, false); +- else if (regset == HLSL_REGSET_UAVS) +- write_sm4_dcl_textures(ctx, &buffer, var, true); ++ if (resource->regset == HLSL_REGSET_SAMPLERS) ++ write_sm4_dcl_samplers(ctx, &buffer, resource); ++ else if (resource->regset == HLSL_REGSET_TEXTURES) ++ write_sm4_dcl_textures(ctx, &buffer, resource, false); ++ else if (resource->regset == HLSL_REGSET_UAVS) ++ write_sm4_dcl_textures(ctx, &buffer, resource, true); + } + + LIST_FOR_EACH_ENTRY(var, &ctx->extern_vars, struct hlsl_ir_var, extern_entry) +@@ -5227,7 +5308,7 @@ static void write_sm4_shdr(struct hlsl_ctx *ctx, + + add_section(dxbc, TAG_SHDR, &buffer); + +- vkd3d_free(extern_resources); ++ sm4_free_extern_resources(extern_resources, extern_resources_count); + } + + int hlsl_sm4_write(struct hlsl_ctx *ctx, struct hlsl_ir_function_decl *entry_func, struct vkd3d_shader_code *out) +diff --git a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h +index 85fca964227..0e93f3a556a 100644 +--- a/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h ++++ b/libs/vkd3d/libs/vkd3d-shader/vkd3d_shader_private.h +@@ -155,11 +155,19 @@ 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_OPERAND_COUNT = 8005, ++ VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_TABLE = 8006, ++ VKD3D_SHADER_ERROR_DXIL_INVALID_VALUE_SYMTAB = 8007, ++ VKD3D_SHADER_ERROR_DXIL_UNSUPPORTED_BITCODE_FORMAT = 8008, ++ VKD3D_SHADER_ERROR_DXIL_INVALID_FUNCTION_DCL = 8009, ++ VKD3D_SHADER_ERROR_DXIL_INVALID_TYPE_ID = 8010, ++ VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE = 8011, + + 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, + }; + + enum vkd3d_shader_opcode +@@ -529,6 +537,7 @@ enum vkd3d_data_type + VKD3D_DATA_DOUBLE, + VKD3D_DATA_CONTINUED, + VKD3D_DATA_UNUSED, ++ VKD3D_DATA_UINT8, + }; + + enum vkd3d_immconst_type +-- +2.40.1 +