From: Giovanni Mascellani gmascellani@codeweavers.com
This is done by creating dummy blocks, which do not have any incoming branch.
SPIR-V specification doesn't allow instructions not belonging to any block. --- libs/vkd3d-shader/spirv.c | 76 ++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 32 deletions(-)
diff --git a/libs/vkd3d-shader/spirv.c b/libs/vkd3d-shader/spirv.c index 95f6914a..24d2ef7e 100644 --- a/libs/vkd3d-shader/spirv.c +++ b/libs/vkd3d-shader/spirv.c @@ -2222,7 +2222,6 @@ struct vkd3d_control_flow_info VKD3D_BLOCK_LOOP, VKD3D_BLOCK_SWITCH, } current_block; - bool inside_block; };
struct vkd3d_push_constant_buffer_binding @@ -2274,6 +2273,7 @@ struct spirv_compiler unsigned int branch_id; unsigned int loop_id; unsigned int switch_id; + unsigned int skip_id; unsigned int control_flow_depth; struct vkd3d_control_flow_info *control_flow_info; size_t control_flow_info_size; @@ -7280,7 +7280,7 @@ static struct vkd3d_control_flow_info *spirv_compiler_find_innermost_breakable_c static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *compiler, const struct vkd3d_shader_instruction *instruction) { - uint32_t loop_header_block_id, loop_body_block_id, continue_block_id; + uint32_t loop_header_block_id, loop_body_block_id, continue_block_id, skip_block_id; struct vkd3d_spirv_builder *builder = &compiler->spirv_builder; const struct vkd3d_shader_src_param *src = instruction->src; uint32_t merge_block_id, val_id, condition_id, true_label; @@ -7309,7 +7309,6 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c cf_info->u.if_.id = compiler->branch_id; cf_info->u.if_.merge_block_id = merge_block_id; cf_info->u.if_.else_block_id = 0; - cf_info->inside_block = true; cf_info->current_block = VKD3D_BLOCK_IF;
vkd3d_spirv_build_op_name(builder, merge_block_id, "branch%u_merge", compiler->branch_id); @@ -7321,8 +7320,7 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c assert(compiler->control_flow_depth); assert(cf_info->current_block == VKD3D_BLOCK_IF);
- if (cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, cf_info->u.if_.merge_block_id); + vkd3d_spirv_build_op_branch(builder, cf_info->u.if_.merge_block_id);
cf_info->u.if_.else_block_id = vkd3d_spirv_alloc_id(builder); vkd3d_spirv_as_op_branch_conditional(&builder->function_stream, @@ -7330,15 +7328,13 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c vkd3d_spirv_build_op_name(builder, cf_info->u.if_.else_block_id, "branch%u_false", cf_info->u.if_.id); vkd3d_spirv_build_op_label(builder, cf_info->u.if_.else_block_id); - cf_info->inside_block = true; break;
case VKD3DSIH_ENDIF: assert(compiler->control_flow_depth); assert(cf_info->current_block == VKD3D_BLOCK_IF);
- if (cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, cf_info->u.if_.merge_block_id); + vkd3d_spirv_build_op_branch(builder, cf_info->u.if_.merge_block_id);
vkd3d_spirv_build_op_label(builder, cf_info->u.if_.merge_block_id);
@@ -7365,7 +7361,6 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c cf_info->u.loop.continue_block_id = continue_block_id; cf_info->u.loop.merge_block_id = merge_block_id; cf_info->current_block = VKD3D_BLOCK_LOOP; - cf_info->inside_block = true;
vkd3d_spirv_build_op_name(builder, loop_header_block_id, "loop%u_header", compiler->loop_id); vkd3d_spirv_build_op_name(builder, loop_body_block_id, "loop%u_body", compiler->loop_id); @@ -7378,10 +7373,7 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c assert(compiler->control_flow_depth); assert(cf_info->current_block == VKD3D_BLOCK_LOOP);
- /* The loop block may have already been ended by an unconditional - * break instruction right before the end of the loop. */ - if (cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, cf_info->u.loop.continue_block_id); + vkd3d_spirv_build_op_branch(builder, cf_info->u.loop.continue_block_id);
vkd3d_spirv_build_op_label(builder, cf_info->u.loop.continue_block_id); vkd3d_spirv_build_op_branch(builder, cf_info->u.loop.header_block_id); @@ -7395,6 +7387,7 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c return VKD3D_ERROR_OUT_OF_MEMORY;
merge_block_id = vkd3d_spirv_alloc_id(builder); + skip_block_id = vkd3d_spirv_alloc_id(builder);
assert(src->reg.data_type == VKD3D_DATA_INT); val_id = spirv_compiler_emit_load_src(compiler, src, VKD3DSP_WRITEMASK_0); @@ -7409,12 +7402,15 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c cf_info->u.switch_.case_blocks_size = 0; cf_info->u.switch_.case_block_count = 0; cf_info->u.switch_.default_block_id = 0; - cf_info->inside_block = false; cf_info->current_block = VKD3D_BLOCK_SWITCH;
+ vkd3d_spirv_build_op_label(builder, skip_block_id); + vkd3d_spirv_build_op_name(builder, merge_block_id, "switch%u_merge", compiler->switch_id); + vkd3d_spirv_build_op_name(builder, skip_block_id, "skip%u", compiler->skip_id);
++compiler->switch_id; + ++compiler->skip_id;
if (!vkd3d_array_reserve((void **)&cf_info->u.switch_.case_blocks, &cf_info->u.switch_.case_blocks_size, 10, sizeof(*cf_info->u.switch_.case_blocks))) @@ -7425,7 +7421,10 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c case VKD3DSIH_ENDSWITCH: assert(compiler->control_flow_depth); assert(cf_info->current_block == VKD3D_BLOCK_SWITCH); - assert(!cf_info->inside_block); + + /* The current block will be ignored, but we still need to + * terminate it to generate valid SPIR-V. */ + vkd3d_spirv_build_op_return(builder);
if (!cf_info->u.switch_.default_block_id) cf_info->u.switch_.default_block_id = cf_info->u.switch_.merge_block_id; @@ -7466,15 +7465,13 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c return VKD3D_ERROR_OUT_OF_MEMORY;
label_id = vkd3d_spirv_alloc_id(builder); - if (cf_info->inside_block) /* fall-through */ - vkd3d_spirv_build_op_branch(builder, label_id); + vkd3d_spirv_build_op_branch(builder, label_id);
cf_info->u.switch_.case_blocks[2 * cf_info->u.switch_.case_block_count + 0] = value; cf_info->u.switch_.case_blocks[2 * cf_info->u.switch_.case_block_count + 1] = label_id; ++cf_info->u.switch_.case_block_count;
vkd3d_spirv_build_op_label(builder, label_id); - cf_info->inside_block = true; vkd3d_spirv_build_op_name(builder, label_id, "switch%u_case%u", cf_info->u.switch_.id, value); break; } @@ -7485,13 +7482,11 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c assert(!cf_info->u.switch_.default_block_id);
cf_info->u.switch_.default_block_id = vkd3d_spirv_alloc_id(builder); - if (cf_info->inside_block) /* fall-through */ - vkd3d_spirv_build_op_branch(builder, cf_info->u.switch_.default_block_id); + vkd3d_spirv_build_op_branch(builder, cf_info->u.switch_.default_block_id);
vkd3d_spirv_build_op_label(builder, cf_info->u.switch_.default_block_id); vkd3d_spirv_build_op_name(builder, cf_info->u.switch_.default_block_id, "switch%u_default", cf_info->u.switch_.id); - cf_info->inside_block = true; break;
case VKD3DSIH_BREAK: @@ -7506,19 +7501,19 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c return VKD3D_ERROR_INVALID_SHADER; }
+ skip_block_id = vkd3d_spirv_alloc_id(builder); + if (breakable_cf_info->current_block == VKD3D_BLOCK_LOOP) - { vkd3d_spirv_build_op_branch(builder, breakable_cf_info->u.loop.merge_block_id); - } else if (breakable_cf_info->current_block == VKD3D_BLOCK_SWITCH) - { - /* The current case block may have already been ended by an - * unconditional continue instruction. */ - if (breakable_cf_info->inside_block) - vkd3d_spirv_build_op_branch(builder, breakable_cf_info->u.switch_.merge_block_id); - } + vkd3d_spirv_build_op_branch(builder, breakable_cf_info->u.switch_.merge_block_id); + + vkd3d_spirv_build_op_label(builder, skip_block_id); + + vkd3d_spirv_build_op_name(builder, skip_block_id, "skip%u", compiler->skip_id); + + ++compiler->skip_id;
- cf_info->inside_block = false; break; }
@@ -7552,9 +7547,16 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c return VKD3D_ERROR_INVALID_SHADER; }
+ skip_block_id = vkd3d_spirv_alloc_id(builder); + vkd3d_spirv_build_op_branch(builder, loop_cf_info->u.loop.continue_block_id);
- cf_info->inside_block = false; + vkd3d_spirv_build_op_label(builder, skip_block_id); + + vkd3d_spirv_build_op_name(builder, skip_block_id, "skip%u", compiler->skip_id); + + ++compiler->skip_id; + break; }
@@ -7578,9 +7580,19 @@ static int spirv_compiler_emit_control_flow_instruction(struct spirv_compiler *c spirv_compiler_emit_return(compiler, instruction);
if (cf_info) - cf_info->inside_block = false; + { + skip_block_id = vkd3d_spirv_alloc_id(builder); + + vkd3d_spirv_build_op_label(builder, skip_block_id); + + vkd3d_spirv_build_op_name(builder, skip_block_id, "skip%u", compiler->skip_id); + + ++compiler->skip_id; + } else + { compiler->main_block_open = false; + } break;
case VKD3DSIH_RETP: