From 2fa7a957a0c68b78a7f642e5ca15ec71c24fbb2a Mon Sep 17 00:00:00 2001 From: Ehsan Date: Tue, 17 Oct 2017 08:53:00 -0400 Subject: [PATCH] [spirv] Add translation for intrinsic rcp. (#713) Also includes creating 64-bit float constants that are needed for reciprocating a 64-bit float. --- docs/SPIR-V.rst | 1 + tools/clang/include/clang/SPIRV/Constant.h | 2 + .../clang/include/clang/SPIRV/ModuleBuilder.h | 1 + tools/clang/lib/SPIRV/Constant.cpp | 14 ++++++ tools/clang/lib/SPIRV/ModuleBuilder.cpp | 1 + tools/clang/lib/SPIRV/SPIRVEmitter.cpp | 46 +++++++++++++++++- tools/clang/lib/SPIRV/SPIRVEmitter.h | 3 ++ .../test/CodeGenSPIRV/constant.scalar.hlsl | 17 ++++++- .../test/CodeGenSPIRV/intrinsics.rcp.hlsl | 48 +++++++++++++++++++ .../unittests/SPIRV/CodeGenSPIRVTest.cpp | 1 + 10 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/intrinsics.rcp.hlsl diff --git a/docs/SPIR-V.rst b/docs/SPIR-V.rst index b1aecc9c2..5713c8c8d 100644 --- a/docs/SPIR-V.rst +++ b/docs/SPIR-V.rst @@ -1101,6 +1101,7 @@ The following intrinsic HLSL functions are currently supported: - ``ddx_fine``: High precision partial derivative with respect to the screen-space x-coordinate. Uses SIR-V ``OpDPdxFine``. - ``ddy_fine``: High precision partial derivative with respect to the screen-space y-coordinate. Uses SIR-V ``OpDPdyFine``. - ``fwidth``: Returns the absolute value of the partial derivatives of the specified value. Uses SIR-V ``OpFwidth``. +- ``rcp``: Calculates a fast, approximate, per-component reciprocal. Uses SIR-V ``OpFDiv``. Using GLSL extended instructions diff --git a/tools/clang/include/clang/SPIRV/Constant.h b/tools/clang/include/clang/SPIRV/Constant.h index 0da6d87c9..fc76d0451 100644 --- a/tools/clang/include/clang/SPIRV/Constant.h +++ b/tools/clang/include/clang/SPIRV/Constant.h @@ -67,6 +67,8 @@ public: uint32_t value, DecorationSet dec = {}); static const Constant *getFloat32(SPIRVContext &ctx, uint32_t type_id, float value, DecorationSet dec = {}); + static const Constant *getFloat64(SPIRVContext &ctx, uint32_t type_id, + double value, DecorationSet dec = {}); // TODO: 64-bit float and integer constant implementation diff --git a/tools/clang/include/clang/SPIRV/ModuleBuilder.h b/tools/clang/include/clang/SPIRV/ModuleBuilder.h index daddc164e..825664c7e 100644 --- a/tools/clang/include/clang/SPIRV/ModuleBuilder.h +++ b/tools/clang/include/clang/SPIRV/ModuleBuilder.h @@ -339,6 +339,7 @@ public: uint32_t getConstantInt32(int32_t value); uint32_t getConstantUint32(uint32_t value); uint32_t getConstantFloat32(float value); + uint32_t getConstantFloat64(double value); uint32_t getConstantComposite(uint32_t typeId, llvm::ArrayRef constituents); uint32_t getConstantNull(uint32_t type); diff --git a/tools/clang/lib/SPIRV/Constant.cpp b/tools/clang/lib/SPIRV/Constant.cpp index bf71bbcc3..6f93c7699 100644 --- a/tools/clang/lib/SPIRV/Constant.cpp +++ b/tools/clang/lib/SPIRV/Constant.cpp @@ -44,6 +44,20 @@ const Constant *Constant::getFloat32(SPIRVContext &ctx, uint32_t type_id, return getUniqueConstant(ctx, c); } +const Constant *Constant::getFloat64(SPIRVContext &ctx, uint32_t type_id, + double value, DecorationSet dec) { + // TODO: The ordering of the 2 words depends on the endian-ness of the host + // machine. + struct wideFloat { + uint32_t word0; + uint32_t word1; + }; + wideFloat words = cast::BitwiseCast(value); + Constant c = + Constant(spv::Op::OpConstant, type_id, {words.word0, words.word1}, dec); + return getUniqueConstant(ctx, c); +} + const Constant *Constant::getUint32(SPIRVContext &ctx, uint32_t type_id, uint32_t value, DecorationSet dec) { Constant c = Constant(spv::Op::OpConstant, type_id, {value}, dec); diff --git a/tools/clang/lib/SPIRV/ModuleBuilder.cpp b/tools/clang/lib/SPIRV/ModuleBuilder.cpp index edd07d47b..acba49a2f 100644 --- a/tools/clang/lib/SPIRV/ModuleBuilder.cpp +++ b/tools/clang/lib/SPIRV/ModuleBuilder.cpp @@ -927,6 +927,7 @@ uint32_t ModuleBuilder::getConstant##builderTy(cppTy value) { \ IMPL_GET_PRIMITIVE_CONST(Int32, int32_t) IMPL_GET_PRIMITIVE_CONST(Uint32, uint32_t) IMPL_GET_PRIMITIVE_CONST(Float32, float) +IMPL_GET_PRIMITIVE_CONST(Float64, double) #undef IMPL_GET_PRIMITIVE_VALUE diff --git a/tools/clang/lib/SPIRV/SPIRVEmitter.cpp b/tools/clang/lib/SPIRV/SPIRVEmitter.cpp index 147b4c156..17bf636e6 100644 --- a/tools/clang/lib/SPIRV/SPIRVEmitter.cpp +++ b/tools/clang/lib/SPIRV/SPIRVEmitter.cpp @@ -3771,6 +3771,9 @@ uint32_t SPIRVEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) { case hlsl::IntrinsicOp::IOP_sincos: { return processIntrinsicSinCos(callExpr); } + case hlsl::IntrinsicOp::IOP_rcp: { + return processIntrinsicRcp(callExpr); + } case hlsl::IntrinsicOp::IOP_saturate: { return processIntrinsicSaturate(callExpr); } @@ -4349,6 +4352,35 @@ uint32_t SPIRVEmitter::processIntrinsicDot(const CallExpr *callExpr) { } } +uint32_t SPIRVEmitter::processIntrinsicRcp(const CallExpr *callExpr) { + // 'rcp' takes only 1 argument that is a scalar, vector, or matrix of type + // float or double. + assert(callExpr->getNumArgs() == 1u); + const QualType returnType = callExpr->getType(); + const uint32_t returnTypeId = typeTranslator.translateType(returnType); + const Expr *arg = callExpr->getArg(0); + const uint32_t argId = doExpr(arg); + const QualType argType = arg->getType(); + + // For cases with matrix argument. + QualType elemType = {}; + uint32_t numRows = 0, numCols = 0; + if (TypeTranslator::isMxNMatrix(argType, &elemType, &numRows, &numCols)) { + const uint32_t vecOne = getVecValueOne(elemType, numCols); + const auto actOnEachVec = [this, vecOne](uint32_t /*index*/, + uint32_t vecType, + uint32_t curRowId) { + return theBuilder.createBinaryOp(spv::Op::OpFDiv, vecType, vecOne, + curRowId); + }; + return processEachVectorInMatrix(arg, argId, actOnEachVec); + } + + // For cases with scalar or vector arguments. + return theBuilder.createBinaryOp(spv::Op::OpFDiv, returnTypeId, + getValueOne(argType), argId); +} + uint32_t SPIRVEmitter::processIntrinsicAllOrAny(const CallExpr *callExpr, spv::Op spvOp) { // 'all' and 'any' take only 1 parameter. @@ -4739,6 +4771,8 @@ uint32_t SPIRVEmitter::getValueOne(QualType type) { { QualType scalarType = {}; if (TypeTranslator::isScalarType(type, &scalarType)) { + // TODO: Support other types such as short, half, etc. + if (scalarType->isSignedIntegerType()) { return theBuilder.getConstantInt32(1); } @@ -4747,8 +4781,14 @@ uint32_t SPIRVEmitter::getValueOne(QualType type) { return theBuilder.getConstantUint32(1); } - if (scalarType->isFloatingType()) { - return theBuilder.getConstantFloat32(1.0); + if (const auto *builtinType = scalarType->getAs()) { + // TODO: Add support for other types that are not covered yet. + switch (builtinType->getKind()) { + case BuiltinType::Double: + return theBuilder.getConstantFloat64(1.0); + case BuiltinType::Float: + return theBuilder.getConstantFloat32(1.0); + } } } } @@ -4861,6 +4901,8 @@ uint32_t SPIRVEmitter::translateAPFloat(const llvm::APFloat &floatValue, switch (bitwidth) { case 32: return theBuilder.getConstantFloat32(floatValue.convertToFloat()); + case 64: + return theBuilder.getConstantFloat64(floatValue.convertToDouble()); default: break; } diff --git a/tools/clang/lib/SPIRV/SPIRVEmitter.h b/tools/clang/lib/SPIRV/SPIRVEmitter.h index 7be8cc24d..0c96fe850 100644 --- a/tools/clang/lib/SPIRV/SPIRVEmitter.h +++ b/tools/clang/lib/SPIRV/SPIRVEmitter.h @@ -290,6 +290,9 @@ private: /// Processes the 'isFinite' intrinsic function. uint32_t processIntrinsicIsFinite(const CallExpr *); + /// Processes the 'rcp' intrinsic function. + uint32_t processIntrinsicRcp(const CallExpr *); + /// Processes the 'sign' intrinsic function for float types. /// The FSign instruction in the GLSL instruction set returns a floating point /// result. The HLSL sign function, however, returns an integer. An extra diff --git a/tools/clang/test/CodeGenSPIRV/constant.scalar.hlsl b/tools/clang/test/CodeGenSPIRV/constant.scalar.hlsl index 59272aa45..67671b03a 100644 --- a/tools/clang/test/CodeGenSPIRV/constant.scalar.hlsl +++ b/tools/clang/test/CodeGenSPIRV/constant.scalar.hlsl @@ -1,7 +1,8 @@ // Run: %dxc -T ps_6_0 -E main // TODO -// 16bit & 64bit integer & floats (require additional capability) +// 16bit & 64bit integer (require additional capability) +// 16bit floats (require additional capability) // float: denormalized numbers, Inf, NaN void main() { @@ -46,4 +47,18 @@ void main() { float c_float_4_2 = 4.2; // CHECK-DAG: %float_n4_2 = OpConstant %float -4.2 float c_float_n4_2 = -4.2; + + // double constants +// CHECK-DAG: %double_0 = OpConstant %double 0 + double c_double_0 = 0.; +// CHECK-DAG: %double_n0 = OpConstant %double -0 + double c_double_n0 = -0.; +// CHECK-DAG: %double_4_5 = OpConstant %double 4.5 + double c_double_4_5 = 4.5; +// CHECK-DAG: %double_n8_2 = OpConstant %double -8.2 + double c_double_n8_2 = -8.2; +// CHECK-DAG: %double_1234567898765_32 = OpConstant %double 1234567898765.32 + double c_large = 1234567898765.32; +// CHECK-DAG: %double_n1234567898765_32 = OpConstant %double -1234567898765.32 + double c_nlarge = -1234567898765.32; } diff --git a/tools/clang/test/CodeGenSPIRV/intrinsics.rcp.hlsl b/tools/clang/test/CodeGenSPIRV/intrinsics.rcp.hlsl new file mode 100644 index 000000000..189678d8d --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/intrinsics.rcp.hlsl @@ -0,0 +1,48 @@ +// Run: %dxc -T vs_6_0 -E main + +// CHECK: [[v4f1:%\d+]] = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1 +// CHECK: [[v3f1:%\d+]] = OpConstantComposite %v3float %float_1 %float_1 %float_1 +// CHECK: [[v4d1:%\d+]] = OpConstantComposite %v4double %double_1 %double_1 %double_1 %double_1 +// CHECK: [[v3d1:%\d+]] = OpConstantComposite %v3double %double_1 %double_1 %double_1 + +void main() { + float a, rcpa; + float4 b, rcpb; + float2x3 c, rcpc; + + double d, rcpd; + double4 e, rcpe; + double2x3 f, rcpf; + +// CHECK: [[a:%\d+]] = OpLoad %float %a +// CHECK-NEXT: {{%\d+}} = OpFDiv %float %float_1 [[a]] + rcpa = rcp(a); + +// CHECK: [[b:%\d+]] = OpLoad %v4float %b +// CHECK-NEXT: {{%\d+}} = OpFDiv %v4float [[v4f1]] [[b]] + rcpb = rcp(b); + +// CHECK: [[c:%\d+]] = OpLoad %mat2v3float %c +// CHECK-NEXT: [[c0:%\d+]] = OpCompositeExtract %v3float [[c]] 0 +// CHECK-NEXT: [[rcpc0:%\d+]] = OpFDiv %v3float [[v3f1]] [[c0]] +// CHECK-NEXT: [[c1:%\d+]] = OpCompositeExtract %v3float [[c]] 1 +// CHECK-NEXT: [[rcpc1:%\d+]] = OpFDiv %v3float [[v3f1]] [[c1]] +// CHECK-NEXT: {{%\d+}} = OpCompositeConstruct %mat2v3float [[rcpc0]] [[rcpc1]] + rcpc = rcp(c); + +// CHECK: [[d:%\d+]] = OpLoad %double %d +// CHECK-NEXT: {{%\d+}} = OpFDiv %double %double_1 [[d]] + rcpd = rcp(d); + +// CHECK: [[e:%\d+]] = OpLoad %v4double %e +// CHECK-NEXT: {{%\d+}} = OpFDiv %v4double [[v4d1]] [[e]] + rcpe = rcp(e); + +// CHECK: [[f:%\d+]] = OpLoad %mat2v3double %f +// CHECK-NEXT: [[f0:%\d+]] = OpCompositeExtract %v3double [[f]] 0 +// CHECK-NEXT: [[rcpf0:%\d+]] = OpFDiv %v3double [[v3d1]] [[f0]] +// CHECK-NEXT: [[f1:%\d+]] = OpCompositeExtract %v3double [[f]] 1 +// CHECK-NEXT: [[rcpf1:%\d+]] = OpFDiv %v3double [[v3d1]] [[f1]] +// CHECK-NEXT: {{%\d+}} = OpCompositeConstruct %mat2v3double [[rcpf0]] [[rcpf1]] + rcpf = rcp(f); +} diff --git a/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp b/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp index 3701a714f..b87812529 100644 --- a/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp +++ b/tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp @@ -576,6 +576,7 @@ TEST_F(FileTest, IntrinsicsFloatSign) { runFileTest("intrinsics.floatsign.hlsl"); } TEST_F(FileTest, IntrinsicsIntSign) { runFileTest("intrinsics.intsign.hlsl"); } +TEST_F(FileTest, IntrinsicsRcp) { runFileTest("intrinsics.rcp.hlsl"); } TEST_F(FileTest, IntrinsicsReflect) { runFileTest("intrinsics.reflect.hlsl"); } TEST_F(FileTest, IntrinsicsRefract) { runFileTest("intrinsics.refract.hlsl"); } TEST_F(FileTest, IntrinsicsReverseBits) {