Skip to content

Commit 462d583

Browse files
committed
[GlobalISel] Add support for *_fpmode intrinsics
The change implements support of the intrinsics `get_fpmode`, `set_fpmode` and `reset_fpmode` in Global Instruction Selector. Now they are lowered into library function calls. Differential Revision: https://reviews.llvm.org/D158260
1 parent e7b2855 commit 462d583

File tree

13 files changed

+439
-4
lines changed

13 files changed

+439
-4
lines changed

llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ class Legalizer : public MachineFunctionPass {
6666
}
6767

6868
MachineFunctionProperties getClearedProperties() const override {
69-
return MachineFunctionProperties().set(
70-
MachineFunctionProperties::Property::NoPHIs);
69+
return MachineFunctionProperties()
70+
.set(MachineFunctionProperties::Property::NoPHIs)
71+
.set(MachineFunctionProperties::Property::NoVRegs);
7172
}
7273

7374
bool runOnMachineFunction(MachineFunction &MF) override;

llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,14 @@ class LegalizerHelper {
286286
uint64_t KnownLen, Align DstAlign, Align SrcAlign,
287287
bool IsVolatile);
288288

289+
// Implements floating-point environment read/write via library function call.
290+
LegalizeResult createGetStateLibcall(MachineIRBuilder &MIRBuilder,
291+
MachineInstr &MI);
292+
LegalizeResult createSetStateLibcall(MachineIRBuilder &MIRBuilder,
293+
MachineInstr &MI);
294+
LegalizeResult createResetStateLibcall(MachineIRBuilder &MIRBuilder,
295+
MachineInstr &MI);
296+
289297
public:
290298
/// Return the alignment to use for a stack temporary object with the given
291299
/// type.
@@ -441,6 +449,7 @@ LegalizerHelper::LegalizeResult
441449
createMemLibcall(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI,
442450
MachineInstr &MI, LostDebugLocObserver &LocObserver);
443451

452+
444453
} // End namespace llvm.
445454

446455
#endif

llvm/include/llvm/Support/TargetOpcodes.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,11 @@ HANDLE_TARGET_OPCODE(G_FMAXNUM_IEEE)
683683
HANDLE_TARGET_OPCODE(G_FMINIMUM)
684684
HANDLE_TARGET_OPCODE(G_FMAXIMUM)
685685

686+
/// Access to FP environment.
687+
HANDLE_TARGET_OPCODE(G_GET_FPMODE)
688+
HANDLE_TARGET_OPCODE(G_SET_FPMODE)
689+
HANDLE_TARGET_OPCODE(G_RESET_FPMODE)
690+
686691
/// Generic pointer offset
687692
HANDLE_TARGET_OPCODE(G_PTR_ADD)
688693

llvm/include/llvm/Target/GenericOpcodes.td

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,35 @@ def G_FNEARBYINT : GenericInstruction {
10081008
let hasSideEffects = false;
10091009
}
10101010

1011+
//------------------------------------------------------------------------------
1012+
// Access to floating-point environment.
1013+
//------------------------------------------------------------------------------
1014+
1015+
// These operations read/write floating-point environment. The interaction with
1016+
// it is modeled as a side effect, because constrained intrinsics use the same
1017+
// method.
1018+
1019+
// Reading floating-point control modes.
1020+
def G_GET_FPMODE : GenericInstruction {
1021+
let OutOperandList = (outs type0:$dst);
1022+
let InOperandList = (ins);
1023+
let hasSideEffects = true;
1024+
}
1025+
1026+
// Setting floating-point control modes.
1027+
def G_SET_FPMODE : GenericInstruction {
1028+
let OutOperandList = (outs);
1029+
let InOperandList = (ins type0:$src);
1030+
let hasSideEffects = true;
1031+
}
1032+
1033+
// Setting floating-point control modes to default state.
1034+
def G_RESET_FPMODE : GenericInstruction {
1035+
let OutOperandList = (outs);
1036+
let InOperandList = (ins);
1037+
let hasSideEffects = true;
1038+
}
1039+
10111040
//------------------------------------------------------------------------------
10121041
// Opcodes for LLVM Intrinsics
10131042
//------------------------------------------------------------------------------

llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ def : GINodeEquiv<G_INTRINSIC, intrinsic_wo_chain> {
116116
let IfConvergent = G_INTRINSIC_CONVERGENT;
117117
}
118118

119+
def : GINodeEquiv<G_GET_FPMODE, get_fpmode>;
120+
def : GINodeEquiv<G_SET_FPMODE, set_fpmode>;
121+
def : GINodeEquiv<G_RESET_FPMODE, reset_fpmode>;
122+
119123
// ISD::INTRINSIC_VOID can also be handled with G_INTRINSIC_W_SIDE_EFFECTS.
120124
let IfConvergent = G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS in {
121125
def : GINodeEquiv<G_INTRINSIC_W_SIDE_EFFECTS, intrinsic_void>;

llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,6 +1837,8 @@ unsigned IRTranslator::getSimpleIntrinsicOpcode(Intrinsic::ID ID) {
18371837
return TargetOpcode::G_LROUND;
18381838
case Intrinsic::llround:
18391839
return TargetOpcode::G_LLROUND;
1840+
case Intrinsic::get_fpmode:
1841+
return TargetOpcode::G_GET_FPMODE;
18401842
}
18411843
return Intrinsic::not_intrinsic;
18421844
}
@@ -2416,6 +2418,16 @@ bool IRTranslator::translateKnownIntrinsic(const CallInst &CI, Intrinsic::ID ID,
24162418

24172419
return true;
24182420
}
2421+
case Intrinsic::set_fpmode: {
2422+
Value *FPState = CI.getOperand(0);
2423+
MIRBuilder.buildInstr(TargetOpcode::G_SET_FPMODE, {},
2424+
{ getOrCreateVReg(*FPState) });
2425+
return true;
2426+
}
2427+
case Intrinsic::reset_fpmode: {
2428+
MIRBuilder.buildInstr(TargetOpcode::G_RESET_FPMODE, {}, {});
2429+
return true;
2430+
}
24192431
#define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC) \
24202432
case Intrinsic::INTRINSIC:
24212433
#include "llvm/IR/ConstrainedOps.def"

llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -796,10 +796,134 @@ conversionLibcall(MachineInstr &MI, MachineIRBuilder &MIRBuilder, Type *ToType,
796796
{{MI.getOperand(1).getReg(), FromType, 0}});
797797
}
798798

799+
static RTLIB::Libcall
800+
getStateLibraryFunctionFor(MachineInstr &MI, const TargetLowering &TLI) {
801+
RTLIB::Libcall RTLibcall;
802+
switch (MI.getOpcode()) {
803+
case TargetOpcode::G_GET_FPMODE:
804+
RTLibcall = RTLIB::FEGETMODE;
805+
break;
806+
case TargetOpcode::G_SET_FPMODE:
807+
case TargetOpcode::G_RESET_FPMODE:
808+
RTLibcall = RTLIB::FESETMODE;
809+
break;
810+
default:
811+
llvm_unreachable("Unexpected opcode");
812+
}
813+
return RTLibcall;
814+
}
815+
816+
// Some library functions that read FP state (fegetmode, fegetenv) write the
817+
// state into a region in memory. IR intrinsics that do the same operations
818+
// (get_fpmode, get_fpenv) return the state as integer value. To implement these
819+
// intrinsics via the library functions, we need to use temporary variable,
820+
// for example:
821+
//
822+
// %0:_(s32) = G_GET_FPMODE
823+
//
824+
// is transformed to:
825+
//
826+
// %1:_(p0) = G_FRAME_INDEX %stack.0
827+
// BL &fegetmode
828+
// %0:_(s32) = G_LOAD % 1
829+
//
830+
LegalizerHelper::LegalizeResult
831+
LegalizerHelper::createGetStateLibcall(MachineIRBuilder &MIRBuilder,
832+
MachineInstr &MI) {
833+
const DataLayout &DL = MIRBuilder.getDataLayout();
834+
auto &MF = MIRBuilder.getMF();
835+
auto &MRI = *MIRBuilder.getMRI();
836+
auto &Ctx = MF.getFunction().getContext();
837+
838+
// Create temporary, where library function will put the read state.
839+
Register Dst = MI.getOperand(0).getReg();
840+
LLT StateTy = MRI.getType(Dst);
841+
TypeSize StateSize = StateTy.getSizeInBytes();
842+
Align TempAlign = getStackTemporaryAlignment(StateTy);
843+
MachinePointerInfo TempPtrInfo;
844+
auto Temp = createStackTemporary(StateSize, TempAlign, TempPtrInfo);
845+
846+
// Create a call to library function, with the temporary as an argument.
847+
unsigned TempAddrSpace = DL.getAllocaAddrSpace();
848+
Type *StatePtrTy = PointerType::get(Ctx, TempAddrSpace);
849+
RTLIB::Libcall RTLibcall = getStateLibraryFunctionFor(MI, TLI);
850+
auto Res =
851+
createLibcall(MIRBuilder, RTLibcall,
852+
CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx), 0),
853+
CallLowering::ArgInfo({Temp.getReg(0), StatePtrTy, 0}));
854+
if (Res != LegalizerHelper::Legalized)
855+
return Res;
856+
857+
// Create a load from the temporary.
858+
MachineMemOperand *MMO = MF.getMachineMemOperand(
859+
TempPtrInfo, MachineMemOperand::MOLoad, StateTy, TempAlign);
860+
MIRBuilder.buildLoadInstr(TargetOpcode::G_LOAD, Dst, Temp, *MMO);
861+
862+
return LegalizerHelper::Legalized;
863+
}
864+
865+
// Similar to `createGetStateLibcall` the function calls a library function
866+
// using transient space in stack. In this case the library function reads
867+
// content of memory region.
868+
LegalizerHelper::LegalizeResult
869+
LegalizerHelper::createSetStateLibcall(MachineIRBuilder &MIRBuilder,
870+
MachineInstr &MI) {
871+
const DataLayout &DL = MIRBuilder.getDataLayout();
872+
auto &MF = MIRBuilder.getMF();
873+
auto &MRI = *MIRBuilder.getMRI();
874+
auto &Ctx = MF.getFunction().getContext();
875+
876+
// Create temporary, where library function will get the new state.
877+
Register Src = MI.getOperand(0).getReg();
878+
LLT StateTy = MRI.getType(Src);
879+
TypeSize StateSize = StateTy.getSizeInBytes();
880+
Align TempAlign = getStackTemporaryAlignment(StateTy);
881+
MachinePointerInfo TempPtrInfo;
882+
auto Temp = createStackTemporary(StateSize, TempAlign, TempPtrInfo);
883+
884+
// Put the new state into the temporary.
885+
MachineMemOperand *MMO = MF.getMachineMemOperand(
886+
TempPtrInfo, MachineMemOperand::MOStore, StateTy, TempAlign);
887+
MIRBuilder.buildStore(Src, Temp, *MMO);
888+
889+
// Create a call to library function, with the temporary as an argument.
890+
unsigned TempAddrSpace = DL.getAllocaAddrSpace();
891+
Type *StatePtrTy = PointerType::get(Ctx, TempAddrSpace);
892+
RTLIB::Libcall RTLibcall = getStateLibraryFunctionFor(MI, TLI);
893+
return createLibcall(MIRBuilder, RTLibcall,
894+
CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx), 0),
895+
CallLowering::ArgInfo({Temp.getReg(0), StatePtrTy, 0}));
896+
}
897+
898+
// The function is used to legalize operations that set default environment
899+
// state. In C library a call like `fesetmode(FE_DFL_MODE)` is used for that.
900+
// On most targets supported in glibc FE_DFL_MODE is defined as
901+
// `((const femode_t *) -1)`. Such assumption is used here. If for some target
902+
// it is not true, the target must provide custom lowering.
903+
LegalizerHelper::LegalizeResult
904+
LegalizerHelper::createResetStateLibcall(MachineIRBuilder &MIRBuilder,
905+
MachineInstr &MI) {
906+
const DataLayout &DL = MIRBuilder.getDataLayout();
907+
auto &MF = MIRBuilder.getMF();
908+
auto &Ctx = MF.getFunction().getContext();
909+
910+
// Create an argument for the library function.
911+
unsigned AddrSpace = DL.getDefaultGlobalsAddressSpace();
912+
Type *StatePtrTy = PointerType::get(Ctx, AddrSpace);
913+
unsigned PtrSize = DL.getPointerSizeInBits(AddrSpace);
914+
LLT MemTy = LLT::pointer(AddrSpace, PtrSize);
915+
auto DefValue = MIRBuilder.buildConstant(LLT::scalar(PtrSize), -1LL);
916+
DstOp Dest(MRI.createGenericVirtualRegister(MemTy));
917+
MIRBuilder.buildIntToPtr(Dest, DefValue);
918+
919+
RTLIB::Libcall RTLibcall = getStateLibraryFunctionFor(MI, TLI);
920+
return createLibcall(MIRBuilder, RTLibcall,
921+
CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx), 0),
922+
CallLowering::ArgInfo({ Dest.getReg(), StatePtrTy, 0}));
923+
}
924+
799925
LegalizerHelper::LegalizeResult
800926
LegalizerHelper::libcall(MachineInstr &MI, LostDebugLocObserver &LocObserver) {
801-
LLT LLTy = MRI.getType(MI.getOperand(0).getReg());
802-
unsigned Size = LLTy.getSizeInBits();
803927
auto &Ctx = MIRBuilder.getMF().getFunction().getContext();
804928

805929
switch (MI.getOpcode()) {
@@ -811,6 +935,8 @@ LegalizerHelper::libcall(MachineInstr &MI, LostDebugLocObserver &LocObserver) {
811935
case TargetOpcode::G_SREM:
812936
case TargetOpcode::G_UREM:
813937
case TargetOpcode::G_CTLZ_ZERO_UNDEF: {
938+
LLT LLTy = MRI.getType(MI.getOperand(0).getReg());
939+
unsigned Size = LLTy.getSizeInBits();
814940
Type *HLTy = IntegerType::get(Ctx, Size);
815941
auto Status = simpleLibcall(MI, MIRBuilder, Size, HLTy);
816942
if (Status != Legalized)
@@ -841,6 +967,8 @@ LegalizerHelper::libcall(MachineInstr &MI, LostDebugLocObserver &LocObserver) {
841967
case TargetOpcode::G_FRINT:
842968
case TargetOpcode::G_FNEARBYINT:
843969
case TargetOpcode::G_INTRINSIC_ROUNDEVEN: {
970+
LLT LLTy = MRI.getType(MI.getOperand(0).getReg());
971+
unsigned Size = LLTy.getSizeInBits();
844972
Type *HLTy = getFloatTypeForLLT(Ctx, LLTy);
845973
if (!HLTy || (Size != 32 && Size != 64 && Size != 80 && Size != 128)) {
846974
LLVM_DEBUG(dbgs() << "No libcall available for type " << LLTy << ".\n");
@@ -903,6 +1031,24 @@ LegalizerHelper::libcall(MachineInstr &MI, LostDebugLocObserver &LocObserver) {
9031031
MI.eraseFromParent();
9041032
return Result;
9051033
}
1034+
case TargetOpcode::G_GET_FPMODE: {
1035+
LegalizeResult Result = createGetStateLibcall(MIRBuilder, MI);
1036+
if (Result != Legalized)
1037+
return Result;
1038+
break;
1039+
}
1040+
case TargetOpcode::G_SET_FPMODE: {
1041+
LegalizeResult Result = createSetStateLibcall(MIRBuilder, MI);
1042+
if (Result != Legalized)
1043+
return Result;
1044+
break;
1045+
}
1046+
case TargetOpcode::G_RESET_FPMODE: {
1047+
LegalizeResult Result = createResetStateLibcall(MIRBuilder, MI);
1048+
if (Result != Legalized)
1049+
return Result;
1050+
break;
1051+
}
9061052
}
9071053

9081054
MI.eraseFromParent();

llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ static bool hasNoSimpleLoops(const LegalizeRule &Rule, const LegalityQuery &Q,
102102
case Lower:
103103
case MoreElements:
104104
case FewerElements:
105+
case Libcall:
105106
break;
106107
default:
107108
return Q.Types[Mutation.first] != Mutation.second;
@@ -118,6 +119,10 @@ static bool mutationIsSane(const LegalizeRule &Rule,
118119
if (Rule.getAction() == Custom || Rule.getAction() == Legal)
119120
return true;
120121

122+
// Skip null mutation.
123+
if (!Mutation.second.isValid())
124+
return true;
125+
121126
const unsigned TypeIdx = Mutation.first;
122127
const LLT OldTy = Q.Types[TypeIdx];
123128
const LLT NewTy = Mutation.second;

llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,10 @@ AArch64LegalizerInfo::AArch64LegalizerInfo(const AArch64Subtarget &ST)
972972

973973
getActionDefinitionsBuilder(G_FMAD).lower();
974974

975+
// Access to floating-point environment.
976+
getActionDefinitionsBuilder({G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE})
977+
.libcall();
978+
975979
getLegacyLegalizerInfo().computeTables();
976980
verify(*ST.getInstrInfo());
977981
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py
2+
; RUN: llc -O0 -mtriple=aarch64-linux-gnu -global-isel -stop-after=irtranslator %s -o - | FileCheck %s
3+
4+
declare i32 @llvm.get.fpmode.i32()
5+
declare void @llvm.set.fpmode.i32(i32 %fpmode)
6+
declare void @llvm.reset.fpmode()
7+
8+
define i32 @func_get_fpmode() #0 {
9+
; CHECK-LABEL: name: func_get_fpmode
10+
; CHECK: bb.1.entry:
11+
; CHECK-NEXT: [[GET_FPMODE:%[0-9]+]]:_(s32) = G_GET_FPMODE
12+
; CHECK-NEXT: $w0 = COPY [[GET_FPMODE]](s32)
13+
; CHECK-NEXT: RET_ReallyLR implicit $w0
14+
entry:
15+
%fpmode = call i32 @llvm.get.fpmode.i32()
16+
ret i32 %fpmode
17+
}
18+
19+
define void @func_set_fpmode(i32 %fpmode) #0 {
20+
; CHECK-LABEL: name: func_set_fpmode
21+
; CHECK: bb.1.entry:
22+
; CHECK-NEXT: liveins: $w0
23+
; CHECK-NEXT: {{ $}}
24+
; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0
25+
; CHECK-NEXT: G_SET_FPMODE [[COPY]](s32)
26+
; CHECK-NEXT: RET_ReallyLR
27+
entry:
28+
call void @llvm.set.fpmode.i32(i32 %fpmode)
29+
ret void
30+
}
31+
32+
33+
define void @func_reset() #0 {
34+
; CHECK-LABEL: name: func_reset
35+
; CHECK: bb.1.entry:
36+
; CHECK-NEXT: G_RESET_FPMODE
37+
; CHECK-NEXT: RET_ReallyLR
38+
entry:
39+
call void @llvm.reset.fpmode()
40+
ret void
41+
}
42+
43+
attributes #0 = { nounwind "use-soft-float"="true" }

0 commit comments

Comments
 (0)