SPV 1.4: Use OpSelect for trivial typed non-scalar/vector expressions.
diff --git a/SPIRV/GlslangToSpv.cpp b/SPIRV/GlslangToSpv.cpp
index fbfb335..d25ccd3 100644
--- a/SPIRV/GlslangToSpv.cpp
+++ b/SPIRV/GlslangToSpv.cpp
@@ -2615,6 +2615,19 @@
// 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 @@
// 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 @@
// 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 0000000..657d647
--- /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 0000000..cfbf337
--- /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 c06592f..b8427c8
--- a/gtests/Spv.FromFile.cpp
+++ b/gtests/Spv.FromFile.cpp
@@ -466,6 +466,7 @@
Glsl, CompileToSpirv14Test,
::testing::ValuesIn(std::vector<std::string>({
"spv.1.4.OpEntryPoint.frag",
+ "spv.1.4.OpSelect.frag",
})),
FileNameAsCustomTestSuffix
);