Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
103 changes: 103 additions & 0 deletions llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ class SPIRVInstructionSelector : public InstructionSelector {
bool selectImageWriteIntrinsic(MachineInstr &I) const;
bool selectResourceGetPointer(Register &ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;
bool selectModf(Register ResVReg, const SPIRVType *ResType,
MachineInstr &I) const;

// Utilities
std::pair<Register, bool>
Expand Down Expand Up @@ -3207,6 +3209,9 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg,
case Intrinsic::spv_discard: {
return selectDiscard(ResVReg, ResType, I);
}
case Intrinsic::modf: {
return selectModf(ResVReg, ResType, I);
}
default: {
std::string DiagMsg;
raw_string_ostream OS(DiagMsg);
Expand Down Expand Up @@ -3990,6 +3995,104 @@ bool SPIRVInstructionSelector::selectLog10(Register ResVReg,
.constrainAllUses(TII, TRI, RBI);
}

bool SPIRVInstructionSelector::selectModf(Register ResVReg,
const SPIRVType *ResType,
MachineInstr &I) const {
// llvm.modf has a single arg --the number to be decomposed-- and returns a
// struct { restype, restype }, while OpenCLLIB::modf has two args --the
// number to be decomposed and a pointer--, returns the fractional part and
// the integral part is stored in the pointer argument. Therefore, we can't
// use directly the OpenCLLIB::modf intrinsic. However, we can do some
// scaffolding to make it work. The idea is to create an alloca instruction
// to get a ptr, pass this ptr to OpenCL::modf, and then load the value
// from this ptr to place it in the struct. llvm.modf returns the fractional
// part as the first element of the result, and the integral part as the
// second element of the result.

// At this point, the return type is not a struct anymore, but rather two
// independent elements of SPIRVResType. We can get each independent element
// from I.getDefs() or I.getOperands().
if (STI.canUseExtInstSet(SPIRV::InstructionSet::OpenCL_std)) {
uint32_t Opcode = CL::modf;
MachineIRBuilder MIRBuilder(I);
// Get pointer type for alloca variable.
const SPIRVType *PtrType = GR.getOrCreateSPIRVPointerType(
ResType, MIRBuilder, SPIRV::StorageClass::Function);
// Create new register for the pointer type of alloca variable.
Register PtrTyReg =
MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass);
MIRBuilder.getMRI()->setType(
PtrTyReg,
LLT::pointer(storageClassToAddressSpace(SPIRV::StorageClass::Function),
GR.getPointerSize()));
// Assign SPIR-V type of the pointer type of the alloca variable to the
// new register.
GR.assignSPIRVTypeToVReg(PtrType, PtrTyReg, MIRBuilder.getMF());
MachineBasicBlock &EntryBB = I.getMF()->front();
// At this point it's difficult to find the right position to insert the
// variable, because most instructions are still MachineInstruction and
// don't have SPIRV opcodes yet. OpFunction and OpFunctionParameter are
// already translated, so we will aim to insert the variable just after the
// last OpFunctionParameter, if any, or just after OpFunction otherwise.
auto VarPos = EntryBB.begin();
while (VarPos != EntryBB.end() &&
VarPos->getOpcode() != SPIRV::OpFunction) {
++VarPos;
}
// Advance VarPos to the next instruction after OpFunction, it will either
// be an OpFunctionParameter, so that we can start the next loop, or the
// position to insert the OpVariable instruction.
++VarPos;
while (VarPos != EntryBB.end() &&
VarPos->getOpcode() == SPIRV::OpFunctionParameter) {
++VarPos;
}
// VarPos is now pointing at after the last OpFunctionParameter, if any,
// or after OpFunction, if no parameters.
// Create a new MachineInstruction for alloca variable in the
// entry block.
auto AllocaMIB =
BuildMI(EntryBB, VarPos, I.getDebugLoc(), TII.get(SPIRV::OpVariable))
.addDef(PtrTyReg)
.addUse(GR.getSPIRVTypeID(PtrType))
.addImm(static_cast<uint32_t>(SPIRV::StorageClass::Function));
Register Variable = AllocaMIB->getOperand(0).getReg();
// Modf must have 4 operands, the first two are the 2 parts of the result,
// the third is the operand, and the last one is the floating point value.
assert(I.getNumOperands() == 4 &&
"Expected 4 operands for modf instruction");
MachineBasicBlock &BB = *I.getParent();
// Create the OpenCLLIB::modf instruction.
auto MIB =
BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpExtInst))
.addDef(ResVReg)
.addUse(GR.getSPIRVTypeID(ResType))
.addImm(static_cast<uint32_t>(SPIRV::InstructionSet::OpenCL_std))
.addImm(Opcode)
.setMIFlags(I.getFlags())
.add(I.getOperand(3)) // Floating point value.
.addUse(Variable); // Pointer to integral part.
// Assign the integral part stored in the ptr to the second element of the
// result.
Register IntegralPartReg = I.getOperand(1).getReg();
if (IntegralPartReg.isValid()) {
// Load the value from the pointer to integral part.
auto LoadMIB = BuildMI(BB, I, I.getDebugLoc(), TII.get(SPIRV::OpLoad))
.addDef(IntegralPartReg)
.addUse(GR.getSPIRVTypeID(ResType))
.addUse(Variable);
return LoadMIB.constrainAllUses(TII, TRI, RBI);
}

return MIB.constrainAllUses(TII, TRI, RBI);
} else if (STI.canUseExtInstSet(SPIRV::InstructionSet::GLSL_std_450)) {
assert(false && "GLSL::Modf is deprecated.");
// FIXME: GL::Modf is deprecated, use Modfstruct instead.
return false;
}
return false;
}

// Generate the instructions to load 3-element vector builtin input
// IDs/Indices.
// Like: GlobalInvocationId, LocalInvocationId, etc....
Expand Down
59 changes: 59 additions & 0 deletions llvm/test/CodeGen/SPIRV/llvm-intrinsics/fp-intrinsics.ll
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
; RUN: llc -verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown %s -o - | FileCheck %s
; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}

; CHECK: %[[#extinst_id:]] = OpExtInstImport "OpenCL.std"

Expand Down Expand Up @@ -337,3 +338,61 @@ entry:
}

declare float @llvm.fma.f32(float, float, float)

; CHECK: OpFunction
; CHECK: %[[#d:]] = OpFunctionParameter %[[#]]
; CHECK: %[[#fracPtr:]] = OpFunctionParameter %[[#]]
; CHECK: %[[#integralPtr:]] = OpFunctionParameter %[[#]]
; CHECK: %[[#varPtr:]] = OpVariable %[[#]] Function
; CHECK: %[[#frac:]] = OpExtInst %[[#var2]] %[[#extinst_id]] modf %[[#d]] %[[#varPtr]]
; CHECK: %[[#integral:]] = OpLoad %[[#var2]] %[[#varPtr]]
; CHECK: OpStore %[[#fracPtr]] %[[#frac]]
; CHECK: OpStore %[[#integralPtr]] %[[#integral]]
; CHECK: OpFunctionEnd
define void @TestModf(double %d, ptr addrspace(1) %frac, ptr addrspace(1) %integral) {
entry:
%4 = tail call { double, double } @llvm.modf.f64(double %d)
%5 = extractvalue { double, double } %4, 0
%6 = extractvalue { double, double } %4, 1
store double %5, ptr addrspace(1) %frac, align 8
store double %6, ptr addrspace(1) %integral, align 8
ret void
}

define dso_local void @TestModf2(double noundef %d, ptr noundef %frac, ptr noundef %integral) {
entry:
%d.addr = alloca double, align 4
%frac.addr = alloca ptr, align 8
%integral.addr = alloca ptr, align 8
store double %d, ptr %d.addr, align 4
store ptr %frac, ptr %frac.addr, align 8
store ptr %integral, ptr %integral.addr, align 8
%0 = load ptr, ptr %frac.addr, align 8
%tobool = icmp ne ptr %0, null
br i1 %tobool, label %lor.lhs.false, label %if.then

lor.lhs.false:
%1 = load ptr, ptr %integral.addr, align 8
%tobool1 = icmp ne ptr %1, null
br i1 %tobool1, label %if.end, label %if.then

if.then:
br label %return

if.end:
%2 = load ptr, ptr %frac.addr, align 8
%3 = load double, ptr %2, align 4
%4 = load ptr, ptr %integral.addr, align 8
%5 = load double, ptr %4, align 4
%6 = tail call { double, double } @llvm.modf.f64(double %d)
%7 = extractvalue { double, double } %6, 0
%8 = extractvalue { double, double } %6, 1
store double %7, ptr %frac, align 4
store double %7, ptr %integral, align 4
br label %return

return:
ret void
}

declare { double, double } @llvm.modf.f64(double)
Loading