Browse Source

[spirv] Support unary operators: ++, --, !, +, - (#497)

* [spirv] Translate prefix/postfix increment/decrement

* [spirv] Translate unary operator !, +, and -
Lei Zhang 8 years ago
parent
commit
c8ff1681be

+ 9 - 0
docs/SPIR-V.rst

@@ -320,6 +320,15 @@ Comparison operators
 
 Note that for comparison of (vectors of) floats, SPIR-V has two sets of instructions: ``OpFOrd*``, ``OpFUnord*``. We translate into ``OpFOrd*`` ones.
 
+Unary operators
++++++++++++++++
+
+FOr `unary operators <https://msdn.microsoft.com/en-us/library/windows/desktop/bb509631(v=vs.85).aspx#Unary_Operators>`_:
+
+- ``!`` is translated into ``OpLogicalNot``. Parsing will gurantee the operands are of boolean types by inserting necessary casts.
+- ``+`` requires no additional SPIR-V instructions.
+- ``-`` is translated into ``OpSNegate`` and ``OpFNegate`` for (vectors of) integers and floats, respectively.
+
 Control flows
 -------------
 

+ 27 - 4
tools/clang/lib/SPIRV/EmitSPIRVAction.cpp

@@ -753,18 +753,41 @@ public:
     const auto subTypeId = typeTranslator.translateType(subType);
 
     switch (opcode) {
-    case UO_PreInc: {
-      const spv::Op spvOp = translateOp(BO_Add, subType);
+    case UO_PreInc:
+    case UO_PreDec:
+    case UO_PostInc:
+    case UO_PostDec: {
+      const bool isPre = opcode == UO_PreInc || opcode == UO_PreDec;
+      const bool isInc = opcode == UO_PreInc || opcode == UO_PostInc;
+
+      const spv::Op spvOp = translateOp(isInc ? BO_Add : BO_Sub, subType);
       const uint32_t one = getValueOne(subType);
       const uint32_t originValue = theBuilder.createLoad(subTypeId, subValue);
       const uint32_t incValue =
           theBuilder.createBinaryOp(spvOp, subTypeId, originValue, one);
       theBuilder.createStore(subValue, incValue);
-      // Prefix increment operator returns a lvalue.
-      return subValue;
+
+      // Prefix increment/decrement operator returns a lvalue, while postfix
+      // increment/decrement returns a rvalue.
+      return isPre ? subValue : originValue;
     }
     case UO_Not:
       return theBuilder.createUnaryOp(spv::Op::OpNot, subTypeId, subValue);
+    case UO_LNot:
+      // Parsing will do the necessary casting to make sure we are applying the
+      // ! operator on boolean values.
+      return theBuilder.createUnaryOp(spv::Op::OpLogicalNot, subTypeId,
+                                      subValue);
+    case UO_Plus:
+      // No need to do anything for the prefix + operator.
+      return subValue;
+    case UO_Minus: {
+      // SPIR-V have two opcodes for negating values: OpSNegate and OpFNegate.
+      const spv::Op spvOp = isFloatOrVecOfFloatType(subType)
+                                ? spv::Op::OpFNegate
+                                : spv::Op::OpSNegate;
+      return theBuilder.createUnaryOp(spvOp, subTypeId, subValue);
+    }
     default:
       break;
     }

+ 22 - 0
tools/clang/test/CodeGenSPIRV/unary-op.logical-not.hlsl

@@ -0,0 +1,22 @@
+// Run: %dxc -T ps_6_0 -E main
+
+void main() {
+// CHECK-LABEL: %bb_entry = OpLabel
+    bool a, b;
+// CHECK:      [[a0:%\d+]] = OpLoad %bool %a
+// CHECK-NEXT: [[b0:%\d+]] = OpLogicalNot %bool [[a0]]
+// CHECK-NEXT: OpStore %b [[b0]]
+    b = !a;
+
+    bool1 c, d;
+// CHECK-NEXT: [[c0:%\d+]] = OpLoad %bool %c
+// CHECK-NEXT: [[d0:%\d+]] = OpLogicalNot %bool [[c0]]
+// CHECK-NEXT: OpStore %d [[d0]]
+    d = !c;
+
+    bool2 i, j;
+// CHECK-NEXT: [[i0:%\d+]] = OpLoad %v2bool %i
+// CHECK-NEXT: [[j0:%\d+]] = OpLogicalNot %v2bool [[i0]]
+// CHECK-NEXT: OpStore %j [[j0]]
+    j = !i;
+}

+ 34 - 0
tools/clang/test/CodeGenSPIRV/unary-op.minus.hlsl

@@ -0,0 +1,34 @@
+// Run: %dxc -T ps_6_0 -E main
+
+void main() {
+// CHECK-LABEL: %bb_entry = OpLabel
+    int a, b;
+// CHECK:      [[a0:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: [[b0:%\d+]] = OpSNegate %int [[a0]]
+// CHECK-NEXT: OpStore %b [[b0]]
+    b = -a;
+
+    uint c, d;
+// CHECK-NEXT: [[c0:%\d+]] = OpLoad %uint %c
+// CHECK-NEXT: [[d0:%\d+]] = OpSNegate %uint [[c0]]
+// CHECK-NEXT: OpStore %d [[d0]]
+    d = -c;
+
+    float i, j;
+// CHECK-NEXT: [[i0:%\d+]] = OpLoad %float %i
+// CHECK-NEXT: [[j0:%\d+]] = OpFNegate %float [[i0]]
+// CHECK-NEXT: OpStore %j [[j0]]
+    j = -i;
+
+    float1 m, n;
+// CHECK-NEXT: [[m0:%\d+]] = OpLoad %float %m
+// CHECK-NEXT: [[n0:%\d+]] = OpFNegate %float [[m0]]
+// CHECK-NEXT: OpStore %n [[n0]]
+    n = -m;
+
+    int3 x, y;
+// CHECK-NEXT: [[x0:%\d+]] = OpLoad %v3int %x
+// CHECK-NEXT: [[y0:%\d+]] = OpSNegate %v3int [[x0]]
+// CHECK-NEXT: OpStore %y [[y0]]
+    y = -x;
+}

+ 29 - 0
tools/clang/test/CodeGenSPIRV/unary-op.plus.hlsl

@@ -0,0 +1,29 @@
+// Run: %dxc -T ps_6_0 -E main
+
+void main() {
+// CHECK-LABEL: %bb_entry = OpLabel
+    int a, b;
+// CHECK:      [[a0:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: OpStore %b [[a0]]
+    b = +a;
+
+    uint c, d;
+// CHECK-NEXT: [[c0:%\d+]] = OpLoad %uint %c
+// CHECK-NEXT: OpStore %d [[c0]]
+    d = +c;
+
+    float i, j;
+// CHECK-NEXT: [[i0:%\d+]] = OpLoad %float %i
+// CHECK-NEXT: OpStore %j [[i0]]
+    j = +i;
+
+    float1 m, n;
+// CHECK-NEXT: [[m0:%\d+]] = OpLoad %float %m
+// CHECK-NEXT: OpStore %n [[m0]]
+    n = +m;
+
+    int3 x, y;
+// CHECK-NEXT: [[x0:%\d+]] = OpLoad %v3int %x
+// CHECK-NEXT: OpStore %y [[x0]]
+    y = +x;
+}

+ 34 - 0
tools/clang/test/CodeGenSPIRV/unary-op.postfix-dec.hlsl

@@ -0,0 +1,34 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: [[v3float_1_1_1:%\d+]] = OpConstantComposite %v3float %float_1 %float_1 %float_1
+
+void main() {
+// CHECK-LABEL: %bb_entry = OpLabel
+    int a, b;
+// CHECK:      [[a0:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: [[a1:%\d+]] = OpISub %int [[a0]] %int_1
+// CHECK-NEXT: OpStore %a [[a1]]
+// CHECK-NEXT: OpStore %b [[a0]]
+    b = a--;
+
+    uint i, j;
+// CHECK-NEXT: [[i0:%\d+]] = OpLoad %uint %i
+// CHECK-NEXT: [[i1:%\d+]] = OpISub %uint [[i0]] %uint_1
+// CHECK-NEXT: OpStore %i [[i1]]
+// CHECK-NEXT: OpStore %j [[i0]]
+    j = i--;
+
+    float o, p;
+// CHECK-NEXT: [[o0:%\d+]] = OpLoad %float %o
+// CHECK-NEXT: [[o1:%\d+]] = OpFSub %float [[o0]] %float_1
+// CHECK-NEXT: OpStore %o [[o1]]
+// CHECK-NEXT: OpStore %p [[o0]]
+    p = o--;
+
+    float3 x, y;
+// CHECK-NEXT: [[x0:%\d+]] = OpLoad %v3float %x
+// CHECK-NEXT: [[x1:%\d+]] = OpFSub %v3float [[x0]] [[v3float_1_1_1]]
+// CHECK-NEXT: OpStore %x [[x1]]
+// CHECK-NEXT: OpStore %y [[x0]]
+    y = x--;
+}

+ 36 - 0
tools/clang/test/CodeGenSPIRV/unary-op.postfix-inc.hlsl

@@ -0,0 +1,36 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: [[v3float_1_1_1:%\d+]] = OpConstantComposite %v3float %float_1 %float_1 %float_1
+
+void main() {
+// CHECK-LABEL: %bb_entry = OpLabel
+    int a, b;
+// CHECK:      [[a0:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: [[a1:%\d+]] = OpIAdd %int [[a0]] %int_1
+// CHECK-NEXT: OpStore %a [[a1]]
+// CHECK-NEXT: OpStore %b [[a0]]
+    b = a++;
+
+    uint i, j;
+// CHECK-NEXT: [[i0:%\d+]] = OpLoad %uint %i
+// CHECK-NEXT: [[i1:%\d+]] = OpIAdd %uint [[i0]] %uint_1
+// CHECK-NEXT: OpStore %i [[i1]]
+// CHECK-NEXT: OpStore %j [[i0]]
+    j = i++;
+
+    float o, p;
+// CHECK-NEXT: [[o0:%\d+]] = OpLoad %float %o
+// CHECK-NEXT: [[o1:%\d+]] = OpFAdd %float [[o0]] %float_1
+// CHECK-NEXT: OpStore %o [[o1]]
+// CHECK-NEXT: OpStore %p [[o0]]
+    p = o++;
+
+    float3 x, y;
+// CHECK-NEXT: [[x0:%\d+]] = OpLoad %v3float %x
+// CHECK-NEXT: [[x1:%\d+]] = OpFAdd %v3float [[x0]] [[v3float_1_1_1]]
+// CHECK-NEXT: OpStore %x [[x1]]
+// CHECK-NEXT: OpStore %y [[x0]]
+    y = x++;
+}
+
+

+ 81 - 0
tools/clang/test/CodeGenSPIRV/unary-op.prefix-dec.hlsl

@@ -0,0 +1,81 @@
+// Run: %dxc -T ps_6_0 -E main
+
+// CHECK: [[v3float_1_1_1:%\d+]] = OpConstantComposite %v3float %float_1 %float_1 %float_1
+
+void main() {
+// CHECK-LABEL: %bb_entry = OpLabel
+    int a, b;
+// CHECK:      [[a0:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: [[a1:%\d+]] = OpISub %int [[a0]] %int_1
+// CHECK-NEXT: OpStore %a [[a1]]
+// CHECK-NEXT: [[a2:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: OpStore %b [[a2]]
+    b = --a;
+// CHECK-NEXT: [[b0:%\d+]] = OpLoad %int %b
+// CHECK-NEXT: [[a3:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: [[a4:%\d+]] = OpISub %int [[a3]] %int_1
+// CHECK-NEXT: OpStore %a [[a4]]
+// CHECK-NEXT: OpStore %a [[b0]]
+    --a = b;
+
+// Spot check a complicated usage case. No need to duplicate it for all types.
+
+// CHECK-NEXT: [[b1:%\d+]] = OpLoad %int %b
+// CHECK-NEXT: [[b2:%\d+]] = OpISub %int [[b1]] %int_1
+// CHECK-NEXT: OpStore %b [[b2]]
+// CHECK-NEXT: [[b3:%\d+]] = OpLoad %int %b
+// CHECK-NEXT: [[b4:%\d+]] = OpISub %int [[b3]] %int_1
+// CHECK-NEXT: OpStore %b [[b4]]
+// CHECK-NEXT: [[b5:%\d+]] = OpLoad %int %b
+
+// CHECK-NEXT: [[a5:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: [[a6:%\d+]] = OpISub %int [[a5]] %int_1
+// CHECK-NEXT: OpStore %a [[a6]]
+// CHECK-NEXT: [[a7:%\d+]] = OpLoad %int %a
+// CHECK-NEXT: [[a8:%\d+]] = OpISub %int [[a7]] %int_1
+// CHECK-NEXT: OpStore %a [[a8]]
+// CHECK-NEXT: OpStore %a [[b5]]
+    --(--a) = --(--b);
+
+    uint i, j;
+// CHECK-NEXT: [[i0:%\d+]] = OpLoad %uint %i
+// CHECK-NEXT: [[i1:%\d+]] = OpISub %uint [[i0]] %uint_1
+// CHECK-NEXT: OpStore %i [[i1]]
+// CHECK-NEXT: [[i2:%\d+]] = OpLoad %uint %i
+// CHECK-NEXT: OpStore %j [[i2]]
+    j = --i;
+// CHECK-NEXT: [[j0:%\d+]] = OpLoad %uint %j
+// CHECK-NEXT: [[i3:%\d+]] = OpLoad %uint %i
+// CHECK-NEXT: [[i4:%\d+]] = OpISub %uint [[i3]] %uint_1
+// CHECK-NEXT: OpStore %i [[i4]]
+// CHECK-NEXT: OpStore %i [[j0]]
+    --i = j;
+
+    float o, p;
+// CHECK-NEXT: [[o0:%\d+]] = OpLoad %float %o
+// CHECK-NEXT: [[o1:%\d+]] = OpFSub %float [[o0]] %float_1
+// CHECK-NEXT: OpStore %o [[o1]]
+// CHECK-NEXT: [[o2:%\d+]] = OpLoad %float %o
+// CHECK-NEXT: OpStore %p [[o2]]
+    p = --o;
+// CHECK-NEXT: [[p0:%\d+]] = OpLoad %float %p
+// CHECK-NEXT: [[o3:%\d+]] = OpLoad %float %o
+// CHECK-NEXT: [[o4:%\d+]] = OpFSub %float [[o3]] %float_1
+// CHECK-NEXT: OpStore %o [[o4]]
+// CHECK-NEXT: OpStore %o [[p0]]
+    --o = p;
+
+    float3 x, y;
+// CHECK-NEXT: [[x0:%\d+]] = OpLoad %v3float %x
+// CHECK-NEXT: [[x1:%\d+]] = OpFSub %v3float [[x0]] [[v3float_1_1_1]]
+// CHECK-NEXT: OpStore %x [[x1]]
+// CHECK-NEXT: [[x2:%\d+]] = OpLoad %v3float %x
+// CHECK-NEXT: OpStore %y [[x2]]
+    y = --x;
+// CHECK-NEXT: [[y0:%\d+]] = OpLoad %v3float %y
+// CHECK-NEXT: [[x3:%\d+]] = OpLoad %v3float %x
+// CHECK-NEXT: [[x4:%\d+]] = OpFSub %v3float [[x3]] [[v3float_1_1_1]]
+// CHECK-NEXT: OpStore %x [[x4]]
+// CHECK-NEXT: OpStore %x [[y0]]
+    --x = y;
+}

+ 1 - 0
tools/clang/test/CodeGenSPIRV/unary-op.prefix-inc.hlsl

@@ -3,6 +3,7 @@
 // CHECK: [[v3float_1_1_1:%\d+]] = OpConstantComposite %v3float %float_1 %float_1 %float_1
 
 void main() {
+// CHECK-LABEL: %bb_entry = OpLabel
     int a, b;
 // CHECK:      [[a0:%\d+]] = OpLoad %int %a
 // CHECK-NEXT: [[a1:%\d+]] = OpIAdd %int [[a0]] %int_1

+ 30 - 2
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -34,20 +34,42 @@ TEST_F(WholeFileTest, ConstantPixelShader) {
 
 // === Partial output tests ===
 
+// For types
 TEST_F(FileTest, ScalarTypes) { runFileTest("type.scalar.hlsl"); }
 TEST_F(FileTest, VectorTypes) { runFileTest("type.vector.hlsl"); }
 
+// For constants
 TEST_F(FileTest, ScalarConstants) { runFileTest("constant.scalar.hlsl"); }
 TEST_F(FileTest, VectorConstants) { runFileTest("constant.vector.hlsl"); }
 
+// For variables
 TEST_F(FileTest, VariableInitialier) { runFileTest("var.init.hlsl"); }
 
+// For prefix/postfix increment/decrement
 TEST_F(FileTest, UnaryOpPrefixIncrement) {
   runFileTest("unary-op.prefix-inc.hlsl");
 }
+TEST_F(FileTest, UnaryOpPrefixDecrement) {
+  runFileTest("unary-op.prefix-dec.hlsl");
+}
+TEST_F(FileTest, UnaryOpPostfixIncrement) {
+  runFileTest("unary-op.postfix-inc.hlsl");
+}
+TEST_F(FileTest, UnaryOpPostfixDecrement) {
+  runFileTest("unary-op.postfix-dec.hlsl");
+}
 
+// For unary operators
+TEST_F(FileTest, UnaryOpPlus) { runFileTest("unary-op.plus.hlsl"); }
+TEST_F(FileTest, UnaryOpMinus) { runFileTest("unary-op.minus.hlsl"); }
+TEST_F(FileTest, UnaryOpLogicalNot) {
+  runFileTest("unary-op.logical-not.hlsl");
+}
+
+// For assignments
 TEST_F(FileTest, BinaryOpAssign) { runFileTest("binary-op.assign.hlsl"); }
 
+// For arithmetic binary operators
 TEST_F(FileTest, BinaryOpScalarArithmetic) {
   runFileTest("binary-op.arithmetic.scalar.hlsl");
 }
@@ -58,6 +80,7 @@ TEST_F(FileTest, BinaryOpMixedArithmetic) {
   runFileTest("binary-op.arithmetic.mixed.hlsl");
 }
 
+// For arithmetic assignments
 TEST_F(FileTest, BinaryOpScalarArithAssign) {
   runFileTest("binary-op.arith-assign.scalar.hlsl");
 }
@@ -68,6 +91,7 @@ TEST_F(FileTest, BinaryOpMixedArithAssign) {
   runFileTest("binary-op.arith-assign.mixed.hlsl");
 }
 
+// For bitwise binary operators
 TEST_F(FileTest, BinaryOpScalarBitwise) {
   runFileTest("binary-op.bitwise.scalar.hlsl");
 }
@@ -78,6 +102,7 @@ TEST_F(FileTest, BinaryOpMixedBitwise) {
   runFileTest("binary-op.bitwise.mixed.hlsl");
 }
 
+// For bitwise assignments
 TEST_F(FileTest, BinaryOpScalarBitwiseAssign) {
   runFileTest("binary-op.bitwise-assign.scalar.hlsl");
 }
@@ -88,6 +113,7 @@ TEST_F(FileTest, BinaryOpMixedBitwiseAssign) {
   runFileTest("binary-op.bitwise-assign.mixed.hlsl");
 }
 
+// For comparison operators
 TEST_F(FileTest, BinaryOpScalarComparison) {
   runFileTest("binary-op.comparison.scalar.hlsl");
 }
@@ -98,16 +124,18 @@ TEST_F(FileTest, BinaryOpMixedComparison) {
   runFileTest("binary-op.comparison.mixed.hlsl");
 }
 
+// For if statements
 TEST_F(FileTest, IfStmtPlainAssign) { runFileTest("if-stmt.plain.hlsl"); }
-
 TEST_F(FileTest, IfStmtNestedIfStmt) { runFileTest("if-stmt.nested.hlsl"); }
 
+// For for statements
 TEST_F(FileTest, ForStmtPlainAssign) { runFileTest("for-stmt.plain.hlsl"); }
-
 TEST_F(FileTest, ForStmtNestedForStmt) { runFileTest("for-stmt.nested.hlsl"); }
 
+// For control flows
 TEST_F(FileTest, ControlFlowNestedIfForStmt) { runFileTest("cf.if.for.hlsl"); }
 
+// For function calls
 TEST_F(FileTest, FunctionCall) { runFileTest("fn.call.hlsl"); }
 
 TEST_F(FileTest, IntrinsicsDot) { runFileTest("intrinsics.dot.hlsl"); }