Conflicts with !450 but can probably go upstream before it since backend changes here are minimal.
-- v3: 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. vkd3d-shader/dxil: Handle the DXIL BR instruction conditional variant. vkd3d-shader/dxil: Handle the DXIL BR instruction unconditional variant. vkd3d-shader/dxil: Introduce a code block terminator struct.
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 | 8 ++ libs/vkd3d-shader/dxil.c | 169 ++++++++++++++++++++--- libs/vkd3d-shader/ir.c | 20 +++ libs/vkd3d-shader/vkd3d_shader_private.h | 9 ++ 4 files changed, 184 insertions(+), 22 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 82d1d71d9..60a6337f7 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,13 @@ 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, " l%u", branch->true_id); + 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..a13a268e7 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 synthetic_id; + struct sm6_block_terminator terminator; };
@@ -471,8 +475,10 @@ 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; + unsigned int block_current_id;
size_t value_count; }; @@ -3182,6 +3188,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 +4211,35 @@ 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 unsigned int sm6_function_alloc_block_id(struct sm6_function *function) +{ + return ++function->block_current_id; +} + +static struct sm6_block *sm6_function_create_block(struct sm6_function *function) +{ + struct sm6_block *block; + + if (!sm6_function_blocks_reserve(function, function->block_count + 1) || !(block = sm6_block_create())) + return NULL; + + block->synthetic_id = sm6_function_alloc_block_id(function); + function->blocks[function->block_count++] = block; + + 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 +4279,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 +4332,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 +4395,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->synthetic_id; + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); @@ -4328,19 +4419,14 @@ 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) +static void sm6_block_emit_instructions(const 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, @@ -4398,6 +4484,49 @@ 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 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 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]; + + if (!sm6_parser_require_space(sm6, block->instruction_count + !i + 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 (function->block_count > 1) + { + if (!i) + sm6_parser_emit_branch(sm6, block->synthetic_id); + sm6_parser_emit_label(sm6, block->synthetic_id); + } + sm6_block_emit_instructions(block, sm6); + } + + return VKD3D_OK; +} + static bool sm6_parser_allocate_named_metadata(struct sm6_parser *sm6) { struct dxil_block *block; @@ -5463,6 +5592,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 +5889,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 | 12 ++++++- libs/vkd3d-shader/dxil.c | 40 ++++++++++++++++++++++-- libs/vkd3d-shader/vkd3d_shader_private.h | 1 + 3 files changed, 49 insertions(+), 4 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 60a6337f7..61500dcc7 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -1650,7 +1650,17 @@ 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, " l%u", branch->true_id); + 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, " ? l%u : l%u", branch->true_id, branch->false_id); + } + else + { + vkd3d_string_buffer_printf(buffer, " l%u", branch->true_id); + } break; }
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index a13a268e7..6bfd40c15 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 @@ -3204,6 +3207,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); @@ -3219,9 +3225,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; @@ -4395,6 +4414,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) @@ -4408,6 +4428,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->synthetic_id; + ins->declaration.branch.false_id = block->terminator.false_block->synthetic_id; + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); @@ -4499,6 +4532,7 @@ static void sm6_parser_emit_branch(struct sm6_parser *sm6, unsigned int block_id
ins = sm6_parser_add_instruction(sm6, VKD3DSIH_BRANCH); ins->declaration.branch.true_id = block_idx; + ins->declaration.branch.false_id = 0; }
static enum vkd3d_result sm6_function_emit_blocks(const struct sm6_function *function, struct sm6_parser *sm6) 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 | 12 ++ libs/vkd3d-shader/dxil.c | 208 ++++++++++++++++++++++- libs/vkd3d-shader/ir.c | 1 + libs/vkd3d-shader/spirv.c | 7 +- libs/vkd3d-shader/vkd3d_shader_private.h | 8 + 5 files changed, 233 insertions(+), 3 deletions(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 61500dcc7..4934c7b38 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", @@ -1874,6 +1875,17 @@ 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]); + shader_print_uint_literal(compiler, ", l", ins->src[i].reg.u.block_id, "]"); + } + break; + default: shader_dump_instruction_flags(compiler, ins);
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 6bfd40c15..1bfd9d52d 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 synthetic_id;
+ struct sm6_phi *phi; + size_t phi_capacity; + size_t phi_count; + struct sm6_block_terminator terminator; };
@@ -1926,6 +1944,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)) @@ -3012,6 +3035,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) { @@ -4128,6 +4171,81 @@ 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; + const struct sm6_value *src; + 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]); + 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; + } + + /* May be a forward reference. */ + src = &sm6->values[src_idx]; + if (src->type && src->type != 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."); + } + + 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) { @@ -4259,6 +4377,39 @@ 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; + } + 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) { @@ -4373,6 +4524,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; @@ -4409,7 +4563,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) @@ -4452,6 +4606,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.u.block_id = incoming_block->synthetic_id; + else + assert(sm6->p.failed); + } + + dst_param_init(dst_param); + dst_param->reg = src_phi->reg; + } +} + static void sm6_block_emit_instructions(const struct sm6_block *block, struct sm6_parser *sm6) { struct vkd3d_shader_instruction *ins = sm6_parser_require_space(sm6, block->instruction_count + 1); @@ -4543,7 +4736,7 @@ static enum vkd3d_result sm6_function_emit_blocks(const struct sm6_function *fun { const struct sm6_block *block = function->blocks[i];
- if (!sm6_parser_require_space(sm6, block->instruction_count + !i + 3)) + if (!sm6_parser_require_space(sm6, block->instruction_count + block->phi_count + !i + 3)) { vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY, "Out of memory emitting shader instructions."); @@ -4554,6 +4747,7 @@ static enum vkd3d_result sm6_function_emit_blocks(const struct sm6_function *fun if (!i) sm6_parser_emit_branch(sm6, block->synthetic_id); sm6_parser_emit_label(sm6, block->synthetic_id); + sm6_block_emit_phi(block, sm6); } sm6_block_emit_instructions(block, sm6); } @@ -5612,9 +5806,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..537580945 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, @@ -837,6 +838,8 @@ struct vkd3d_shader_register uint64_t immconst_uint64[VKD3D_DVEC2_SIZE]; double immconst_double[VKD3D_DVEC2_SIZE]; unsigned fp_body_idx; + /* for phi incomings: a dominant block id. */ + unsigned int block_id; } u; };
@@ -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 1bfd9d52d..d29c62dea 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -2465,7 +2465,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 537580945..a973c717c 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 | 12 ++ libs/vkd3d-shader/dxil.c | 135 +++++++++++++++++++++++ libs/vkd3d-shader/vkd3d_shader_private.h | 15 +++ 3 files changed, 162 insertions(+)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index 4934c7b38..994cd396f 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", @@ -1886,6 +1887,17 @@ 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, ", %u: l%u", switch_->cases[i].value, switch_->cases[i].block_id); + vkd3d_string_buffer_printf(buffer, ", default: l%u", switch_->default_id); + break; + } + default: shader_dump_instruction_flags(compiler, ins);
diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index d29c62dea..29aa0b454 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; + uint32_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 @@ -4260,6 +4270,75 @@ 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); + + 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_uint(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) { @@ -4532,6 +4611,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; @@ -4570,6 +4653,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) { @@ -4595,6 +4679,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->synthetic_id; + continue; + } + cases[count].value = switch_case->value; + cases[count++].block_id = case_block->synthetic_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); @@ -5819,6 +5953,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 a973c717c..96926058c 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 +{ + unsigned int 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; };
PHI is broken here for constants. The block index needs to be either in a union with `alignment` or in `immconst_uint[1]`.
It's possible to use `src` for switch, and use `reg.idx[0]` and `[1]` for each case, or drop them in `immconst_uint`. That would have the advantage of using the existing parameter allocation/freeing mechanism, but it would be a bit ugly and inefficient.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- return true;
+}
+static unsigned int sm6_function_alloc_block_id(struct sm6_function *function) +{
- return ++function->block_current_id;
+}
+static struct sm6_block *sm6_function_create_block(struct sm6_function *function) +{
- struct sm6_block *block;
- if (!sm6_function_blocks_reserve(function, function->block_count + 1) || !(block = sm6_block_create()))
return NULL;
- block->synthetic_id = sm6_function_alloc_block_id(function);
Unless I'm missing something, `synthetic_id` is just the block position in the `blocks` array plus 1. Do we need to store it explicitly, or can we directly use the position in the array as block id?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
ERR("Failed to allocate code block array.\n");
return false;
- }
- return true;
+}
+static unsigned int sm6_function_alloc_block_id(struct sm6_function *function) +{
- return ++function->block_current_id;
+}
+static struct sm6_block *sm6_function_create_block(struct sm6_function *function) +{
- struct sm6_block *block;
- if (!sm6_function_blocks_reserve(function, function->block_count + 1) || !(block = sm6_block_create()))
Is this useful? You already reserved space for all the blocks just before calling this function in loop. Relatedly, you don't seem to need to keep a capacity count for the blocks, since everything is allocated at the beginning.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/d3d_asm.c:
switch (ins->handler_idx) {
case VKD3DSIH_BRANCH:
{
const struct vkd3d_shader_branch *branch = &ins->declaration.branch;
vkd3d_string_buffer_printf(buffer, " l%u", branch->true_id);
I don't fully know the high level choices around syntax coloring in D3D ASM, but I'd say that a label would warrant some coloring. I would even propose to add a new dedicated color for labels, since they're pretty unlike any other syntax element we already have, and I guess that at some point it can be useful to single them out quickly when reading code.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
+{
- struct vkd3d_shader_instruction *ins;
- ins = sm6_parser_add_instruction(sm6, VKD3DSIH_BRANCH);
- ins->declaration.branch.true_id = block_idx;
+}
+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];
if (!sm6_parser_require_space(sm6, block->instruction_count + !i + 3))
I'm not sure I understand the computation here. I guess `!i` is got the branch instruction, then there is the label, but where the remaining 2 comes from?
Also, why do you need to emit a branch? Aren't block already supposed to be terminated?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
- {
const struct sm6_block *block = function->blocks[i];
if (!sm6_parser_require_space(sm6, block->instruction_count + !i + 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 (function->block_count > 1)
{
if (!i)
sm6_parser_emit_branch(sm6, block->synthetic_id);
sm6_parser_emit_label(sm6, block->synthetic_id);
}
sm6_block_emit_instructions(block, sm6);
I guess this could be even inlined here, it's very short and it fits nicely into what `sm6_function_emit_blocks()` is doing.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/vkd3d_shader_private.h:
uint64_t immconst_uint64[VKD3D_DVEC2_SIZE]; double immconst_double[VKD3D_DVEC2_SIZE]; unsigned fp_body_idx;
/* for phi incomings: a dominant block id. */
I guess the correct word here is "precedessor" rather than "dominant", right? A predecessor block is not necessarily dominating the current one, nor is a dominating node necessarily a predecessor.
Also, this field is in a union with the immediate constant values, but as far as I can tell a PHI incoming parameter can be a constant. So wouldn't they be fighting for the union?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
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;
}
/* May be a forward reference. */
src = &sm6->values[src_idx];
if (src->type && src->type != 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.");
}
It's kind of annoying to just validate back references and not forward references. It already happens for `sm6_parser_get_value_idx_by_ref()`, since I don't think we ever go back checking that the type found in the operands array matches the type of its target. And I don't think we necessarily have to do something about it, since it's probably not a priority. But it irritates my need for uniformity.
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
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;
}
phi->incoming[j].reg = src->u.reg;
How hard would it be to validate that the incoming register belongs indeed to the stated block, and that that block is indeed a predecessor of the current one?
Giovanni Mascellani (@giomasce) commented about libs/vkd3d-shader/dxil.c:
+{
- 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;
Would it make sense to validate the type? I guess, for example, that int64 or non-scalar types are not allowed here.
On Fri Nov 24 12:44:24 2023 +0000, Conor McCarthy wrote:
It's possible to use `src` for switch, and use `reg.idx[0]` and `[1]` for each case, or drop them in `immconst_uint`. That would have the advantage of using the existing parameter allocation/freeing mechanism, but it would be a bit ugly and inefficient.
I don't think I have any problem with your current approach (renaming `icb` to `opaque` and using it). I wouldn't like to stretch the semantic of other data structures too much.
On Fri Nov 24 12:45:29 2023 +0000, Conor McCarthy wrote:
PHI is broken here for constants. The block index needs to be either in a union with `alignment` or in `immconst_uint[1]`.
I wouldn't try to be too smart about it. Just make it a structure member rather then keeping it inside the union.
Unless I'm missing something, `synthetic_id` is just the block position in the `blocks` array plus 1.
The structuriser allocates ids in a particular order not identical to array order, but your code doesn't necessarily need to do that, so yes this can be removed at least for now.
On Fri Nov 24 13:38:57 2023 +0000, Conor McCarthy wrote:
Unless I'm missing something, `synthetic_id` is just the block
position in the `blocks` array plus 1. The structuriser allocates ids in a particular order not identical to array order, but your code doesn't necessarily need to do that, so yes this can be removed at least for now.
Actually it's very convenient to have this so we can get the id from an individual block without needing the array.
On Fri Nov 24 12:41:19 2023 +0000, Giovanni Mascellani wrote:
Is this useful? You already reserved space for all the blocks just before calling this function in loop. Relatedly, you don't seem to need to keep a capacity count for the blocks, since everything is allocated at the beginning.
At the moment no. It can be added once the structuriser needs to create new blocks.