From 2714f5410c50a882c5653485cf17b4f96378cf17 Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sun, 7 Nov 2021 12:37:23 +0100 Subject: [PATCH] CFG: Handle degenerate selection constructs. Apparently, it's legal to use a selection construct where both paths branch to same location, but a different merge point is used. This breaks many assumptions the variable scope analyzer makes. The only logical way to generate code for this scenario is to treat the selection construct as a trivial switch construct with only a default case. --- .../degenerate-selection-constructs.asm.frag | 118 +++++++ .../degenerate-selection-constructs.asm.frag | 310 ++++++++++++++++++ spirv_parser.cpp | 43 +++ 3 files changed, 471 insertions(+) create mode 100644 reference/shaders-no-opt/asm/degenerate-selection-constructs.asm.frag create mode 100644 shaders-no-opt/asm/degenerate-selection-constructs.asm.frag diff --git a/reference/shaders-no-opt/asm/degenerate-selection-constructs.asm.frag b/reference/shaders-no-opt/asm/degenerate-selection-constructs.asm.frag new file mode 100644 index 00000000..eb1cf0ca --- /dev/null +++ b/reference/shaders-no-opt/asm/degenerate-selection-constructs.asm.frag @@ -0,0 +1,118 @@ +#version 320 es +precision mediump float; +precision highp int; + +layout(binding = 1, std140) uniform buf1 +{ + highp vec2 resolution; +} _9; + +layout(binding = 0, std140) uniform buf0 +{ + highp vec2 injectionSwitch; +} _13; + +layout(location = 0) out highp vec4 _GLF_color; + +bool checkSwap(highp float a, highp float b) +{ + bool _153 = gl_FragCoord.y < (_9.resolution.y / 2.0); + highp float _160; + if (_153) + { + _160 = a; + } + else + { + highp float _159 = 0.0; + _160 = _159; + } + bool _147; + do + { + highp float _168; + if (_153) + { + _168 = b; + } + else + { + highp float _167 = 0.0; + _168 = _167; + } + if (_153) + { + _147 = _160 > _168; + } + if (true) + { + break; + } + else + { + break; + } + } while(false); + highp float _180; + if (_153) + { + highp float _179 = 0.0; + _180 = _179; + } + else + { + _180 = a; + } + highp float _186; + if (_153) + { + highp float _185 = 0.0; + _186 = _185; + } + else + { + _186 = b; + } + if (!_153) + { + _147 = _180 < _186; + } + return _147; +} + +void main() +{ + highp float data[10]; + for (int i = 0; i < 10; i++) + { + data[i] = float(10 - i) * _13.injectionSwitch.y; + } + for (int i_1 = 0; i_1 < 9; i_1++) + { + for (int j = 0; j < 10; j++) + { + if (j < (i_1 + 1)) + { + continue; + } + highp float param = data[i_1]; + highp float param_1 = data[j]; + bool doSwap = checkSwap(param, param_1); + if (doSwap) + { + highp float temp = data[i_1]; + data[i_1] = data[j]; + data[j] = temp; + } + } + } + if (gl_FragCoord.x < (_9.resolution.x / 2.0)) + { + _GLF_color = vec4(data[0] / 10.0, data[5] / 10.0, data[9] / 10.0, 1.0); + } + else + { + _GLF_color = vec4(data[5] / 10.0, data[9] / 10.0, data[0] / 10.0, 1.0); + } +} + diff --git a/shaders-no-opt/asm/degenerate-selection-constructs.asm.frag b/shaders-no-opt/asm/degenerate-selection-constructs.asm.frag new file mode 100644 index 00000000..eac8fadf --- /dev/null +++ b/shaders-no-opt/asm/degenerate-selection-constructs.asm.frag @@ -0,0 +1,310 @@ +; SPIR-V +; Version: 1.0 +; Generator: Khronos SPIR-V Tools Assembler; 0 +; Bound: 816 +; Schema: 0 + OpCapability Shader + %1 = OpExtInstImport "GLSL.std.450" + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %gl_FragCoord %_GLF_color + OpExecutionMode %main OriginUpperLeft + OpSource ESSL 320 + OpName %main "main" + OpName %checkSwap_f1_f1_ "checkSwap(f1;f1;" + OpName %a "a" + OpName %b "b" + OpName %gl_FragCoord "gl_FragCoord" + OpName %buf1 "buf1" + OpMemberName %buf1 0 "resolution" + OpName %_ "" + OpName %i "i" + OpName %data "data" + OpName %buf0 "buf0" + OpMemberName %buf0 0 "injectionSwitch" + OpName %__0 "" + OpName %i_0 "i" + OpName %j "j" + OpName %doSwap "doSwap" + OpName %param "param" + OpName %param_0 "param" + OpName %temp "temp" + OpName %_GLF_color "_GLF_color" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpMemberDecorate %buf1 0 Offset 0 + OpDecorate %buf1 Block + OpDecorate %_ DescriptorSet 0 + OpDecorate %_ Binding 1 + OpMemberDecorate %buf0 0 Offset 0 + OpDecorate %buf0 Block + OpDecorate %__0 DescriptorSet 0 + OpDecorate %__0 Binding 0 + OpDecorate %_GLF_color Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 +%_ptr_Function_float = OpTypePointer Function %float + %bool = OpTypeBool + %9 = OpTypeFunction %bool %_ptr_Function_float %_ptr_Function_float + %v4float = OpTypeVector %float 4 +%_ptr_Input_v4float = OpTypePointer Input %v4float +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input + %uint = OpTypeInt 32 0 + %uint_1 = OpConstant %uint 1 +%_ptr_Input_float = OpTypePointer Input %float + %v2float = OpTypeVector %float 2 + %buf1 = OpTypeStruct %v2float +%_ptr_Uniform_buf1 = OpTypePointer Uniform %buf1 + %_ = OpVariable %_ptr_Uniform_buf1 Uniform + %int = OpTypeInt 32 1 + %int_0 = OpConstant %int 0 +%_ptr_Uniform_float = OpTypePointer Uniform %float + %float_2 = OpConstant %float 2 +%_ptr_Function_bool = OpTypePointer Function %bool +%_ptr_Function_int = OpTypePointer Function %int + %int_10 = OpConstant %int 10 + %uint_10 = OpConstant %uint 10 +%_arr_float_uint_10 = OpTypeArray %float %uint_10 +%_ptr_Function__arr_float_uint_10 = OpTypePointer Function %_arr_float_uint_10 + %buf0 = OpTypeStruct %v2float +%_ptr_Uniform_buf0 = OpTypePointer Uniform %buf0 + %__0 = OpVariable %_ptr_Uniform_buf0 Uniform + %int_1 = OpConstant %int 1 + %int_9 = OpConstant %int 9 + %uint_0 = OpConstant %uint 0 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %_GLF_color = OpVariable %_ptr_Output_v4float Output + %float_10 = OpConstant %float 10 + %int_5 = OpConstant %int 5 + %float_1 = OpConstant %float 1 + %float_0 = OpConstant %float 0 + %false = OpConstantFalse %bool + %true = OpConstantTrue %bool + %main = OpFunction %void None %3 + %5 = OpLabel + %i = OpVariable %_ptr_Function_int Function + %data = OpVariable %_ptr_Function__arr_float_uint_10 Function + %i_0 = OpVariable %_ptr_Function_int Function + %j = OpVariable %_ptr_Function_int Function + %doSwap = OpVariable %_ptr_Function_bool Function + %param = OpVariable %_ptr_Function_float Function + %param_0 = OpVariable %_ptr_Function_float Function + %temp = OpVariable %_ptr_Function_float Function + OpStore %i %int_0 + OpBranch %50 + %50 = OpLabel + OpLoopMerge %52 %53 None + OpBranch %54 + %54 = OpLabel + %55 = OpLoad %int %i + %57 = OpSLessThan %bool %55 %int_10 + OpBranchConditional %57 %51 %52 + %51 = OpLabel + %62 = OpLoad %int %i + %63 = OpLoad %int %i + %64 = OpISub %int %int_10 %63 + %65 = OpConvertSToF %float %64 + %69 = OpAccessChain %_ptr_Uniform_float %__0 %int_0 %uint_1 + %70 = OpLoad %float %69 + %71 = OpFMul %float %65 %70 + %72 = OpAccessChain %_ptr_Function_float %data %62 + OpStore %72 %71 + OpBranch %53 + %53 = OpLabel + %73 = OpLoad %int %i + %75 = OpIAdd %int %73 %int_1 + OpStore %i %75 + OpBranch %50 + %52 = OpLabel + OpStore %i_0 %int_0 + OpBranch %77 + %77 = OpLabel + OpLoopMerge %79 %80 None + OpBranch %81 + %81 = OpLabel + %82 = OpLoad %int %i_0 + %84 = OpSLessThan %bool %82 %int_9 + OpBranchConditional %84 %78 %79 + %78 = OpLabel + OpStore %j %int_0 + OpBranch %86 + %86 = OpLabel + OpLoopMerge %88 %89 None + OpBranch %90 + %90 = OpLabel + %91 = OpLoad %int %j + %92 = OpSLessThan %bool %91 %int_10 + OpBranchConditional %92 %87 %88 + %87 = OpLabel + %93 = OpLoad %int %j + %94 = OpLoad %int %i_0 + %95 = OpIAdd %int %94 %int_1 + %96 = OpSLessThan %bool %93 %95 + OpSelectionMerge %98 None + OpBranchConditional %96 %97 %98 + %97 = OpLabel + OpBranch %89 + %98 = OpLabel + %101 = OpLoad %int %i_0 + %102 = OpLoad %int %j + %104 = OpAccessChain %_ptr_Function_float %data %101 + %105 = OpLoad %float %104 + OpStore %param %105 + %107 = OpAccessChain %_ptr_Function_float %data %102 + %108 = OpLoad %float %107 + OpStore %param_0 %108 + %109 = OpFunctionCall %bool %checkSwap_f1_f1_ %param %param_0 + OpStore %doSwap %109 + %110 = OpLoad %bool %doSwap + OpSelectionMerge %112 None + OpBranchConditional %110 %111 %112 + %111 = OpLabel + %114 = OpLoad %int %i_0 + %115 = OpAccessChain %_ptr_Function_float %data %114 + %116 = OpLoad %float %115 + OpStore %temp %116 + %117 = OpLoad %int %i_0 + %118 = OpLoad %int %j + %119 = OpAccessChain %_ptr_Function_float %data %118 + %120 = OpLoad %float %119 + %121 = OpAccessChain %_ptr_Function_float %data %117 + OpStore %121 %120 + %122 = OpLoad %int %j + %123 = OpLoad %float %temp + %124 = OpAccessChain %_ptr_Function_float %data %122 + OpStore %124 %123 + OpBranch %112 + %112 = OpLabel + OpBranch %89 + %89 = OpLabel + %125 = OpLoad %int %j + %126 = OpIAdd %int %125 %int_1 + OpStore %j %126 + OpBranch %86 + %88 = OpLabel + OpBranch %80 + %80 = OpLabel + %127 = OpLoad %int %i_0 + %128 = OpIAdd %int %127 %int_1 + OpStore %i_0 %128 + OpBranch %77 + %79 = OpLabel + %130 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_0 + %131 = OpLoad %float %130 + %132 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %uint_0 + %133 = OpLoad %float %132 + %134 = OpFDiv %float %133 %float_2 + %135 = OpFOrdLessThan %bool %131 %134 + OpSelectionMerge %137 None + OpBranchConditional %135 %136 %153 + %136 = OpLabel + %140 = OpAccessChain %_ptr_Function_float %data %int_0 + %141 = OpLoad %float %140 + %143 = OpFDiv %float %141 %float_10 + %145 = OpAccessChain %_ptr_Function_float %data %int_5 + %146 = OpLoad %float %145 + %147 = OpFDiv %float %146 %float_10 + %148 = OpAccessChain %_ptr_Function_float %data %int_9 + %149 = OpLoad %float %148 + %150 = OpFDiv %float %149 %float_10 + %152 = OpCompositeConstruct %v4float %143 %147 %150 %float_1 + OpStore %_GLF_color %152 + OpBranch %137 + %153 = OpLabel + %154 = OpAccessChain %_ptr_Function_float %data %int_5 + %155 = OpLoad %float %154 + %156 = OpFDiv %float %155 %float_10 + %157 = OpAccessChain %_ptr_Function_float %data %int_9 + %158 = OpLoad %float %157 + %159 = OpFDiv %float %158 %float_10 + %160 = OpAccessChain %_ptr_Function_float %data %int_0 + %161 = OpLoad %float %160 + %162 = OpFDiv %float %161 %float_10 + %163 = OpCompositeConstruct %v4float %156 %159 %162 %float_1 + OpStore %_GLF_color %163 + OpBranch %137 + %137 = OpLabel + OpReturn + OpFunctionEnd +%checkSwap_f1_f1_ = OpFunction %bool None %9 + %a = OpFunctionParameter %_ptr_Function_float + %b = OpFunctionParameter %_ptr_Function_float + %13 = OpLabel + %35 = OpVariable %_ptr_Function_bool Function + %20 = OpAccessChain %_ptr_Input_float %gl_FragCoord %uint_1 + %21 = OpLoad %float %20 + %29 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %uint_1 + %30 = OpLoad %float %29 + %32 = OpFDiv %float %30 %float_2 + %33 = OpFOrdLessThan %bool %21 %32 + OpBranch %36 + %36 = OpLabel + OpSelectionMerge %351 None + OpBranchConditional %33 %352 %354 + %352 = OpLabel + %353 = OpLoad %float %a + OpBranch %351 + %354 = OpLabel + %355 = OpCopyObject %float %float_0 + OpBranch %351 + %351 = OpLabel + %38 = OpPhi %float %353 %352 %355 %354 + OpSelectionMerge %386 None + OpBranchConditional %false %385 %385 + %385 = OpLabel + OpSelectionMerge %356 None + OpBranchConditional %33 %357 %359 + %357 = OpLabel + %358 = OpLoad %float %b + OpBranch %356 + %359 = OpLabel + %360 = OpCopyObject %float %float_0 + OpBranch %356 + %356 = OpLabel + %39 = OpPhi %float %358 %357 %360 %359 + %40 = OpFOrdGreaterThan %bool %38 %39 + OpBranch %362 + %362 = OpLabel + OpSelectionMerge %479 None + OpBranchConditional %33 %480 %479 + %480 = OpLabel + OpStore %35 %40 + OpBranch %479 + %479 = OpLabel + OpBranchConditional %true %361 %386 + %361 = OpLabel + OpBranch %386 + %386 = OpLabel + OpBranch %41 + %41 = OpLabel + OpSelectionMerge %363 None + OpBranchConditional %33 %366 %364 + %364 = OpLabel + %365 = OpLoad %float %a + OpBranch %363 + %366 = OpLabel + %367 = OpCopyObject %float %float_0 + OpBranch %363 + %363 = OpLabel + %42 = OpPhi %float %365 %364 %367 %366 + OpSelectionMerge %368 None + OpBranchConditional %33 %371 %369 + %369 = OpLabel + %370 = OpLoad %float %b + OpBranch %368 + %371 = OpLabel + %372 = OpCopyObject %float %float_0 + OpBranch %368 + %368 = OpLabel + %43 = OpPhi %float %370 %369 %372 %371 + %44 = OpFOrdLessThan %bool %42 %43 + OpSelectionMerge %373 None + OpBranchConditional %33 %373 %374 + %374 = OpLabel + OpStore %35 %44 + OpBranch %373 + %373 = OpLabel + OpBranch %37 + %37 = OpLabel + %45 = OpLoad %bool %35 + OpReturnValue %45 + OpFunctionEnd diff --git a/spirv_parser.cpp b/spirv_parser.cpp index d50d2e84..58fcec10 100644 --- a/spirv_parser.cpp +++ b/spirv_parser.cpp @@ -961,6 +961,49 @@ void Parser::parse(const Instruction &instruction) current_block->false_block = ops[2]; current_block->terminator = SPIRBlock::Select; + + if (current_block->true_block == current_block->false_block) + { + // Bogus conditional, translate to a direct branch. + // Avoids some ugly edge cases later when analyzing CFGs. + + // There are some super jank cases where the merge block is different from the true/false, + // and later branches can "break" out of the selection construct this way. + // This is complete nonsense, but CTS hits this case. + // In this scenario, we should see the selection construct as more of a Switch with one default case. + // The problem here is that this breaks any attempt to break out of outer switch statements, + // but it's theoretically solvable if this ever comes up using the ladder breaking system ... + + if (current_block->true_block != current_block->next_block && + current_block->merge == SPIRBlock::MergeSelection) + { + uint32_t ids = ir.increase_bound_by(2); + + SPIRType type; + type.basetype = SPIRType::Int; + type.width = 32; + set(ids, type); + auto &c = set(ids + 1, ids); + + current_block->condition = c.self; + current_block->default_block = current_block->true_block; + current_block->terminator = SPIRBlock::MultiSelect; + ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT; + ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT; + } + else + { + ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT; + current_block->next_block = current_block->true_block; + current_block->condition = 0; + current_block->true_block = 0; + current_block->false_block = 0; + current_block->merge_block = 0; + current_block->merge = SPIRBlock::MergeNone; + current_block->terminator = SPIRBlock::Direct; + } + } + current_block = nullptr; break; }