From: Conor McCarthy cmccarthy@codeweavers.com
--- libs/vkd3d-shader/d3d_asm.c | 13 +++ libs/vkd3d-shader/dxil.c | 140 +++++++++++++++++++++++ libs/vkd3d-shader/ir.c | 2 +- libs/vkd3d-shader/vkd3d_shader_private.h | 2 + 4 files changed, 156 insertions(+), 1 deletion(-)
diff --git a/libs/vkd3d-shader/d3d_asm.c b/libs/vkd3d-shader/d3d_asm.c index fe3133a1a..8e9a394fb 100644 --- a/libs/vkd3d-shader/d3d_asm.c +++ b/libs/vkd3d-shader/d3d_asm.c @@ -830,6 +830,13 @@ static void shader_print_uint_literal(struct vkd3d_d3d_asm_compiler *compiler, prefix, compiler->colours.literal, i, compiler->colours.reset, suffix); }
+static void shader_print_uint64_literal(struct vkd3d_d3d_asm_compiler *compiler, + const char *prefix, uint64_t i, const char *suffix) +{ + vkd3d_string_buffer_printf(&compiler->buffer, "%s%s%"PRIu64"%s%s", + prefix, compiler->colours.literal, i, compiler->colours.reset, suffix); +} + static void shader_print_hex_literal(struct vkd3d_d3d_asm_compiler *compiler, const char *prefix, unsigned int i, const char *suffix) { @@ -1175,6 +1182,12 @@ static void shader_dump_register(struct vkd3d_d3d_asm_compiler *compiler, const if (reg->dimension == VSIR_DIMENSION_VEC4) shader_print_double_literal(compiler, ", ", reg->u.immconst_f64[1], ""); } + else if (reg->data_type == VKD3D_DATA_UINT64) + { + shader_print_uint64_literal(compiler, "", reg->u.immconst_u64[0], ""); + if (reg->dimension == VSIR_DIMENSION_VEC4) + shader_print_uint64_literal(compiler, "", reg->u.immconst_u64[1], ""); + } else { shader_addline(buffer, "<unhandled data type %#x>", reg->data_type); diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 8d43e6364..e8fe844c4 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -490,15 +490,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 @@ -1998,6 +2008,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++; @@ -4537,6 +4554,89 @@ static void sm6_parser_emit_store(struct sm6_parser *sm6, const struct dxil_reco dst_param->reg.alignment = alignment; }
+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) { @@ -5018,6 +5118,10 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const case FUNC_CODE_INST_STORE: sm6_parser_emit_store(sm6, record, ins, dst); 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; @@ -5059,6 +5163,7 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ { struct vkd3d_shader_src_param *src_params; struct vkd3d_shader_instruction *ins; + unsigned int i, count;
switch (block->terminator.type) { @@ -5087,6 +5192,40 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_ break; }
+ case TERMINATOR_SWITCH: + { + ins = sm6_parser_add_instruction(sm6, VKD3DSIH_SWITCH_MONOLITHIC); + if (!(src_params = instruction_src_params_alloc(ins, block->terminator.case_count * 2u + 1, sm6))) + return; + src_param_init(&src_params[0]); + src_params[0].reg = block->terminator.conditional_reg; + /* TODO: merge block id. */ + vsir_src_param_init_label(&src_params[2], 0); + + for (i = 0, count = 3; 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) + { + vsir_src_param_init_label(&src_params[1], case_block->id); + continue; + } + vsir_src_param_init(&src_params[count], VKD3DSPR_IMMCONST64, VKD3D_DATA_UINT64, 0); + src_params[count++].reg.u.immconst_u64[0] = switch_case->value; + vsir_src_param_init_label(&src_params[count++], case_block->id); + } + + break; + } + case TERMINATOR_RET: { sm6_parser_add_instruction(sm6, VKD3DSIH_RET); @@ -6299,6 +6438,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/ir.c b/libs/vkd3d-shader/ir.c index 5e05a0bb4..22cf495c2 100644 --- a/libs/vkd3d-shader/ir.c +++ b/libs/vkd3d-shader/ir.c @@ -312,7 +312,7 @@ void vsir_register_init(struct vkd3d_shader_register *reg, enum vkd3d_shader_reg reg->alignment = 0; }
-static void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d_shader_register_type reg_type, +void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d_shader_register_type reg_type, enum vkd3d_data_type data_type, unsigned int idx_count) { vsir_register_init(¶m->reg, reg_type, data_type, idx_count); diff --git a/libs/vkd3d-shader/vkd3d_shader_private.h b/libs/vkd3d-shader/vkd3d_shader_private.h index d27490432..bb1ad6f50 100644 --- a/libs/vkd3d-shader/vkd3d_shader_private.h +++ b/libs/vkd3d-shader/vkd3d_shader_private.h @@ -885,6 +885,8 @@ struct vkd3d_shader_src_param enum vkd3d_shader_src_modifier modifiers; };
+void vsir_src_param_init(struct vkd3d_shader_src_param *param, enum vkd3d_shader_register_type reg_type, + enum vkd3d_data_type data_type, unsigned int idx_count); void vsir_src_param_init_label(struct vkd3d_shader_src_param *param, unsigned int label_id);
struct vkd3d_shader_index_range