SPV 1.4: Use OpSelect for trivial typed non-scalar/vector expressions.
authorJohn Kessenich <cepheus@frii.com>
Thu, 10 Jan 2019 11:23:06 +0000 (18:23 +0700)
committerJohn Kessenich <cepheus@frii.com>
Fri, 10 May 2019 06:02:45 +0000 (00:02 -0600)
SPIRV/GlslangToSpv.cpp
Test/baseResults/spv.1.4.OpSelect.frag.out [new file with mode: 0755]
Test/spv.1.4.OpSelect.frag [new file with mode: 0755]
gtests/Spv.FromFile.cpp [changed mode: 0644->0755]

index fbfb335..d25ccd3 100644 (file)
@@ -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 (executable)
index 0000000..657d647
--- /dev/null
@@ -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 (executable)
index 0000000..cfbf337
--- /dev/null
@@ -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
+}
old mode 100644 (file)
new mode 100755 (executable)
index c06592f..b8427c8
@@ -466,6 +466,7 @@ INSTANTIATE_TEST_CASE_P(
     Glsl, CompileToSpirv14Test,
     ::testing::ValuesIn(std::vector<std::string>({
         "spv.1.4.OpEntryPoint.frag",
+        "spv.1.4.OpSelect.frag",
     })),
     FileNameAsCustomTestSuffix
 );