diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp index fbfb335c..d25ccd36 100644 --- a/SPIRV/GlslangToSpv.cpp +++ b/SPIRV/GlslangToSpv.cpp @@ -2615,6 +2615,19 @@ bool TGlslangToSpvTraverser::visitAggregate(glslang::TVisit visit, glslang::TInt // next layer copies r-values into memory to use the access-chain mechanism bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang::TIntermSelection* node) { + // see if OpSelect can handle it + const auto isOpSelectable = [&]() { + if (node->getBasicType() == glslang::EbtVoid) + return false; + // OpSelect can do all other types starting with SPV 1.4 + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4) { + // pre-1.4, only scalars and vectors can be handled + if ((!node->getType().isScalar() && !node->getType().isVector())) + return false; + } + return true; + }; + // See if it simple and safe, or required, to execute both sides. // Crucially, side effects must be either semantically required or avoided, // and there are performance trade-offs. @@ -2633,9 +2646,7 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang // if not required to execute both, decide based on performance/practicality... - // see if OpSelect can handle it - if ((!node->getType().isScalar() && !node->getType().isVector()) || - node->getBasicType() == glslang::EbtVoid) + if (!isOpSelectable()) return false; assert(node->getType() == node->getTrueBlock() ->getAsTyped()->getType() && @@ -2672,14 +2683,16 @@ bool TGlslangToSpvTraverser::visitSelection(glslang::TVisit /* visit */, glslang // emit code to select between trueValue and falseValue // see if OpSelect can handle it - if (node->getType().isScalar() || node->getType().isVector()) { + if (isOpSelectable()) { // Emit OpSelect for this selection. // smear condition to vector, if necessary (AST is always scalar) - if (builder.isVector(trueValue)) + // Before 1.4, smear like for mix(), starting with 1.4, keep it scalar + if (glslangIntermediate->getSpv().spv < glslang::EShTargetSpv_1_4 && builder.isVector(trueValue)) { condition = builder.smearScalar(spv::NoPrecision, condition, builder.makeVectorType(builder.makeBoolType(), builder.getNumComponents(trueValue))); + } // OpSelect result = builder.createTriOp(spv::OpSelect, diff --git a/Test/baseResults/spv.1.4.OpSelect.frag.out b/Test/baseResults/spv.1.4.OpSelect.frag.out new file mode 100755 index 00000000..657d6472 --- /dev/null +++ b/Test/baseResults/spv.1.4.OpSelect.frag.out @@ -0,0 +1,153 @@ +spv.1.4.OpSelect.frag +Validation failed +// Module Version 10400 +// Generated by (magic number): 80007 +// Id's are bound by 98 + + Capability Shader + 1: ExtInstImport "GLSL.std.450" + MemoryModel Logical GLSL450 + EntryPoint Fragment 4 "main" 17 20 82 84 + ExecutionMode 4 OriginUpperLeft + Source GLSL 450 + Name 4 "main" + Name 6 "fun1(" + Name 8 "fun2(" + Name 12 "f1" + Name 14 "f2" + Name 17 "outv" + Name 20 "cond" + Name 30 "iv1" + Name 34 "iv2" + Name 53 "m1" + Name 59 "m2" + Name 75 "S1" + MemberName 75(S1) 0 "a" + MemberName 75(S1) 1 "b" + Name 77 "fv" + Name 82 "in1" + Name 84 "in2" + Decorate 17(outv) Location 0 + Decorate 20(cond) Flat + Decorate 20(cond) Location 4 + Decorate 82(in1) Flat + Decorate 82(in1) Location 0 + Decorate 84(in2) Flat + Decorate 84(in2) Location 2 + 2: TypeVoid + 3: TypeFunction 2 + 10: TypeFloat 32 + 11: TypePointer Function 10(float) + 13: 10(float) Constant 1065353216 + 15: 10(float) Constant 1073741824 + 16: TypePointer Output 10(float) + 17(outv): 16(ptr) Variable Output + 18: TypeInt 32 1 + 19: TypePointer Input 18(int) + 20(cond): 19(ptr) Variable Input + 22: 18(int) Constant 8 + 23: TypeBool + 28: TypeVector 18(int) 4 + 29: TypePointer Function 28(ivec4) + 39: 18(int) Constant 0 + 44: TypeInt 32 0 + 45: 44(int) Constant 2 + 50: TypeVector 10(float) 3 + 51: TypeMatrix 50(fvec3) 3 + 52: TypePointer Function 51 + 54: 10(float) Constant 0 + 55: 50(fvec3) ConstantComposite 13 54 54 + 56: 50(fvec3) ConstantComposite 54 13 54 + 57: 50(fvec3) ConstantComposite 54 54 13 + 58: 51 ConstantComposite 55 56 57 + 60: 50(fvec3) ConstantComposite 15 54 54 + 61: 50(fvec3) ConstantComposite 54 15 54 + 62: 50(fvec3) ConstantComposite 54 54 15 + 63: 51 ConstantComposite 60 61 62 + 65: 18(int) Constant 20 + 70: 18(int) Constant 2 + 71: 44(int) Constant 1 + 75(S1): TypeStruct 10(float) 18(int) + 76: TypePointer Function 75(S1) + 79: 18(int) Constant 5 + 81: TypePointer Input 75(S1) + 82(in1): 81(ptr) Variable Input + 84(in2): 81(ptr) Variable Input + 4(main): 2 Function None 3 + 5: Label + 12(f1): 11(ptr) Variable Function + 14(f2): 11(ptr) Variable Function + 30(iv1): 29(ptr) Variable Function + 34(iv2): 29(ptr) Variable Function + 53(m1): 52(ptr) Variable Function + 59(m2): 52(ptr) Variable Function + 77(fv): 76(ptr) Variable Function + Store 12(f1) 13 + Store 14(f2) 15 + 21: 18(int) Load 20(cond) + 24: 23(bool) SLessThan 21 22 + 25: 10(float) Load 12(f1) + 26: 10(float) Load 14(f2) + 27: 10(float) Select 24 25 26 + Store 17(outv) 27 + 31: 10(float) Load 12(f1) + 32: 18(int) ConvertFToS 31 + 33: 28(ivec4) CompositeConstruct 32 32 32 32 + Store 30(iv1) 33 + 35: 10(float) Load 14(f2) + 36: 18(int) ConvertFToS 35 + 37: 28(ivec4) CompositeConstruct 36 36 36 36 + Store 34(iv2) 37 + 38: 18(int) Load 20(cond) + 40: 23(bool) SGreaterThan 38 39 + 41: 28(ivec4) Load 30(iv1) + 42: 28(ivec4) Load 34(iv2) + 43: 28(ivec4) Select 40 41 42 + 46: 18(int) CompositeExtract 43 2 + 47: 10(float) ConvertSToF 46 + 48: 10(float) Load 17(outv) + 49: 10(float) FMul 48 47 + Store 17(outv) 49 + Store 53(m1) 58 + Store 59(m2) 63 + 64: 18(int) Load 20(cond) + 66: 23(bool) SLessThan 64 65 + 67: 51 Load 53(m1) + 68: 51 Load 59(m2) + 69: 51 Select 66 67 68 + 72: 10(float) CompositeExtract 69 2 1 + 73: 10(float) Load 17(outv) + 74: 10(float) FMul 73 72 + Store 17(outv) 74 + 78: 18(int) Load 20(cond) + 80: 23(bool) SGreaterThan 78 79 + 83: 75(S1) Load 82(in1) + 85: 75(S1) Load 84(in2) + 86: 75(S1) Select 80 83 85 + Store 77(fv) 86 + 87: 11(ptr) AccessChain 77(fv) 39 + 88: 10(float) Load 87 + 89: 10(float) Load 17(outv) + 90: 10(float) FMul 89 88 + Store 17(outv) 90 + 91: 18(int) Load 20(cond) + 92: 23(bool) SGreaterThan 91 39 + SelectionMerge 94 None + BranchConditional 92 93 96 + 93: Label + 95: 2 FunctionCall 6(fun1() + Branch 94 + 96: Label + 97: 2 FunctionCall 8(fun2() + Branch 94 + 94: Label + Return + FunctionEnd + 6(fun1(): 2 Function None 3 + 7: Label + Return + FunctionEnd + 8(fun2(): 2 Function None 3 + 9: Label + Return + FunctionEnd diff --git a/Test/spv.1.4.OpSelect.frag b/Test/spv.1.4.OpSelect.frag new file mode 100755 index 00000000..cfbf337a --- /dev/null +++ b/Test/spv.1.4.OpSelect.frag @@ -0,0 +1,37 @@ +#version 450 + +struct S1 { + float a; + int b; +}; + +layout(location = 0) flat in S1 in1; +layout(location = 2) flat in S1 in2; +layout(location = 4) flat in int cond; + +layout(location = 0) out float outv; + +void fun1(){} +void fun2(){} + +void main() +{ + // glslang will only make OpSelect for very trivial looking expressions + + float f1 = 1.0; + float f2 = 2.0; + outv = cond < 8 ? f1 : f2; // in all versions + + ivec4 iv1 = ivec4(f1); + ivec4 iv2 = ivec4(f2); + outv *= (cond > 0 ? iv1 : iv2).z; // in all versions, but in 1.4 as scalar condition, not smeared ala mix() + + mat3 m1 = mat3(1.0); + mat3 m2 = mat3(2.0); + outv *= (cond < 20 ? m1 : m2)[2][1]; // in 1.4, but not before + + S1 fv = cond > 5 ? in1 : in2; // in 1.4, but not before + outv *= fv.a; + + cond > 0 ? fun1() : fun2(); // not allowed by any version +} diff --git a/gtests/Spv.FromFile.cpp b/gtests/Spv.FromFile.cpp old mode 100644 new mode 100755 index c06592f4..b8427c82 --- a/gtests/Spv.FromFile.cpp +++ b/gtests/Spv.FromFile.cpp @@ -466,6 +466,7 @@ INSTANTIATE_TEST_CASE_P( Glsl, CompileToSpirv14Test, ::testing::ValuesIn(std::vector({ "spv.1.4.OpEntryPoint.frag", + "spv.1.4.OpSelect.frag", })), FileNameAsCustomTestSuffix );