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

This commit is contained in:
Conor McCarthy
2024-09-30 11:54:56 +10:00
committed by Henri Verbeet
parent cfe9cd3794
commit 8fdc156adb
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

@@ -762,8 +762,7 @@ struct sm6_phi
enum sm6_block_terminator_type enum sm6_block_terminator_type
{ {
TERMINATOR_UNCOND_BR, TERMINATOR_BR,
TERMINATOR_COND_BR,
TERMINATOR_SWITCH, TERMINATOR_SWITCH,
TERMINATOR_RET, TERMINATOR_RET,
}; };
@@ -779,8 +778,6 @@ struct sm6_block_terminator
{ {
struct vkd3d_shader_register conditional_reg; struct vkd3d_shader_register conditional_reg;
enum sm6_block_terminator_type type; enum sm6_block_terminator_type type;
const struct sm6_block *true_block;
const struct sm6_block *false_block;
struct terminator_case *cases; struct terminator_case *cases;
unsigned int case_count; unsigned int case_count;
}; };
@@ -4741,59 +4738,86 @@ static void sm6_parser_emit_binop(struct sm6_parser *sm6, const struct dxil_reco
instruction_dst_param_init_ssa_scalar(ins, type_flags, sm6); instruction_dst_param_init_ssa_scalar(ins, type_flags, sm6);
} }
static const struct sm6_block *sm6_function_get_block(const struct sm6_function *function, uint64_t index, static bool sm6_function_validate_block_index(const struct sm6_function *function,
struct sm6_parser *sm6) uint64_t index, struct sm6_parser *dxil)
{ {
if (index >= function->block_count) if (index >= function->block_count)
{ {
WARN("Invalid code block index %#"PRIx64".\n", index); 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 code block index %#"PRIx64" for a control flow instruction.", index); "Invalid code block index %#"PRIx64" for a control flow instruction.", index);
return NULL; return false;
} }
return true;
}
static const struct sm6_block *sm6_function_get_block(const struct sm6_function *function,
uint64_t index, struct sm6_parser *dxil)
{
if (!sm6_function_validate_block_index(function, index, dxil))
return NULL;
return function->blocks[index]; return function->blocks[index];
} }
static void sm6_parser_emit_br(struct sm6_parser *sm6, const struct dxil_record *record, static void sm6_parser_emit_br(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 vkd3d_shader_src_param *src_params;
const struct sm6_value *value; const struct sm6_value *value;
unsigned int i = 2; unsigned int i = 2;
if (record->operand_count != 1 && record->operand_count < 3) if (record->operand_count != 1 && record->operand_count < 3)
{ {
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 branch instruction.", record->operand_count); "Invalid operand count %u for a branch instruction.", record->operand_count);
return; return;
} }
if (record->operand_count == 1) if (record->operand_count == 1)
{ {
code_block->terminator.type = TERMINATOR_UNCOND_BR; if (!sm6_function_validate_block_index(function, record->operands[0], dxil))
code_block->terminator.true_block = sm6_function_get_block(function, record->operands[0], sm6); return;
vsir_instruction_init(ins, &dxil->p.location, VSIR_OP_BRANCH);
if (!(src_params = instruction_src_params_alloc(ins, 1, dxil)))
{
vkd3d_shader_instruction_make_nop(ins);
return;
}
/* Label id is 1-based. */
vsir_src_param_init_label(&src_params[0], record->operands[0] + 1);
} }
else else
{ {
if (!sm6->bool_type) if (!dxil->bool_type)
{ {
WARN("Bool type not found.\n"); vkd3d_shader_parser_error(&dxil->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
vkd3d_shader_parser_error(&sm6->p, VKD3D_SHADER_ERROR_DXIL_INVALID_MODULE,
"Module does not define a boolean type for conditions."); "Module does not define a boolean type for conditions.");
return; return;
} }
if (!(value = sm6_parser_get_value_by_ref(sm6, record, sm6->bool_type, &i)) if (!(value = sm6_parser_get_value_by_ref(dxil, record, dxil->bool_type, &i))
|| !sm6_value_validate_is_bool(value, sm6)) || !sm6_value_validate_is_bool(value, dxil))
return; return;
dxil_record_validate_operand_max_count(record, i, sm6); dxil_record_validate_operand_max_count(record, i, dxil);
code_block->terminator.type = TERMINATOR_COND_BR; if (!sm6_function_validate_block_index(function, record->operands[0], dxil)
vsir_register_from_dxil_value(&code_block->terminator.conditional_reg, value, 0, sm6); || !sm6_function_validate_block_index(function, record->operands[1], dxil))
code_block->terminator.true_block = sm6_function_get_block(function, record->operands[0], sm6); return;
code_block->terminator.false_block = sm6_function_get_block(function, record->operands[1], sm6);
vsir_instruction_init(ins, &dxil->p.location, VSIR_OP_BRANCH);
if (!(src_params = instruction_src_params_alloc(ins, 3, dxil)))
{
vkd3d_shader_instruction_make_nop(ins);
return;
}
src_param_init_from_value(&src_params[0], value, 0, dxil);
/* Label id is 1-based. */
vsir_src_param_init_label(&src_params[1], record->operands[0] + 1);
vsir_src_param_init_label(&src_params[2], record->operands[1] + 1);
} }
ins->opcode = VSIR_OP_NOP; code_block->terminator.type = TERMINATOR_BR;
} }
static bool sm6_parser_emit_reg_composite_construct(struct sm6_parser *sm6, static bool sm6_parser_emit_reg_composite_construct(struct sm6_parser *sm6,
@@ -8458,13 +8482,17 @@ static enum vkd3d_result sm6_parser_function_init(struct sm6_parser *sm6, const
if (record->code == FUNC_CODE_INST_PHI) if (record->code == FUNC_CODE_INST_PHI)
code_block->phi[code_block->phi_count - 1].value = *dst; code_block->phi[code_block->phi_count - 1].value = *dst;
/* A vsir instruction can be emitted for a block terminator, so handle
* a possible instruction count increment before moving to the next
* code block. */
if (code_block)
code_block->instruction_count += ins->opcode != VSIR_OP_NOP;
if (is_terminator) if (is_terminator)
{ {
++block_idx; ++block_idx;
code_block = (block_idx < function->block_count) ? function->blocks[block_idx] : NULL; code_block = (block_idx < function->block_count) ? function->blocks[block_idx] : NULL;
} }
if (code_block)
code_block->instruction_count += ins->opcode != VSIR_OP_NOP;
if (dst->type && fwd_type && dst->type != fwd_type) if (dst->type && fwd_type && dst->type != fwd_type)
{ {
@@ -8493,33 +8521,8 @@ static void sm6_block_emit_terminator(const struct sm6_block *block, struct sm6_
switch (block->terminator.type) switch (block->terminator.type)
{ {
case TERMINATOR_UNCOND_BR: case TERMINATOR_BR:
if (!block->terminator.true_block) /* Emitted during parsing. */
return;
if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_BRANCH)))
return;
if (!(src_params = instruction_src_params_alloc(ins, 1, sm6)))
{
vkd3d_shader_instruction_make_nop(ins);
return;
}
vsir_src_param_init_label(&src_params[0], block->terminator.true_block->id);
break;
case TERMINATOR_COND_BR:
if (!block->terminator.true_block || !block->terminator.false_block)
return;
if (!(ins = sm6_parser_add_instruction(sm6, VSIR_OP_BRANCH)))
return;
if (!(src_params = instruction_src_params_alloc(ins, 3, sm6)))
{
vkd3d_shader_instruction_make_nop(ins);
return;
}
src_param_init(&src_params[0]);
src_params[0].reg = block->terminator.conditional_reg;
vsir_src_param_init_label(&src_params[1], block->terminator.true_block->id);
vsir_src_param_init_label(&src_params[2], block->terminator.false_block->id);
break; break;
case TERMINATOR_SWITCH: case TERMINATOR_SWITCH: