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.
This commit is contained in:
Hans-Kristian Arntzen 2021-11-07 12:37:23 +01:00
parent 04293e03fd
commit 2714f5410c
3 changed files with 471 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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<SPIRType>(ids, type);
auto &c = set<SPIRConstant>(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;
}