Conflicts with !450 but can probably go upstream before it since backend changes here are minimal.
-- v5: vkd3d-shader/dxil: Handle the DXIL SWITCH instruction. vkd3d-shader: Rename shader_instruction_array_add_icb() to shader_instruction_array_add_opaque_param(). vkd3d-shader/dxil: Handle the DXIL PHI instruction.
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 1709212fa..4090293b1 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -448,11 +448,23 @@ struct sm6_symbol const char *name; };
+enum sm6_block_terminator_type +{ + TERMINATOR_RET, +}; + +struct sm6_block_terminator +{ + enum sm6_block_terminator_type type; +}; + struct sm6_block { struct vkd3d_shader_instruction *instructions; size_t instruction_capacity; size_t instruction_count; + + struct sm6_block_terminator terminator; };
struct sm6_function @@ -4061,6 +4073,8 @@ static void sm6_parser_emit_ret(struct sm6_parser *sm6, const struct dxil_record if (record->operand_count) FIXME("Non-void return is not implemented.\n");
+ code_block->terminator.type = TERMINATOR_RET; + ins->handler_idx = VKD3DSIH_NOP; }
@@ -4299,6 +4313,21 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const return VKD3D_OK; }
+static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_parser *sm6) +{ + switch (block->terminator.type) + { + case TERMINATOR_RET: + { + sm6_parser_add_instruction(sm6, VKD3DSIH_RET); + break; + } + + default: + vkd3d_unreachable(); + } +} + static bool sm6_block_emit_instructions(struct sm6_block *block, struct sm6_parser *sm6) { struct vkd3d_shader_instruction *ins = sm6_parser_require_space(sm6, block->instruction_count + 1); @@ -4309,7 +4338,7 @@ static bool sm6_block_emit_instructions(struct sm6_block *block, struct sm6_pars memcpy(ins, block->instructions, block->instruction_count * sizeof(*block->instructions)); sm6->p.instructions.count += block->instruction_count;
- sm6_parser_add_instruction(sm6, VKD3DSIH_RET); + sm6_block_emit_terminator(block, sm6);
return true; }
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 9 ++ libs/vkd3d-shader/dxil.c | 165 ++++++++++++++++++----- libs/vkd3d-shader/ir.c | 20 +++ libs/vkd3d-shader/vkd3d_shader_private.h | 9 ++ 4 files changed, 172 insertions(+), 31 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 82d1d71d9..48c44e89f 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -44,6 +44,7 @@ static const char * const shader_opcode_names[] = [VKD3DSIH_BEM ] = "bem", [VKD3DSIH_BFI ] = "bfi", [VKD3DSIH_BFREV ] = "bfrev", + [VKD3DSIH_BRANCH ] = "branch", [VKD3DSIH_BREAK ] = "break", [VKD3DSIH_BREAKC ] = "breakc", [VKD3DSIH_BREAKP ] = "breakp", @@ -1646,6 +1647,14 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler,
switch (ins->handler_idx) { + case VKD3DSIH_BRANCH: + { + const struct vkd3d_shader_branch *branch = &ins->declaration.branch; + vkd3d_string_buffer_printf(buffer, " %sl%u%s", compiler->colours.reg, branch->true_id, + compiler->colours.reset); + break; + } + case VKD3DSIH_DCL: case VKD3DSIH_DCL_UAV_TYPED: vkd3d_string_buffer_printf(buffer, "%s", compiler->colours.opcode); diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 4090293b1..25bcf1757 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -450,12 +450,14 @@ struct sm6_symbol
enum sm6_block_terminator_type { + TERMINATOR_UNCOND_BR, TERMINATOR_RET, };
struct sm6_block_terminator { enum sm6_block_terminator_type type; + const struct sm6_block *true_block; };
struct sm6_block @@ -464,6 +466,8 @@ struct sm6_block size_t instruction_capacity; size_t instruction_count;
+ unsigned int id; + struct sm6_block_terminator terminator; };
@@ -471,7 +475,8 @@ struct sm6_function { const struct sm6_value *declaration;
- struct sm6_block *blocks[1]; + struct sm6_block **blocks; + size_t block_capacity; size_t block_count;
size_t value_count; @@ -3182,6 +3187,45 @@ static void sm6_parser_emit_binop(struct sm6_parser *sm6, const struct dxil_reco } }
+static const struct sm6_block *sm6_function_get_block(const struct sm6_function *function, uint64_t index, + struct sm6_parser *sm6) +{ + if (index >= function->block_count) + { + WARN("Invalid code block index %#"PRIx64".\n", index); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid code block index %#"PRIx64" for a control flow instruction.", index); + return NULL; + } + return function->blocks[index]; +} + +static void sm6_parser_emit_br(struct sm6_parser *sm6, const struct dxil_record *record, + struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) +{ + if (record->operand_count != 1 && record->operand_count < 3) + { + WARN("Invalid operand count %u.\n", record->operand_count); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid operand count %u for a BR instruction.", record->operand_count); + return; + } + + if (record->operand_count == 1) + { + code_block->terminator.type = TERMINATOR_UNCOND_BR; + code_block->terminator.true_block = sm6_function_get_block(function, record->operands[0], sm6); + } + else + { + FIXME("Conditional branch is not implemented.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Conditional branch is not implemented."); + } + + ins->handler_idx = VKD3DSIH_NOP; +} + static void sm6_parser_emit_dx_cbuffer_load(struct sm6_parser *sm6, struct sm6_block *code_block, enum dx_intrinsic_opcode op, const struct sm6_value **operands, struct vkd3d_shader_instruction *ins) { @@ -4166,6 +4210,30 @@ static bool sm6_metadata_get_uint64_value(const struct sm6_parser *sm6, return true; }
+static bool sm6_function_blocks_reserve(struct sm6_function *function, unsigned int reserve) +{ + if (!vkd3d_array_reserve((void **)&function->blocks, &function->block_capacity, + reserve, sizeof(*function->blocks))) + { + ERR("Failed to allocate code block array.\n"); + return false; + } + return true; +} + +static struct sm6_block *sm6_function_create_block(struct sm6_function *function) +{ + struct sm6_block *block; + + if (!(block = sm6_block_create())) + return NULL; + + function->blocks[function->block_count++] = block; + block->id = function->block_count; + + return block; +} + static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const struct dxil_block *block, struct sm6_function *function) { @@ -4205,16 +4273,18 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const WARN("Function contains no blocks.\n"); return VKD3D_ERROR_INVALID_SHADER; } - if (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"); + if (!sm6_function_blocks_reserve(function, block_count)) return VKD3D_ERROR_OUT_OF_MEMORY; + + /* Pre-allocate all blocks to simplify instruction parsing. */ + for (i = 0; i < block_count; ++i) + { + if (!sm6_function_create_block(function)) + { + ERR("Failed to allocate code block.\n"); + return VKD3D_ERROR_OUT_OF_MEMORY; + } } function->block_count = block_count; code_block = function->blocks[0]; @@ -4256,6 +4326,10 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const case FUNC_CODE_INST_BINOP: sm6_parser_emit_binop(sm6, record, ins, dst); break; + case FUNC_CODE_INST_BR: + sm6_parser_emit_br(sm6, record, function, code_block, ins); + is_terminator = true; + break; case FUNC_CODE_INST_CALL: sm6_parser_emit_call(sm6, record, code_block, ins, dst); break; @@ -4315,8 +4389,19 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const
static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_parser *sm6) { + struct vkd3d_shader_instruction *ins; + switch (block->terminator.type) { + case TERMINATOR_UNCOND_BR: + { + if (!block->terminator.true_block) + return; + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_BRANCH); + ins->declaration.branch.true_id = block->terminator.true_block->id; + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); @@ -4328,21 +4413,6 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ } }
-static bool sm6_block_emit_instructions(struct sm6_block *block, struct sm6_parser *sm6) -{ - struct vkd3d_shader_instruction *ins = sm6_parser_require_space(sm6, block->instruction_count + 1); - - if (!ins) - return false; - - memcpy(ins, block->instructions, block->instruction_count * sizeof(*block->instructions)); - sm6->p.instructions.count += block->instruction_count; - - sm6_block_emit_terminator(block, sm6); - - return true; -} - static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const struct dxil_block *block, unsigned int level) { @@ -4398,6 +4468,43 @@ static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const st return VKD3D_OK; }
+static void sm6_parser_emit_label(struct sm6_parser *sm6, unsigned int label_id) +{ + struct vkd3d_shader_instruction *ins; + + ins = sm6_parser_require_space(sm6, 1); + vsir_instruction_init_label(ins, &sm6->p.location, label_id, &sm6->p); + ++sm6->p.instructions.count; +} + +static enum vkd3d_result sm6_function_emit_blocks(const struct sm6_function *function, struct sm6_parser *sm6) +{ + unsigned int i; + + for (i = 0; i < function->block_count; ++i) + { + const struct sm6_block *block = function->blocks[i]; + + /* Space for the label and terminator. */ + if (!sm6_parser_require_space(sm6, block->instruction_count + 2)) + { + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory emitting shader instructions."); + return VKD3D_ERROR_OUT_OF_MEMORY; + } + if (i) + sm6_parser_emit_label(sm6, block->id); + + memcpy(&sm6->p.instructions.elements[sm6->p.instructions.count], block->instructions, + block->instruction_count * sizeof(*block->instructions)); + sm6->p.instructions.count += block->instruction_count; + + sm6_block_emit_terminator(block, sm6); + } + + return VKD3D_OK; +} + static bool sm6_parser_allocate_named_metadata(struct sm6_parser *sm6) { struct dxil_block *block; @@ -5463,6 +5570,7 @@ static void sm6_functions_cleanup(struct sm6_function *functions, size_t count) { for (j = 0; j < functions[i].block_count; ++j) sm6_block_destroy(functions[i].blocks[j]); + vkd3d_free(functions[i].blocks); } vkd3d_free(functions); } @@ -5759,13 +5867,8 @@ static enum vkd3d_result sm6_parser_init(struct sm6_parser *sm6, const uint32_t return VKD3D_ERROR_INVALID_SHADER; }
- assert(sm6->function_count == 1); - if (!sm6_block_emit_instructions(fn->blocks[0], sm6)) - { - vkd3d_shader_error(message_context, &location, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, - "Out of memory emitting shader instructions."); - return VKD3D_ERROR_OUT_OF_MEMORY; - } + if ((ret = sm6_function_emit_blocks(fn, sm6)) < 0) + return ret;
dxil_block_destroy(&sm6->root_block);
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index acdb660ea..bf2b0d3d3 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -320,6 +320,26 @@ void vsir_instruction_init(struct vkd3d_shader_instruction *ins, const struct vk ins->handler_idx = handler_idx; }
+bool vsir_instruction_init_label(struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, + unsigned int label_id, void *parser) +{ + struct vkd3d_shader_src_param *src_param; + + if (!(src_param = shader_parser_get_src_params(parser, 1))) + return false; + + vsir_register_init(&src_param->reg, VKD3DSPR_LABEL, VKD3D_DATA_UINT, 1); + src_param->reg.idx[0].offset = label_id; + src_param->swizzle = 0; + src_param->modifiers = 0; + + vsir_instruction_init(ins, location, VKD3DSIH_LABEL); + ins->src = src_param; + ins->src_count = 1; + + return true; +} + static enum vkd3d_result instruction_array_flatten_hull_shader_phases(struct vkd3d_shader_instruction_array *src_instructions) { struct hull_flattener flattener = {*src_instructions}; diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index d3989672b..ae4a0ab7b 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -235,6 +235,7 @@ enum vkd3d_shader_opcode VKD3DSIH_BEM, VKD3DSIH_BFI, VKD3DSIH_BFREV, + VKD3DSIH_BRANCH, VKD3DSIH_BREAK, VKD3DSIH_BREAKC, VKD3DSIH_BREAKP, @@ -1101,6 +1102,11 @@ struct vkd3d_shader_location unsigned int line, column; };
+struct vkd3d_shader_branch +{ + unsigned int true_id; +}; + struct vkd3d_shader_instruction { struct vkd3d_shader_location location; @@ -1140,6 +1146,7 @@ struct vkd3d_shader_instruction struct vkd3d_shader_index_range index_range; struct vkd3d_shader_indexable_temp indexable_temp; struct vkd3d_shader_function_table_pointer fp; + struct vkd3d_shader_branch branch; } declaration; };
@@ -1155,6 +1162,8 @@ static inline bool vkd3d_shader_ver_le(const struct vkd3d_shader_version *v, uns
void vsir_instruction_init(struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, enum vkd3d_shader_opcode handler_idx); +bool vsir_instruction_init_label(struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, + unsigned int label_id, void *parser);
static inline bool vkd3d_shader_instruction_has_texel_offset(const struct vkd3d_shader_instruction *ins) {
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 15 +++++++-- libs/vkd3d-shader/dxil.c | 39 ++++++++++++++++++++++-- libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 3 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 48c44e89f..ea0662017 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -1650,8 +1650,19 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler, case VKD3DSIH_BRANCH: { const struct vkd3d_shader_branch *branch = &ins->declaration.branch; - vkd3d_string_buffer_printf(buffer, " %sl%u%s", compiler->colours.reg, branch->true_id, - compiler->colours.reset); + if (ins->src_count) + { + shader_dump_instruction_flags(compiler, ins); + vkd3d_string_buffer_printf(buffer, " "); + shader_dump_src_param(compiler, ins->src); + vkd3d_string_buffer_printf(buffer, " ? %sl%u%s : %sl%u%s", compiler->colours.reg, branch->true_id, + compiler->colours.reset, compiler->colours.reg, branch->false_id, compiler->colours.reset); + } + else + { + vkd3d_string_buffer_printf(buffer, " %sl%u%s", compiler->colours.reg, branch->true_id, + compiler->colours.reset); + } break; }
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 25bcf1757..7b79a464f 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -451,13 +451,16 @@ struct sm6_symbol enum sm6_block_terminator_type { TERMINATOR_UNCOND_BR, + TERMINATOR_COND_BR, TERMINATOR_RET, };
struct sm6_block_terminator { + struct vkd3d_shader_register conditional_reg; enum sm6_block_terminator_type type; const struct sm6_block *true_block; + const struct sm6_block *false_block; };
struct sm6_block @@ -3203,6 +3206,9 @@ static const struct sm6_block *sm6_function_get_block(const struct sm6_function static void sm6_parser_emit_br(struct sm6_parser *sm6, const struct dxil_record *record, struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) { + const struct sm6_value *value; + unsigned int i = 2; + if (record->operand_count != 1 && record->operand_count < 3) { WARN("Invalid operand count %u.\n", record->operand_count); @@ -3218,9 +3224,22 @@ static void sm6_parser_emit_br(struct sm6_parser *sm6, const struct dxil_record } else { - FIXME("Conditional branch is not implemented.\n"); - vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, - "Conditional branch is not implemented."); + if (!sm6->bool_type) + { + WARN("Bool type not found.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE, + "Module does not define a boolean type for conditions."); + return; + } + if (!(value = sm6_parser_get_value_by_ref(sm6, record, sm6->bool_type, &i)) + || !sm6_value_validate_is_bool(value, sm6)) + return; + dxil_record_validate_operand_max_count(record, i, sm6); + + code_block->terminator.type = TERMINATOR_COND_BR; + code_block->terminator.conditional_reg = value->u.reg; + code_block->terminator.true_block = sm6_function_get_block(function, record->operands[0], sm6); + code_block->terminator.false_block = sm6_function_get_block(function, record->operands[1], sm6); }
ins->handler_idx = VKD3DSIH_NOP; @@ -4389,6 +4408,7 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const
static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_parser *sm6) { + struct vkd3d_shader_src_param *src_param; struct vkd3d_shader_instruction *ins;
switch (block->terminator.type) @@ -4402,6 +4422,19 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ break; }
+ case TERMINATOR_COND_BR: + { + if (!block->terminator.true_block || !block->terminator.false_block) + return; + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_BRANCH); + src_param = instruction_src_params_alloc(ins, 1, sm6); + src_param_init(src_param); + src_param->reg = block->terminator.conditional_reg; + ins->declaration.branch.true_id = block->terminator.true_block->id; + ins->declaration.branch.false_id = block->terminator.false_block->id; + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index ae4a0ab7b..e95d47480 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1105,6 +1105,7 @@ struct vkd3d_shader_location struct vkd3d_shader_branch { unsigned int true_id; + unsigned int false_id; };
struct vkd3d_shader_instruction
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 13 ++ libs/vkd3d-shader/dxil.c | 222 ++++++++++++++++++++++- libs/vkd3d-shader/ir.c | 1 + libs/vkd3d-shader/spirv.c | 7 +- libs/vkd3d-shader/vkd3d_shader_private.h | 8 + 5 files changed, 246 insertions(+), 5 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index ea0662017..274c3552f 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -242,6 +242,7 @@ static const char * const shader_opcode_names[] = [VKD3DSIH_NRM ] = "nrm", [VKD3DSIH_OR ] = "or", [VKD3DSIH_PHASE ] = "phase", + [VKD3DSIH_PHI ] = "phi", [VKD3DSIH_POW ] = "pow", [VKD3DSIH_RCP ] = "rcp", [VKD3DSIH_REP ] = "rep", @@ -1876,6 +1877,18 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler, shader_print_bool_literal(compiler, " = ", ins->src[0].reg.u.immconst_uint[0], ""); break;
+ case VKD3DSIH_PHI: + vkd3d_string_buffer_printf(buffer, " "); + shader_dump_dst_param(compiler, ins->dst, false); + for (i = 0; i < ins->src_count; ++i) + { + vkd3d_string_buffer_printf(buffer, ", ["); + shader_dump_src_param(compiler, &ins->src[i]); + vkd3d_string_buffer_printf(buffer, ", %sl%u%s]", compiler->colours.reg, + ins->src[i].reg.block_id, compiler->colours.reset); + } + break; + default: shader_dump_instruction_flags(compiler, ins);
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 7b79a464f..61cb95d5b 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -448,6 +448,20 @@ struct sm6_symbol const char *name; };
+struct incoming_value +{ + const struct sm6_block *block; + struct vkd3d_shader_register reg; +}; + +struct sm6_phi +{ + struct vkd3d_shader_register reg; + struct incoming_value *incoming; + size_t incoming_capacity; + size_t incoming_count; +}; + enum sm6_block_terminator_type { TERMINATOR_UNCOND_BR, @@ -471,6 +485,10 @@ struct sm6_block
unsigned int id;
+ struct sm6_phi *phi; + size_t phi_capacity; + size_t phi_count; + struct sm6_block_terminator terminator; };
@@ -1925,6 +1943,11 @@ static bool sm6_value_is_icb(const struct sm6_value *value) return value->value_type == VALUE_TYPE_ICB; }
+static bool sm6_value_is_ssa(const struct sm6_value *value) +{ + return sm6_value_is_register(value) && register_is_ssa(&value->u.reg); +} + static inline unsigned int sm6_value_get_constant_uint(const struct sm6_value *value) { if (!sm6_value_is_constant(value)) @@ -3011,6 +3034,26 @@ static struct sm6_block *sm6_block_create() return block; }
+static struct sm6_phi *sm6_block_phi_require_space(struct sm6_block *block, struct sm6_parser *sm6) +{ + struct sm6_phi *phi; + + if (!vkd3d_array_reserve((void **)&block->phi, &block->phi_capacity, block->phi_count + 1, sizeof(*block->phi))) + { + ERR("Failed to allocate phi array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a phi instruction."); + return NULL; + } + phi = &block->phi[block->phi_count++]; + + phi->incoming = NULL; + phi->incoming_capacity = 0; + phi->incoming_count = 0; + + return phi; +} + static enum vkd3d_shader_opcode map_binary_op(uint64_t code, const struct sm6_type *type_a, const struct sm6_type *type_b, struct sm6_parser *sm6) { @@ -4127,6 +4170,72 @@ static void sm6_parser_emit_load(struct sm6_parser *sm6, const struct dxil_recor instruction_dst_param_init_ssa_scalar(ins, sm6); }
+static void sm6_parser_emit_phi(struct sm6_parser *sm6, const struct dxil_record *record, + struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins, + struct sm6_value *dst) +{ + const struct sm6_type *type; + struct sm6_phi *phi; + unsigned int i, j; + uint64_t src_idx; + + if (!(record->operand_count & 1)) + { + WARN("Invalid operand count %u.\n", record->operand_count); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid operand count %u for phi instruction.", record->operand_count); + return; + } + + if (!(type = sm6_parser_get_type(sm6, record->operands[0]))) + return; + if (!sm6_type_is_numeric(type)) + { + /* dxc doesn't seem to use buffer/resource read return types here. */ + FIXME("Only scalar numeric types are supported.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Result type class %u of a phi instruction is not scalar numeric.", type->class); + return; + } + + dst->type = type; + register_init_ssa_scalar(&dst->u.reg, type, sm6); + + if (!(phi = sm6_block_phi_require_space(code_block, sm6))) + return; + phi->reg = dst->u.reg; + phi->incoming_count = record->operand_count / 2u; + + if (!vkd3d_array_reserve((void **)&phi->incoming, &phi->incoming_capacity, phi->incoming_count, + sizeof(*phi->incoming))) + { + ERR("Failed to allocate phi incoming array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a phi incoming array."); + return; + } + + for (i = 1; i < record->operand_count; i += 2) + { + src_idx = sm6->value_count - decode_rotated_signed_value(record->operands[i]); + /* May be a forward reference. */ + if (src_idx >= sm6->cur_max_value) + { + FIXME("Invalid value index %"PRIu64".\n", src_idx); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "Invalid value index %"PRIu64" for a phi incoming value.", src_idx); + return; + } + + j = i / 2u; + /* Store the value index in the register for later resolution. */ + phi->incoming[j].reg.idx[0].offset = src_idx; + phi->incoming[j].block = sm6_function_get_block(function, record->operands[i + 1], sm6); + } + + ins->handler_idx = VKD3DSIH_NOP; +} + 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) { @@ -4253,6 +4362,45 @@ static struct sm6_block *sm6_function_create_block(struct sm6_function *function return block; }
+static enum vkd3d_result sm6_function_resolve_phi_incomings(const struct sm6_function *function, + struct sm6_parser *sm6) +{ + const struct sm6_block *block; + size_t i, j, block_idx; + + for (block_idx = 0; block_idx < function->block_count; ++block_idx) + { + block = function->blocks[block_idx]; + + for (i = 0; i < block->phi_count; ++i) + { + struct sm6_phi *phi = &block->phi[i]; + const struct sm6_value *src; + + for (j = 0; j < phi->incoming_count; ++j) + { + src = &sm6->values[phi->incoming[j].reg.idx[0].offset]; + if (!sm6_value_is_constant(src) && !sm6_value_is_undef(src) && !sm6_value_is_ssa(src)) + { + FIXME("PHI incoming value is not a constant or SSA register.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "A PHI incoming value is not a constant or SSA register."); + return VKD3D_ERROR_INVALID_SHADER; + } + if (src->u.reg.data_type != phi->reg.data_type) + { + WARN("Type mismatch.\n"); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "The type of a phi incoming value does not match the result type."); + } + phi->incoming[j].reg = src->u.reg; + } + } + } + + return VKD3D_OK; +} + static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const struct dxil_block *block, struct sm6_function *function) { @@ -4367,6 +4515,9 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const case FUNC_CODE_INST_LOAD: sm6_parser_emit_load(sm6, record, ins, dst); break; + case FUNC_CODE_INST_PHI: + sm6_parser_emit_phi(sm6, record, function, code_block, ins, dst); + break; case FUNC_CODE_INST_RET: sm6_parser_emit_ret(sm6, record, code_block, ins); is_terminator = true; @@ -4403,7 +4554,7 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const return VKD3D_ERROR_INVALID_SHADER; }
- return VKD3D_OK; + return sm6_function_resolve_phi_incomings(function, sm6); }
static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_parser *sm6) @@ -4446,6 +4597,45 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ } }
+static void sm6_block_emit_phi(const struct sm6_block *block, struct sm6_parser *sm6) +{ + struct vkd3d_shader_instruction *ins; + unsigned int i, j, incoming_count; + const struct sm6_phi *src_phi; + + sm6_parser_require_space(sm6, block->phi_count); + + for (i = 0; i < block->phi_count; ++i) + { + struct vkd3d_shader_src_param *src_params; + struct vkd3d_shader_dst_param *dst_param; + + src_phi = &block->phi[i]; + incoming_count = src_phi->incoming_count; + + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_PHI); + if (!(src_params = instruction_src_params_alloc(ins, incoming_count, sm6))) + return; + if (!(dst_param = instruction_dst_params_alloc(ins, 1, sm6))) + return; + + for (j = 0; j < incoming_count; ++j) + { + const struct sm6_block *incoming_block = src_phi->incoming[j].block; + + src_param_init(&src_params[j]); + src_params[j].reg = src_phi->incoming[j].reg; + if (incoming_block) + src_params[j].reg.block_id = incoming_block->id; + else + assert(sm6->p.failed); + } + + dst_param_init(dst_param); + dst_param->reg = src_phi->reg; + } +} + static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const struct dxil_block *block, unsigned int level) { @@ -4501,6 +4691,14 @@ static enum vkd3d_result sm6_parser_module_init(struct sm6_parser *sm6, const st return VKD3D_OK; }
+static void sm6_parser_emit_branch(struct sm6_parser *sm6, unsigned int block_idx) +{ + struct vkd3d_shader_instruction *ins; + + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_BRANCH); + ins->declaration.branch.true_id = block_idx; +} + static void sm6_parser_emit_label(struct sm6_parser *sm6, unsigned int label_id) { struct vkd3d_shader_instruction *ins; @@ -4518,15 +4716,21 @@ static enum vkd3d_result sm6_function_emit_blocks(const struct sm6_function *fun { const struct sm6_block *block = function->blocks[i];
- /* Space for the label and terminator. */ - if (!sm6_parser_require_space(sm6, block->instruction_count + 2)) + /* Space for a branch, label and terminator. */ + if (!sm6_parser_require_space(sm6, block->instruction_count + block->phi_count + 3)) { vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, "Out of memory emitting shader instructions."); return VKD3D_ERROR_OUT_OF_MEMORY; } - if (i) + if (function->block_count > 1) + { + /* The entry block cannot be a loop header. Emit branch and label in case there is a loop. */ + if (!i) + sm6_parser_emit_branch(sm6, block->id); sm6_parser_emit_label(sm6, block->id); + sm6_block_emit_phi(block, sm6); + }
memcpy(&sm6->p.instructions.elements[sm6->p.instructions.count], block->instructions, block->instruction_count * sizeof(*block->instructions)); @@ -5589,9 +5793,19 @@ static void sm6_symtab_cleanup(struct sm6_symbol *symbols, size_t count) vkd3d_free(symbols); }
+static void sm6_phi_destroy(struct sm6_phi *phi) +{ + vkd3d_free(phi->incoming); +} + static void sm6_block_destroy(struct sm6_block *block) { + unsigned int i; + vkd3d_free(block->instructions); + for (i = 0; i < block->phi_count; ++i) + sm6_phi_destroy(&block->phi[i]); + vkd3d_free(block->phi); vkd3d_free(block); }
diff --git a/libs/vkd3d-shader/ir.c b/libs/vkd3d-shader/ir.c index bf2b0d3d3..80f624a2a 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -310,6 +310,7 @@ void vsir_register_init(struct vkd3d_shader_register *reg, enum vkd3d_shader_reg reg->idx_count = idx_count; reg->dimension = VSIR_DIMENSION_SCALAR; reg->alignment = 0; + memset(®->u, 0, sizeof(reg->u)); }
void vsir_instruction_init(struct vkd3d_shader_instruction *ins, const struct vkd3d_shader_location *location, diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index be149a0cf..4f59cf3de 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -3785,7 +3785,12 @@ static uint32_t spirv_compiler_emit_load_ssa_reg(struct spirv_compiler *compiler
ssa = spirv_compiler_get_ssa_register_info(compiler, reg); val_id = ssa->id; - assert(val_id); + if (!val_id) + { + /* Should only be from a missing instruction implementation. */ + assert(compiler->failed); + return 0; + } assert(vkd3d_swizzle_is_scalar(swizzle));
if (reg->dimension == VSIR_DIMENSION_SCALAR) diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index e95d47480..6d0acc85d 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -433,6 +433,7 @@ enum vkd3d_shader_opcode VKD3DSIH_NRM, VKD3DSIH_OR, VKD3DSIH_PHASE, + VKD3DSIH_PHI, VKD3DSIH_POW, VKD3DSIH_RCP, VKD3DSIH_REP, @@ -830,6 +831,8 @@ struct vkd3d_shader_register enum vsir_dimension dimension; /* known address alignment for optimisation, or zero */ unsigned int alignment; + /* for phi incomings: id of a predecessor block dominated by the reg's origin block. */ + unsigned int block_id; union { DWORD immconst_uint[VKD3D_VEC4_SIZE]; @@ -1176,6 +1179,11 @@ static inline bool register_is_constant(const struct vkd3d_shader_register *reg) return (reg->type == VKD3DSPR_IMMCONST || reg->type == VKD3DSPR_IMMCONST64); }
+static inline bool register_is_ssa(const struct vkd3d_shader_register *reg) +{ + return reg->type == VKD3DSPR_SSA; +} + struct vkd3d_shader_param_node { struct vkd3d_shader_param_node *next;
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/dxil.c | 2 +- libs/vkd3d-shader/tpf.c | 2 +- libs/vkd3d-shader/vkd3d_shader_main.c | 15 +++++++-------- libs/vkd3d-shader/vkd3d_shader_private.h | 9 ++++----- 4 files changed, 13 insertions(+), 15 deletions(-)
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 61cb95d5b..89e3e0bab 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -2464,7 +2464,7 @@ static enum vkd3d_result value_allocate_constant_array(struct sm6_value *dst, co "Out of memory allocating an immediate constant buffer of count %u.", count); return VKD3D_ERROR_OUT_OF_MEMORY; } - if (!shader_instruction_array_add_icb(&sm6->p.instructions, icb)) + if (!shader_instruction_array_add_opaque_param(&sm6->p.instructions, icb)) { ERR("Failed to store icb object.\n"); vkd3d_free(icb); diff --git a/libs/vkd3d-shader/tpf.c b/libs/vkd3d-shader/tpf.c index 61d14e30d..0652b26fc 100644 --- a/libs/vkd3d-shader/tpf.c +++ b/libs/vkd3d-shader/tpf.c @@ -796,7 +796,7 @@ static void shader_sm4_read_shader_data(struct vkd3d_shader_instruction *ins, ui icb->component_count = VKD3D_VEC4_SIZE; icb->element_count = icb_size / VKD3D_VEC4_SIZE; memcpy(icb->data, tokens, sizeof(*tokens) * icb_size); - shader_instruction_array_add_icb(&priv->p.instructions, icb); + shader_instruction_array_add_opaque_param(&priv->p.instructions, icb); ins->declaration.icb = icb; }
diff --git a/libs/vkd3d-shader/vkd3d_shader_main.c b/libs/vkd3d-shader/vkd3d_shader_main.c index 77164c9c9..f2fccec66 100644 --- a/libs/vkd3d-shader/vkd3d_shader_main.c +++ b/libs/vkd3d-shader/vkd3d_shader_main.c @@ -2045,13 +2045,12 @@ bool shader_instruction_array_reserve(struct vkd3d_shader_instruction_array *ins return true; }
-bool shader_instruction_array_add_icb(struct vkd3d_shader_instruction_array *instructions, - struct vkd3d_shader_immediate_constant_buffer *icb) +bool shader_instruction_array_add_opaque_param(struct vkd3d_shader_instruction_array *instructions, void *param) { - if (!vkd3d_array_reserve((void **)&instructions->icbs, &instructions->icb_capacity, instructions->icb_count + 1, - sizeof(*instructions->icbs))) + if (!vkd3d_array_reserve((void **)&instructions->opaque_params, &instructions->opaque_param_capacity, + instructions->opaque_param_count + 1, sizeof(*instructions->opaque_params))) return false; - instructions->icbs[instructions->icb_count++] = icb; + instructions->opaque_params[instructions->opaque_param_count++] = param; return true; }
@@ -2140,9 +2139,9 @@ void shader_instruction_array_destroy(struct vkd3d_shader_instruction_array *ins vkd3d_free(instructions->elements); shader_param_allocator_destroy(&instructions->dst_params); shader_param_allocator_destroy(&instructions->src_params); - for (i = 0; i < instructions->icb_count; ++i) - vkd3d_free(instructions->icbs[i]); - vkd3d_free(instructions->icbs); + for (i = 0; i < instructions->opaque_param_count; ++i) + vkd3d_free(instructions->opaque_params[i]); + vkd3d_free(instructions->opaque_params); }
void vkd3d_shader_build_varying_map(const struct vkd3d_shader_signature *output_signature, diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 6d0acc85d..39401e0e7 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -1223,15 +1223,14 @@ struct vkd3d_shader_instruction_array
struct vkd3d_shader_param_allocator src_params; struct vkd3d_shader_param_allocator dst_params; - struct vkd3d_shader_immediate_constant_buffer **icbs; - size_t icb_capacity; - size_t icb_count; + void **opaque_params; + size_t opaque_param_capacity; + size_t opaque_param_count; };
bool shader_instruction_array_init(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve); bool shader_instruction_array_reserve(struct vkd3d_shader_instruction_array *instructions, unsigned int reserve); -bool shader_instruction_array_add_icb(struct vkd3d_shader_instruction_array *instructions, - struct vkd3d_shader_immediate_constant_buffer *icb); +bool shader_instruction_array_add_opaque_param(struct vkd3d_shader_instruction_array *instructions, void *param); bool shader_instruction_array_clone_instruction(struct vkd3d_shader_instruction_array *instructions, unsigned int dst, unsigned int src); void shader_instruction_array_destroy(struct vkd3d_shader_instruction_array *instructions);
From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 16 +++ libs/vkd3d-shader/dxil.c | 156 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 15 +++ 3 files changed, 187 insertions(+)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 274c3552f..385949e12 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -280,6 +280,7 @@ static const char * const shader_opcode_names[] = [VKD3DSIH_SUB ] = "sub", [VKD3DSIH_SWAPC ] = "swapc", [VKD3DSIH_SWITCH ] = "switch", + [VKD3DSIH_SWITCH_MONOLITHIC ] = "switch", [VKD3DSIH_SYNC ] = "sync", [VKD3DSIH_TEX ] = "texld", [VKD3DSIH_TEXBEM ] = "texbem", @@ -1889,6 +1890,21 @@ static void shader_dump_instruction(struct vkd3d_d3d_asm_compiler *compiler, } break;
+ case VKD3DSIH_SWITCH_MONOLITHIC: + { + const struct vkd3d_shader_switch *switch_ = &ins->declaration.switch_; + vkd3d_string_buffer_printf(buffer, " "); + shader_dump_src_param(compiler, ins->src); + for (i = 0; i < switch_->case_count; ++i) + { + vkd3d_string_buffer_printf(buffer, ", %"PRIu64": %sl%u%s", switch_->cases[i].value, + compiler->colours.reg, switch_->cases[i].block_id, compiler->colours.reset); + } + vkd3d_string_buffer_printf(buffer, ", default: %sl%u%s", compiler->colours.reg, switch_->default_id, + compiler->colours.reset); + break; + } + default: shader_dump_instruction_flags(compiler, ins);
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 89e3e0bab..0526444b5 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -466,15 +466,25 @@ enum sm6_block_terminator_type { TERMINATOR_UNCOND_BR, TERMINATOR_COND_BR, + TERMINATOR_SWITCH, TERMINATOR_RET, };
+struct terminator_case +{ + const struct sm6_block *block; + uint64_t value; + bool is_default; +}; + struct sm6_block_terminator { struct vkd3d_shader_register conditional_reg; enum sm6_block_terminator_type type; const struct sm6_block *true_block; const struct sm6_block *false_block; + struct terminator_case *cases; + unsigned int case_count; };
struct sm6_block @@ -1955,6 +1965,13 @@ static inline unsigned int sm6_value_get_constant_uint(const struct sm6_value *v return register_get_uint_value(&value->u.reg); }
+static uint64_t sm6_value_get_constant_uint64(const struct sm6_value *value) +{ + if (!sm6_value_is_constant(value)) + return UINT_MAX; + return register_get_uint64_value(&value->u.reg); +} + static unsigned int sm6_parser_alloc_ssa_id(struct sm6_parser *sm6) { return sm6->ssa_next_id++; @@ -4250,6 +4267,89 @@ static void sm6_parser_emit_ret(struct sm6_parser *sm6, const struct dxil_record ins->handler_idx = VKD3DSIH_NOP; }
+static void sm6_parser_emit_switch(struct sm6_parser *sm6, const struct dxil_record *record, + struct sm6_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins) +{ + struct sm6_block_terminator *terminator = &code_block->terminator; + const struct sm6_type *type; + const struct sm6_value *src; + unsigned int i = 1, j; + + if (record->operand_count < 3 || !(record->operand_count & 1)) + { + WARN("Invalid operand count %u.\n", record->operand_count); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND_COUNT, + "Invalid operand count %u for a switch instruction.", record->operand_count); + return; + } + + if (!(type = sm6_parser_get_type(sm6, record->operands[0]))) + return; + + if (!(src = sm6_parser_get_value_by_ref(sm6, record, type, &i)) + || !sm6_value_validate_is_register(src, sm6)) + return; + assert(i == 2); + + if (src->type != type) + { + WARN("Type mismatch.\n"); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "The type of a switch selector value does not match the selector type."); + } + if (!sm6_type_is_integer(type)) + { + WARN("Selector is not scalar integer.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "Selector type class %u of a switch instruction is not scalar integer.", type->class); + return; + } + + terminator->conditional_reg = src->u.reg; + terminator->type = TERMINATOR_SWITCH; + + terminator->case_count = record->operand_count / 2u; + if (!(terminator->cases = vkd3d_calloc(terminator->case_count, sizeof(*terminator->cases)))) + { + ERR("Failed to allocate case array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a switch case array."); + return; + } + + /* Executes 'operand_count / 2' times because operand_count is uneven. */ + for (; i < record->operand_count; i += 2) + { + j = i / 2u - 1; + terminator->cases[j].block = sm6_function_get_block(function, record->operands[i], sm6); + /* For structurisation it is convenient to store the default in the case array. */ + terminator->cases[j].is_default = !j; + } + + for (i = 3; i < record->operand_count; i += 2) + { + if (!(src = sm6_parser_get_value_safe(sm6, record->operands[i]))) + return; + + if (src->type != type) + { + WARN("Type mismatch.\n"); + vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "The type of a switch case value does not match the selector type."); + } + if (!sm6_value_is_constant(src)) + { + WARN("Case value is not a constant.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, + "A switch case value is not a constant."); + } + + terminator->cases[i / 2u].value = sm6_value_get_constant_uint64(src); + } + + ins->handler_idx = VKD3DSIH_NOP; +} + static void sm6_parser_emit_vselect(struct sm6_parser *sm6, const struct dxil_record *record, struct vkd3d_shader_instruction *ins, struct sm6_value *dst) { @@ -4523,6 +4623,10 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const is_terminator = true; ret_found = true; break; + case FUNC_CODE_INST_SWITCH: + sm6_parser_emit_switch(sm6, record, function, code_block, ins); + is_terminator = true; + break; case FUNC_CODE_INST_VSELECT: sm6_parser_emit_vselect(sm6, record, ins, dst); break; @@ -4561,6 +4665,7 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ { struct vkd3d_shader_src_param *src_param; struct vkd3d_shader_instruction *ins; + unsigned int i, count;
switch (block->terminator.type) { @@ -4586,6 +4691,56 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ break; }
+ case TERMINATOR_SWITCH: + { + struct vkd3d_shader_switch_case *cases; + + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_SWITCH_MONOLITHIC); + src_param = instruction_src_params_alloc(ins, 1, sm6); + src_param_init(src_param); + src_param->reg = block->terminator.conditional_reg; + + if (!(cases = vkd3d_calloc(block->terminator.case_count - 1, sizeof(*cases)))) + { + ERR("Failed to allocate case array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory allocating a switch case array."); + return; + } + + for (i = 0, count = 0; i < block->terminator.case_count; ++i) + { + const struct terminator_case *switch_case; + const struct sm6_block *case_block; + + switch_case = &block->terminator.cases[i]; + if (!(case_block = switch_case->block)) + { + assert(sm6->p.failed); + continue; + } + if (switch_case->is_default) + { + ins->declaration.switch_.default_id = case_block->id; + continue; + } + cases[count].value = switch_case->value; + cases[count++].block_id = case_block->id; + } + assert(ins->declaration.switch_.default_id); + + ins->declaration.switch_.cases = cases; + ins->declaration.switch_.case_count = count; + if (!shader_instruction_array_add_opaque_param(&sm6->p.instructions, cases)) + { + ERR("Failed to store case array.\n"); + vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, + "Out of memory storing a switch case array."); + vkd3d_free(cases); + } + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); @@ -5806,6 +5961,7 @@ static void sm6_block_destroy(struct sm6_block *block) for (i = 0; i < block->phi_count; ++i) sm6_phi_destroy(&block->phi[i]); vkd3d_free(block->phi); + vkd3d_free(block->terminator.cases); vkd3d_free(block); }
diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index 39401e0e7..add30331e 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -471,6 +471,7 @@ enum vkd3d_shader_opcode VKD3DSIH_SUB, VKD3DSIH_SWAPC, VKD3DSIH_SWITCH, + VKD3DSIH_SWITCH_MONOLITHIC, VKD3DSIH_SYNC, VKD3DSIH_TEX, VKD3DSIH_TEXBEM, @@ -1111,6 +1112,19 @@ struct vkd3d_shader_branch unsigned int false_id; };
+struct vkd3d_shader_switch_case +{ + uint64_t value; + unsigned int block_id; +}; + +struct vkd3d_shader_switch +{ + const struct vkd3d_shader_switch_case *cases; + unsigned int case_count; + unsigned int default_id; +}; + struct vkd3d_shader_instruction { struct vkd3d_shader_location location; @@ -1151,6 +1165,7 @@ struct vkd3d_shader_instruction struct vkd3d_shader_indexable_temp indexable_temp; struct vkd3d_shader_function_table_pointer fp; struct vkd3d_shader_branch branch; + struct vkd3d_shader_switch switch_; } declaration; };
On Fri Nov 24 15:05:40 2023 +0000, Conor McCarthy wrote:
The incoming block doesn't need to define the register's value; the value just needs to be definitely available there, i.e. the block of origin must dominate the incoming block. The structuriser will have helpers which can validate preds, but I think it's too much for this MR.
Ah, interesting, this was not clear to me. Of course that dominating block could have been executed more than once by the time we get to the current block. I suppose the value that is taken is the one resulting from the latest execution, right?
On Fri Nov 24 15:01:49 2023 +0000, Conor McCarthy wrote:
changed this line in [version 4 of the diff](/wine/vkd3d/-/merge_requests/491/diffs?diff_id=85836&start_sha=80d5e454bd58ec47fcdc6d56087886d1eac901b0#546f1cf6d37c971d8c949c878b1f0c7fb945472f_4453_4459)
Ok, could you then mention in a comment that `id` is supposed to be the array index plus one?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
return register_get_uint_value(&value->u.reg);
}
+static uint64_t sm6_value_get_constant_uint64(const struct sm6_value *value) +{
- if (!sm6_value_is_constant(value))
return UINT_MAX;
You might want to return `UINT64_MAX` here.