浏览代码

[spirv] Translate intrinsic asdouble and asuint. (#758)

Ehsan 7 年之前
父节点
当前提交
97aab11faf

+ 3 - 0
docs/SPIR-V.rst

@@ -1153,6 +1153,9 @@ extended instruction mapping, so they are handled with additional steps:
   conversion into integer matrices.
 - ``asuint``: converts the component type of a scalar/vector/matrix from float
   or int into uint. Uses ``OpBitcast``. This method currently does not support
+- ``asuint``: Converts a double into two 32-bit unsigned integers. Uses SPIR-V ``OpBitCast``.
+- ``asdouble``: Converts two 32-bit unsigned integers into a double, or four 32-bit unsigned
+  integers into two doubles. Uses SPIR-V ``OpVectorShuffle`` and ``OpBitCast``.
   conversion into unsigned integer matrices.
 - ``isfinite`` : Determines if the specified value is finite. Since ``OpIsFinite``
   requires the ``Kernel`` capability, translation is done using ``OpIsNan`` and

+ 90 - 12
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -4004,6 +4004,7 @@ uint32_t SPIRVEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) {
     return processIntrinsicAllOrAny(callExpr, spv::Op::OpAll);
   case hlsl::IntrinsicOp::IOP_any:
     return processIntrinsicAllOrAny(callExpr, spv::Op::OpAny);
+  case hlsl::IntrinsicOp::IOP_asdouble:
   case hlsl::IntrinsicOp::IOP_asfloat:
   case hlsl::IntrinsicOp::IOP_asint:
   case hlsl::IntrinsicOp::IOP_asuint:
@@ -4866,27 +4867,104 @@ uint32_t SPIRVEmitter::processIntrinsicAllOrAny(const CallExpr *callExpr,
 }
 
 uint32_t SPIRVEmitter::processIntrinsicAsType(const CallExpr *callExpr) {
+  // This function handles 'asint', 'asuint', 'asfloat', and 'asdouble'.
+
+  // Method 1: ret asint(arg)
+  //    arg component type = {float, uint}
+  //    arg template  type = {scalar, vector, matrix}
+  //    ret template  type = same as arg template type.
+  //    ret component type = int
+
+  // Method 2: ret asuint(arg)
+  //    arg component type = {float, int}
+  //    arg template  type = {scalar, vector, matrix}
+  //    ret template  type = same as arg template type.
+  //    ret component type = uint
+
+  // Method 3: ret asfloat(arg)
+  //    arg component type = {float, uint, int}
+  //    arg template  type = {scalar, vector, matrix}
+  //    ret template  type = same as arg template type.
+  //    ret component type = float
+
+  // Method 4: double  asdouble(uint lowbits, uint highbits)
+  // Method 5: double2 asdouble(uint2 lowbits, uint2 highbits)
+  // Method 6:
+  //           void asuint(
+  //           in  double value,
+  //           out uint lowbits,
+  //           out uint highbits
+  //           );
+
   const QualType returnType = callExpr->getType();
+  const uint32_t numArgs = callExpr->getNumArgs();
   const uint32_t returnTypeId = typeTranslator.translateType(returnType);
-  assert(callExpr->getNumArgs() == 1u);
-  const Expr *arg = callExpr->getArg(0);
-  const QualType argType = arg->getType();
+  const Expr *arg0 = callExpr->getArg(0);
+  const QualType argType = arg0->getType();
 
-  // asfloat may take a float or a float vector or a float matrix as argument.
-  // These cases would be a no-op.
+  // Method 3 return type may be the same as arg type, so it would be a no-op.
   if (returnType.getCanonicalType() == argType.getCanonicalType())
-    return doExpr(arg);
+    return doExpr(arg0);
 
-  // SPIR-V does not support non-floating point matrices. So 'asint' and
-  // 'asuint' for MxN matrices are currently not supported.
+  // SPIR-V does not support non-floating point matrices. For the above methods
+  // that involve matrices, either the input or the output is a non-float
+  // matrix. (except for 'asfloat' taking a float matrix and returning a float
+  // matrix, which is a no-op and is handled by the condition above).
   if (TypeTranslator::isMxNMatrix(argType)) {
-    emitError("SPIR-V does not support non-floating point matrices. Thus, "
-              "'asint' and 'asuint' currently do not take matrix arguments.");
+    emitError("non-floating-point matrix type unimplemented",
+              callExpr->getExprLoc());
     return 0;
   }
 
-  return theBuilder.createUnaryOp(spv::Op::OpBitcast, returnTypeId,
-                                  doExpr(arg));
+  switch (numArgs) {
+  case 1: {
+    // Handling Method 1, 2, and 3.
+    return theBuilder.createUnaryOp(spv::Op::OpBitcast, returnTypeId,
+                                    doExpr(arg0));
+  }
+  case 2: {
+    const uint32_t lowbits = doExpr(arg0);
+    const uint32_t highbits = doExpr(callExpr->getArg(1));
+    const uint32_t uintType = theBuilder.getUint32Type();
+    const uint32_t doubleType = theBuilder.getFloat64Type();
+    // Handling Method 4
+    if (argType->isUnsignedIntegerType()) {
+      const uint32_t uintVec2Type = theBuilder.getVecType(uintType, 2);
+      const uint32_t operand = theBuilder.createCompositeConstruct(
+          uintVec2Type, {lowbits, highbits});
+      return theBuilder.createUnaryOp(spv::Op::OpBitcast, doubleType, operand);
+    }
+    // Handling Method 5
+    else {
+      const uint32_t uintVec4Type = theBuilder.getVecType(uintType, 4);
+      const uint32_t doubleVec2Type = theBuilder.getVecType(doubleType, 2);
+      const uint32_t operand = theBuilder.createVectorShuffle(
+          uintVec4Type, lowbits, highbits, {0, 2, 1, 3});
+      return theBuilder.createUnaryOp(spv::Op::OpBitcast, doubleVec2Type,
+                                      operand);
+    }
+  }
+  case 3: {
+    // Handling Method 6.
+    const uint32_t value = doExpr(arg0);
+    const uint32_t lowbits = doExpr(callExpr->getArg(1));
+    const uint32_t highbits = doExpr(callExpr->getArg(2));
+    const uint32_t uintType = theBuilder.getUint32Type();
+    const uint32_t uintVec2Type = theBuilder.getVecType(uintType, 2);
+    const uint32_t vecResult =
+        theBuilder.createUnaryOp(spv::Op::OpBitcast, uintVec2Type, value);
+    theBuilder.createStore(
+        lowbits, theBuilder.createCompositeExtract(uintType, vecResult, {0}));
+    theBuilder.createStore(
+        highbits, theBuilder.createCompositeExtract(uintType, vecResult, {1}));
+    return 0;
+  }
+  default:
+    emitError("unrecognized signature for intrinsic function %0",
+              callExpr->getExprLoc())
+        << callExpr->getDirectCallee()->getName();
+    return 0;
+  }
 }
 
 uint32_t SPIRVEmitter::processIntrinsicIsFinite(const CallExpr *callExpr) {

+ 26 - 0
tools/clang/test/CodeGenSPIRV/intrinsics.asdouble.hlsl

@@ -0,0 +1,26 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// Signatures:
+// double  asdouble(uint lowbits, uint highbits)
+// double2 asdouble(uint2 lowbits, uint2 highbits)
+
+void main() {
+
+// CHECK:      [[arg:%\d+]] = OpCompositeConstruct %v2uint %uint_1 %uint_2
+// CHECK-NEXT:     {{%\d+}} = OpBitcast %double [[arg]]
+  double a = asdouble(1u, 2u);
+
+  uint low, high;
+// CHECK:        [[low:%\d+]] = OpLoad %uint %low
+// CHECK-NEXT:  [[high:%\d+]] = OpLoad %uint %high
+// CHECK-NEXT:  [[arg2:%\d+]] = OpCompositeConstruct %v2uint [[low]] [[high]]
+// CHECK-NEXT:       {{%\d+}} = OpBitcast %double [[arg2]]
+  double b = asdouble(low, high);
+
+// CHECK:         [[low2:%\d+]] = OpLoad %v2uint %low2
+// CHECK-NEXT:   [[high2:%\d+]] = OpLoad %v2uint %high2
+// CHECK-NEXT:    [[arg3:%\d+]] = OpVectorShuffle %v4uint [[low2]] [[high2]] 0 2 1 3
+// CHECK-NEXT:         {{%\d+}} = OpBitcast %v2double [[arg3]]
+  uint2 low2, high2;
+  double2 c = asdouble(low2, high2);
+}

+ 23 - 3
tools/clang/test/CodeGenSPIRV/intrinsics.asuint.hlsl

@@ -1,8 +1,17 @@
 // Run: %dxc -T vs_6_0 -E main
 
-// According to HLSL reference:
-// The 'asuint' function can only operate on int, float,
-// vector of these scalars, and matrix of these scalars.
+// Signature: ret asuint(arg)
+//    arg component type = {float, int}
+//    arg template  type = {scalar, vector, matrix}
+//    ret template  type = same as arg template type.
+//    ret component type = uint
+
+// Signature:
+//           void asuint(
+//           in  double value,
+//           out uint lowbits,
+//           out uint highbits
+//           )
 
 void main() {
     uint result;
@@ -43,4 +52,15 @@ void main() {
     // CHECK-NEXT: OpStore %result4 [[i_as_uint]]
     float4 i;
     result4 = asuint(i);
+
+    double value;
+    uint lowbits;
+    uint highbits;
+// CHECK-NEXT:      [[value:%\d+]] = OpLoad %double %value
+// CHECK-NEXT:  [[resultVec:%\d+]] = OpBitcast %v2uint [[value]]
+// CHECK-NEXT: [[resultVec0:%\d+]] = OpCompositeExtract %uint [[resultVec]] 0
+// CHECK-NEXT:                       OpStore %lowbits [[resultVec0]]
+// CHECK-NEXT: [[resultVec1:%\d+]] = OpCompositeExtract %uint [[resultVec]] 1
+// CHECK-NEXT:                       OpStore %highbits [[resultVec1]]
+    asuint(value, lowbits, highbits);
 }

+ 3 - 0
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -556,6 +556,9 @@ TEST_F(FileTest, IntrinsicsDot) { runFileTest("intrinsics.dot.hlsl"); }
 TEST_F(FileTest, IntrinsicsMul) { runFileTest("intrinsics.mul.hlsl"); }
 TEST_F(FileTest, IntrinsicsAll) { runFileTest("intrinsics.all.hlsl"); }
 TEST_F(FileTest, IntrinsicsAny) { runFileTest("intrinsics.any.hlsl"); }
+TEST_F(FileTest, IntrinsicsAsDouble) {
+  runFileTest("intrinsics.asdouble.hlsl");
+}
 TEST_F(FileTest, IntrinsicsAsfloat) { runFileTest("intrinsics.asfloat.hlsl"); }
 TEST_F(FileTest, IntrinsicsAsint) { runFileTest("intrinsics.asint.hlsl"); }
 TEST_F(FileTest, IntrinsicsAsuint) { runFileTest("intrinsics.asuint.hlsl"); }