vkd3d-shader/dxil: Emit switch instructions during parsing.

This commit is contained in:
Conor McCarthy
2024-09-30 13:10:26 +10:00
committed by Henri Verbeet
parent 8fdc156adb
commit c9f660ec55
Notes: Henri Verbeet 2025-11-20 18:36:32 +01:00
Approved-by: Giovanni Mascellani (@giomasce)
Approved-by: Henri Verbeet (@hverbeet)
Merge-Request: https://gitlab.winehq.org/wine/vkd3d/-/merge_requests/1133

View File

@@ -767,19 +767,9 @@ enum sm6_block_terminator_type
TERMINATOR_RET, TERMINATOR_RET,
}; };
struct terminator_case
{
const struct sm6_block *block;
uint64_t value;
bool is_default;
};
struct sm6_block_terminator struct sm6_block_terminator
{ {
struct vkd3d_shader_register conditional_reg;
enum sm6_block_terminator_type type; enum sm6_block_terminator_type type;
struct terminator_case *cases;
unsigned int case_count;
}; };
struct sm6_block 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; 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_function *function, struct sm6_block *code_block, struct vkd3d_shader_instruction *ins)
{ {
struct sm6_block_terminator *terminator = &code_block->terminator; struct sm6_block_terminator *terminator = &code_block->terminator;
struct vkd3d_shader_src_param *src_params;
const struct sm6_type *type; const struct sm6_type *type;
const struct sm6_value *src; 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)) if (record->operand_count < 3 || !(record->operand_count & 1))
{ {
WARN("Invalid operand count %u.\n", record->operand_count); vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_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); "Invalid operand count %u for a switch instruction.", record->operand_count);
return; return;
} }
if (!(type = sm6_parser_get_type(sm6, record->operands[0]))) if (!(type = sm6_parser_get_type(dxil, record->operands[0])))
return; return;
if (!(src = sm6_parser_get_value_by_ref(sm6, record, type, &i)) if (!(src = sm6_parser_get_value_by_ref(dxil, record, type, &i))
|| !sm6_value_validate_is_register(src, sm6)) || !sm6_value_validate_is_register(src, dxil))
return; return;
VKD3D_ASSERT(i == 2); VKD3D_ASSERT(i == 2);
if (src->type != type) if (src->type != type)
{ vkd3d_shader_parser_warning(&dxil->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
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."); "The type of a switch selector value does not match the selector type.");
}
if (!sm6_type_is_integer(type)) if (!sm6_type_is_integer(type))
{ {
WARN("Selector is not scalar integer.\n"); vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
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); "Selector type class %u of a switch instruction is not scalar integer.", type->class);
return; return;
} }
vsir_register_from_dxil_value(&terminator->conditional_reg, src, 0, sm6); if (!sm6_function_validate_block_index(function, record->operands[2], dxil))
terminator->type = TERMINATOR_SWITCH; return;
terminator->case_count = record->operand_count / 2u; vsir_instruction_init(ins, &dxil->p.location, VSIR_OP_SWITCH_MONOLITHIC);
if (!(terminator->cases = vkd3d_calloc(terminator->case_count, sizeof(*terminator->cases)))) if (!(src_params = instruction_src_params_alloc(ins, record->operand_count, dxil)))
{ {
ERR("Failed to allocate case array.\n"); vkd3d_shader_instruction_make_nop(ins);
vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_OUT_OF_MEMORY,
"Out of memory allocating a switch case array.");
return; return;
} }
/* Executes 'operand_count / 2' times because operand_count is uneven. */ src_param_init_from_value(&src_params[0], src, 0, dxil);
for (; i < record->operand_count; i += 2) /* Set the default block label id, 1-based. */
{ vsir_src_param_init_label(&src_params[1], record->operands[2] + 1);
j = i / 2u - 1; /* Set a zero merge block label id as a placeholder until it is set during
terminator->cases[j].block = sm6_function_get_block(function, record->operands[i], sm6); * the structurisation pass. */
/* For structurisation it is convenient to store the default in the case array. */ vsir_src_param_init_label(&src_params[2], 0);
terminator->cases[j].is_default = !j;
} terminator->type = TERMINATOR_SWITCH;
for (i = 3; i < record->operand_count; i += 2) 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; return;
}
if (src->type != type) if (src->type != type)
{ vkd3d_shader_parser_warning(&dxil->p, VKD3D_SHADER_WARNING_DXIL_TYPE_MISMATCH,
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."); "The type of a switch case value does not match the selector type.");
}
if (!sm6_value_is_constant(src)) if (!sm6_value_is_constant(src))
{ vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_OPERAND,
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."); "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, 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) 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) switch (block->terminator.type)
{ {
case TERMINATOR_BR: case TERMINATOR_BR:
/* Emitted during parsing. */
break;
case TERMINATOR_SWITCH: case TERMINATOR_SWITCH:
if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_SWITCH_MONOLITHIC))) /* Emitted during parsing. */
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);
}
break; break;
case TERMINATOR_RET: case TERMINATOR_RET:
@@ -10722,7 +10672,6 @@ static void sm6_block_destroy(struct sm6_block *block)
for (i = 0; i < block->phi_count; ++i) for (i = 0; i < block->phi_count; ++i)
sm6_phi_destroy(&block->phi[i]); sm6_phi_destroy(&block->phi[i]);
vkd3d_free(block->phi); vkd3d_free(block->phi);
vkd3d_free(block->terminator.cases);
vkd3d_free(block); vkd3d_free(block);
} }