From c9f660ec5576af2b71f0558b9dacf52c65444cbe Mon Sep 17 00:00:00 2001 From: Conor McCarthy Date: Mon, 30 Sep 2024 13:10:26 +1000 Subject: [PATCH] vkd3d-shader/dxil: Emit switch instructions during parsing. --- libs/vkd3d-shader/dxil.c | 159 +++++++++++++-------------------------- 1 file changed, 54 insertions(+), 105 deletions(-) diff --git a/libs/vkd3d-shader/dxil.c b/libs/vkd3d-shader/dxil.c index 05c06570b..b523ea173 100644 --- a/libs/vkd3d-shader/dxil.c +++ b/libs/vkd3d-shader/dxil.c @@ -767,19 +767,9 @@ enum sm6_block_terminator_type 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; - struct terminator_case *cases; - unsigned int case_count; }; struct sm6_block @@ -7855,87 +7845,101 @@ static void sm6_parser_emit_store(struct sm6_parser *sm6, const struct dxil_reco dst_param->reg.idx_count = 1; } -static void sm6_parser_emit_switch(struct sm6_parser *sm6, const struct dxil_record *record, +static void sm6_parser_emit_switch(struct sm6_parser *dxil, 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; + struct vkd3d_shader_src_param *src_params; const struct sm6_type *type; const struct sm6_value *src; - unsigned int i = 1, j; + uint64_t case_value; + unsigned int i = 1; 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, + vkd3d_shader_parser_error(&dxil->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]))) + if (!(type = sm6_parser_get_type(dxil, record->operands[0]))) return; - if (!(src = sm6_parser_get_value_by_ref(sm6, record, type, &i)) - || !sm6_value_validate_is_register(src, sm6)) + if (!(src = sm6_parser_get_value_by_ref(dxil, record, type, &i)) + || !sm6_value_validate_is_register(src, dxil)) return; VKD3D_ASSERT(i == 2); if (src->type != type) - { - WARN("Type mismatch.\n"); - vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + vkd3d_shader_parser_warning(&dxil->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, + vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, "Selector type class %u of a switch instruction is not scalar integer.", type->class); return; } - vsir_register_from_dxil_value(&terminator->conditional_reg, src, 0, sm6); - terminator->type = TERMINATOR_SWITCH; + if (!sm6_function_validate_block_index(function, record->operands[2], dxil)) + return; - terminator->case_count = record->operand_count / 2u; - if (!(terminator->cases = vkd3d_calloc(terminator->case_count, sizeof(*terminator->cases)))) + vsir_instruction_init(ins, &dxil->p.location, VSIR_OP_SWITCH_MONOLITHIC); + if (!(src_params = instruction_src_params_alloc(ins, record->operand_count, dxil))) { - 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."); + vkd3d_shader_instruction_make_nop(ins); 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; - } + src_param_init_from_value(&src_params[0], src, 0, dxil); + /* Set the default block label id, 1-based. */ + vsir_src_param_init_label(&src_params[1], record->operands[2] + 1); + /* Set a zero merge block label id as a placeholder until it is set during + * the structurisation pass. */ + vsir_src_param_init_label(&src_params[2], 0); + + terminator->type = TERMINATOR_SWITCH; for (i = 3; i < record->operand_count; i += 2) { - if (!(src = sm6_parser_get_value_safe(sm6, record->operands[i]))) + if (!(src = sm6_parser_get_value_safe(dxil, record->operands[i]))) + { + vkd3d_shader_instruction_make_nop(ins); return; + } if (src->type != type) - { - WARN("Type mismatch.\n"); - vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + vkd3d_shader_parser_warning(&dxil->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, + vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND, "A switch case value is not a constant."); + + case_value = sm6_value_get_constant_uint64(src, dxil); + + /* Set the case constant value. 64-bit values are supported. */ + if (src_params[0].reg.data_type == VSIR_DATA_U64) + { + vsir_src_param_init(&src_params[i], VKD3DSPR_IMMCONST64, VSIR_DATA_U64, 0); + src_params[i].reg.u.immconst_u64[0] = case_value; + } + else + { + if (case_value > UINT_MAX) + vkd3d_shader_parser_warning(&dxil->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, + "Truncating 64-bit switch case value %"PRIx64" to 32 bits.", case_value); + vsir_src_param_init(&src_params[i], VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0); + src_params[i].reg.u.immconst_u32[0] = case_value; } - terminator->cases[i / 2u].value = sm6_value_get_constant_uint64(src, sm6); + if (!sm6_function_validate_block_index(function, record->operands[i + 1], dxil)) + { + vkd3d_shader_instruction_make_nop(ins); + return; + } + /* Set the case block label id, 1-based. */ + vsir_src_param_init_label(&src_params[i + 1], record->operands[i + 1] + 1); } - - ins->opcode = VSIR_OP_NOP; } static void sm6_parser_emit_vselect(struct sm6_parser *sm6, const struct dxil_record *record, @@ -8515,65 +8519,11 @@ 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_params; - struct vkd3d_shader_instruction *ins; - unsigned int i, count; - switch (block->terminator.type) { case TERMINATOR_BR: - /* Emitted during parsing. */ - break; - case TERMINATOR_SWITCH: - if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_SWITCH_MONOLITHIC))) - return; - if (!(src_params = instruction_src_params_alloc(ins, block->terminator.case_count * 2u + 1, sm6))) - { - vkd3d_shader_instruction_make_nop(ins); - return; - } - src_param_init(&src_params[0]); - src_params[0].reg = block->terminator.conditional_reg; - /* TODO: emit the 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)) - { - VKD3D_ASSERT(sm6->p.status < 0); - continue; - } - if (switch_case->is_default) - { - vsir_src_param_init_label(&src_params[1], case_block->id); - continue; - } - - if (src_params[0].reg.data_type == VSIR_DATA_U64) - { - vsir_src_param_init(&src_params[count], VKD3DSPR_IMMCONST64, VSIR_DATA_U64, 0); - src_params[count++].reg.u.immconst_u64[0] = switch_case->value; - } - else - { - if (switch_case->value > UINT_MAX) - { - WARN("Truncating 64-bit constant %"PRIx64".\n", switch_case->value); - vkd3d_shader_parser_warning(&sm6->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH, - "Truncating 64-bit switch case value %"PRIx64" to 32 bits.", switch_case->value); - } - vsir_src_param_init(&src_params[count], VKD3DSPR_IMMCONST, VSIR_DATA_U32, 0); - src_params[count++].reg.u.immconst_u32[0] = switch_case->value; - } - vsir_src_param_init_label(&src_params[count++], case_block->id); - } - + /* Emitted during parsing. */ break; case TERMINATOR_RET: @@ -10722,7 +10672,6 @@ 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); }