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 04e58a5d6..d257925d6 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 @@ -1956,6 +1966,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 UINT64_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++; @@ -4251,6 +4268,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) { @@ -4525,6 +4625,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; @@ -4563,6 +4667,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) { @@ -4588,6 +4693,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); @@ -5808,6 +5963,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; };