diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 915db6824d7c6..8bd3a7ce73ff2 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -339,6 +339,8 @@ class SPIRVInstructionSelector : public InstructionSelector { MachineInstr &I) const; bool selectDerivativeInst(Register ResVReg, const SPIRVType *ResType, MachineInstr &I, const unsigned DPdOpCode) const; + bool selectFCanonicalize(Register ResVReg, const SPIRVType *ResType, + MachineInstr &I) const; // Utilities std::pair buildI32Constant(uint32_t Val, MachineInstr &I, @@ -987,6 +989,9 @@ bool SPIRVInstructionSelector::spvSelect(Register ResVReg, case TargetOpcode::G_FMAXIMUM: return selectExtInst(ResVReg, ResType, I, CL::fmax, GL::NMax); + case TargetOpcode::G_FCANONICALIZE: + return selectFCanonicalize(ResVReg, ResType, I); + case TargetOpcode::G_FCOPYSIGN: return selectExtInst(ResVReg, ResType, I, CL::copysign); @@ -3007,6 +3012,33 @@ SPIRVInstructionSelector::buildI32Constant(uint32_t Val, MachineInstr &I, return {NewReg, Result}; } +bool SPIRVInstructionSelector::selectFCanonicalize(Register ResVReg, + const SPIRVType *ResType, + MachineInstr &I) const { + // There is no native fcanonicalize instruction in SPIRV. We can lower it to: + // - fmin(x, x) or + // - fmul(x, 1.0) + // + // We use fmul(x, 1.0) here, because: + // - llvm-spirv translates fmin to a function call, whereas + // fmul is translated to the LLVM fmul instruction. + // - fmin requires either OpenCL or GLSL extended instruction set, whereas + // fmul does not. + + // fcanonicalize(x) -> fmul(x, 1.0) + SPIRVType *SpirvScalarType = GR.getScalarOrVectorComponentType(ResType); + auto Opcode = ResType->getOpcode() == SPIRV::OpTypeVector + ? SPIRV::OpVectorTimesScalar + : SPIRV::OpFMulS; + + return BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(Opcode)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(I.getOperand(1).getReg()) + .addUse(buildOnesValF(SpirvScalarType, I)) + .constrainAllUses(TII, TRI, RBI); +} + bool SPIRVInstructionSelector::selectFCmp(Register ResVReg, const SPIRVType *ResType, MachineInstr &I) const { diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp index 03d846cb90b4c..fcfb2f33b88c3 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -467,6 +467,9 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { G_INTRINSIC_ROUNDEVEN}) .legalFor(allFloatScalarsAndVectors); + getActionDefinitionsBuilder(G_FCANONICALIZE) + .legalFor(allFloatScalarsAndVectors); + getActionDefinitionsBuilder(G_FCOPYSIGN) .legalForCartesianProduct(allFloatScalarsAndVectors, allFloatScalarsAndVectors); diff --git a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll index 70030ca1a0316..7fbd489eb1346 100644 --- a/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll +++ b/llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll @@ -8,6 +8,11 @@ ; CHECK: %[[#var2:]] = OpTypeFloat 64 ; CHECK: %[[#var3:]] = OpTypeVector %[[#var1]] 4 +; 15360 = 0x3c00 = 1.0 (bf16) +; CHECK: %[[#one_f16:]] = OpConstant %[[#var0]] 15360 +; CHECK: %[[#one_f32:]] = OpConstant %[[#var1]] 1 +; CHECK: %[[#one_f64:]] = OpConstant %[[#var2]] 1 + ; CHECK: OpFunction ; CHECK: %[[#]] = OpExtInst %[[#var0]] %[[#extinst_id]] fabs ; CHECK: OpFunctionEnd @@ -403,3 +408,43 @@ return: } declare { double, double } @llvm.modf.f64(double) + +; CHECK: OpFunction +; CHECK: %[[#x:]] = OpFunctionParameter %[[#]] +; CHECK: %[[#]] = OpFMul %[[#var0]] %[[#x]] %[[#one_f16]] +; CHECK: OpFunctionEnd +define dso_local half @TestCanonicalizeF16(half %x) { +entry: + %t = tail call half @llvm.canonicalize.f16(half %x) + ret half %t +} + +; CHECK: OpFunction +; CHECK: %[[#x:]] = OpFunctionParameter %[[#]] +; CHECK: %[[#]] = OpFMul %[[#var1]] %[[#x]] %[[#one_f32]] +; CHECK: OpFunctionEnd +define dso_local float @TestCanonicalizeF32(float %x) { +entry: + %t = tail call float @llvm.canonicalize.f32(float %x) + ret float %t +} + +; CHECK: OpFunction +; CHECK: %[[#x:]] = OpFunctionParameter %[[#]] +; CHECK: %[[#]] = OpFMul %[[#var2]] %[[#x]] %[[#one_f64]] +; CHECK: OpFunctionEnd +define dso_local double @TestCanonicalizeF64(double %x) { +entry: + %t = tail call double @llvm.canonicalize.f64(double %x) + ret double %t +} + +; CHECK: OpFunction +; CHECK: %[[#x:]] = OpFunctionParameter %[[#]] +; CHECK: %[[#]] = OpVectorTimesScalar %[[#var3]] %[[#x]] %[[#one_f32]] +; CHECK: OpFunctionEnd +define dso_local <4 x float> @TestCanonicalizeVec(<4 x float> %x) { +entry: + %t = tail call <4 x float> @llvm.canonicalize.v4f32(<4 x float> %x) + ret <4 x float> %t +}