Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Register, bool>
buildI32Constant(uint32_t Val, MachineInstr &I,
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
45 changes: 45 additions & 0 deletions llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Loading