diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h --- a/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/Legalizer.h @@ -66,8 +66,9 @@ } MachineFunctionProperties getClearedProperties() const override { - return MachineFunctionProperties().set( - MachineFunctionProperties::Property::NoPHIs); + return MachineFunctionProperties() + .set(MachineFunctionProperties::Property::NoPHIs) + .set(MachineFunctionProperties::Property::NoVRegs); } bool runOnMachineFunction(MachineFunction &MF) override; diff --git a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h --- a/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/LegalizerHelper.h @@ -286,6 +286,14 @@ uint64_t KnownLen, Align DstAlign, Align SrcAlign, bool IsVolatile); + // Implements floating-point environment read/write via library function call. + LegalizeResult createGetStateLibcall(MachineIRBuilder &MIRBuilder, + MachineInstr &MI); + LegalizeResult createSetStateLibcall(MachineIRBuilder &MIRBuilder, + MachineInstr &MI); + LegalizeResult createResetStateLibcall(MachineIRBuilder &MIRBuilder, + MachineInstr &MI); + public: /// Return the alignment to use for a stack temporary object with the given /// type. @@ -441,6 +449,7 @@ createMemLibcall(MachineIRBuilder &MIRBuilder, MachineRegisterInfo &MRI, MachineInstr &MI, LostDebugLocObserver &LocObserver); + } // End namespace llvm. #endif diff --git a/llvm/include/llvm/Support/TargetOpcodes.def b/llvm/include/llvm/Support/TargetOpcodes.def --- a/llvm/include/llvm/Support/TargetOpcodes.def +++ b/llvm/include/llvm/Support/TargetOpcodes.def @@ -683,6 +683,11 @@ HANDLE_TARGET_OPCODE(G_FMINIMUM) HANDLE_TARGET_OPCODE(G_FMAXIMUM) +/// Access to FP environment. +HANDLE_TARGET_OPCODE(G_GET_FPMODE) +HANDLE_TARGET_OPCODE(G_SET_FPMODE) +HANDLE_TARGET_OPCODE(G_RESET_FPMODE) + /// Generic pointer offset HANDLE_TARGET_OPCODE(G_PTR_ADD) diff --git a/llvm/include/llvm/Target/GenericOpcodes.td b/llvm/include/llvm/Target/GenericOpcodes.td --- a/llvm/include/llvm/Target/GenericOpcodes.td +++ b/llvm/include/llvm/Target/GenericOpcodes.td @@ -1008,6 +1008,35 @@ let hasSideEffects = false; } +//------------------------------------------------------------------------------ +// Access to floating-point environment. +//------------------------------------------------------------------------------ + +// These operations read/write floating-point environment. The interaction with +// it is modeled as a side effect, because constrained intrinsics use the same +// method. + +// Reading floating-point control modes. +def G_GET_FPMODE : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins); + let hasSideEffects = true; +} + +// Setting floating-point control modes. +def G_SET_FPMODE : GenericInstruction { + let OutOperandList = (outs); + let InOperandList = (ins type0:$src); + let hasSideEffects = true; +} + +// Setting floating-point control modes to default state. +def G_RESET_FPMODE : GenericInstruction { + let OutOperandList = (outs); + let InOperandList = (ins); + let hasSideEffects = true; +} + //------------------------------------------------------------------------------ // Opcodes for LLVM Intrinsics //------------------------------------------------------------------------------ diff --git a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td --- a/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td +++ b/llvm/include/llvm/Target/GlobalISel/SelectionDAGCompat.td @@ -116,6 +116,10 @@ let IfConvergent = G_INTRINSIC_CONVERGENT; } +def : GINodeEquiv; +def : GINodeEquiv; +def : GINodeEquiv; + // ISD::INTRINSIC_VOID can also be handled with G_INTRINSIC_W_SIDE_EFFECTS. let IfConvergent = G_INTRINSIC_CONVERGENT_W_SIDE_EFFECTS in { def : GINodeEquiv; diff --git a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp --- a/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ b/llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -1837,6 +1837,8 @@ return TargetOpcode::G_LROUND; case Intrinsic::llround: return TargetOpcode::G_LLROUND; + case Intrinsic::get_fpmode: + return TargetOpcode::G_GET_FPMODE; } return Intrinsic::not_intrinsic; } @@ -2416,6 +2418,16 @@ return true; } + case Intrinsic::set_fpmode: { + Value *FPState = CI.getOperand(0); + MIRBuilder.buildInstr(TargetOpcode::G_SET_FPMODE, {}, + { getOrCreateVReg(*FPState) }); + return true; + } + case Intrinsic::reset_fpmode: { + MIRBuilder.buildInstr(TargetOpcode::G_RESET_FPMODE, {}, {}); + return true; + } #define INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC) \ case Intrinsic::INTRINSIC: #include "llvm/IR/ConstrainedOps.def" diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp --- a/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerHelper.cpp @@ -796,10 +796,134 @@ {{MI.getOperand(1).getReg(), FromType, 0}}); } +static RTLIB::Libcall +getStateLibraryFunctionFor(MachineInstr &MI, const TargetLowering &TLI) { + RTLIB::Libcall RTLibcall; + switch (MI.getOpcode()) { + case TargetOpcode::G_GET_FPMODE: + RTLibcall = RTLIB::FEGETMODE; + break; + case TargetOpcode::G_SET_FPMODE: + case TargetOpcode::G_RESET_FPMODE: + RTLibcall = RTLIB::FESETMODE; + break; + default: + llvm_unreachable("Unexpected opcode"); + } + return RTLibcall; +} + +// Some library functions that read FP state (fegetmode, fegetenv) write the +// state into a region in memory. IR intrinsics that do the same operations +// (get_fpmode, get_fpenv) return the state as integer value. To implement these +// intrinsics via the library functions, we need to use temporary variable, +// for example: +// +// %0:_(s32) = G_GET_FPMODE +// +// is transformed to: +// +// %1:_(p0) = G_FRAME_INDEX %stack.0 +// BL &fegetmode +// %0:_(s32) = G_LOAD % 1 +// +LegalizerHelper::LegalizeResult +LegalizerHelper::createGetStateLibcall(MachineIRBuilder &MIRBuilder, + MachineInstr &MI) { + const DataLayout &DL = MIRBuilder.getDataLayout(); + auto &MF = MIRBuilder.getMF(); + auto &MRI = *MIRBuilder.getMRI(); + auto &Ctx = MF.getFunction().getContext(); + + // Create temporary, where library function will put the read state. + Register Dst = MI.getOperand(0).getReg(); + LLT StateTy = MRI.getType(Dst); + TypeSize StateSize = StateTy.getSizeInBytes(); + Align TempAlign = getStackTemporaryAlignment(StateTy); + MachinePointerInfo TempPtrInfo; + auto Temp = createStackTemporary(StateSize, TempAlign, TempPtrInfo); + + // Create a call to library function, with the temporary as an argument. + unsigned TempAddrSpace = DL.getAllocaAddrSpace(); + Type *StatePtrTy = PointerType::get(Ctx, TempAddrSpace); + RTLIB::Libcall RTLibcall = getStateLibraryFunctionFor(MI, TLI); + auto Res = + createLibcall(MIRBuilder, RTLibcall, + CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx), 0), + CallLowering::ArgInfo({Temp.getReg(0), StatePtrTy, 0})); + if (Res != LegalizerHelper::Legalized) + return Res; + + // Create a load from the temporary. + MachineMemOperand *MMO = MF.getMachineMemOperand( + TempPtrInfo, MachineMemOperand::MOLoad, StateTy, TempAlign); + MIRBuilder.buildLoadInstr(TargetOpcode::G_LOAD, Dst, Temp, *MMO); + + return LegalizerHelper::Legalized; +} + +// Similar to `createGetStateLibcall` the function calls a library function +// using transient space in stack. In this case the library function reads +// content of memory region. +LegalizerHelper::LegalizeResult +LegalizerHelper::createSetStateLibcall(MachineIRBuilder &MIRBuilder, + MachineInstr &MI) { + const DataLayout &DL = MIRBuilder.getDataLayout(); + auto &MF = MIRBuilder.getMF(); + auto &MRI = *MIRBuilder.getMRI(); + auto &Ctx = MF.getFunction().getContext(); + + // Create temporary, where library function will get the new state. + Register Src = MI.getOperand(0).getReg(); + LLT StateTy = MRI.getType(Src); + TypeSize StateSize = StateTy.getSizeInBytes(); + Align TempAlign = getStackTemporaryAlignment(StateTy); + MachinePointerInfo TempPtrInfo; + auto Temp = createStackTemporary(StateSize, TempAlign, TempPtrInfo); + + // Put the new state into the temporary. + MachineMemOperand *MMO = MF.getMachineMemOperand( + TempPtrInfo, MachineMemOperand::MOStore, StateTy, TempAlign); + MIRBuilder.buildStore(Src, Temp, *MMO); + + // Create a call to library function, with the temporary as an argument. + unsigned TempAddrSpace = DL.getAllocaAddrSpace(); + Type *StatePtrTy = PointerType::get(Ctx, TempAddrSpace); + RTLIB::Libcall RTLibcall = getStateLibraryFunctionFor(MI, TLI); + return createLibcall(MIRBuilder, RTLibcall, + CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx), 0), + CallLowering::ArgInfo({Temp.getReg(0), StatePtrTy, 0})); +} + +// The function is used to legalize operations that set default environment +// state. In C library a call like `fesetmode(FE_DFL_MODE)` is used for that. +// On most targets supported in glibc FE_DFL_MODE is defined as +// `((const femode_t *) -1)`. Such assumption is used here. If for some target +// it is not true, the target must provide custom lowering. +LegalizerHelper::LegalizeResult +LegalizerHelper::createResetStateLibcall(MachineIRBuilder &MIRBuilder, + MachineInstr &MI) { + const DataLayout &DL = MIRBuilder.getDataLayout(); + auto &MF = MIRBuilder.getMF(); + auto &Ctx = MF.getFunction().getContext(); + + // Create an argument for the library function. + unsigned AddrSpace = DL.getDefaultGlobalsAddressSpace(); + Type *StatePtrTy = PointerType::get(Ctx, AddrSpace); + unsigned PtrSize = DL.getPointerSizeInBits(AddrSpace); + LLT MemTy = LLT::pointer(AddrSpace, PtrSize); + auto DefValue = MIRBuilder.buildConstant(LLT::scalar(PtrSize), -1LL); + DstOp Dest(MRI.createGenericVirtualRegister(MemTy)); + MIRBuilder.buildIntToPtr(Dest, DefValue); + + RTLIB::Libcall RTLibcall = getStateLibraryFunctionFor(MI, TLI); + return createLibcall(MIRBuilder, RTLibcall, + CallLowering::ArgInfo({0}, Type::getVoidTy(Ctx), 0), + CallLowering::ArgInfo({ Dest.getReg(), StatePtrTy, 0})); +} + LegalizerHelper::LegalizeResult LegalizerHelper::libcall(MachineInstr &MI, LostDebugLocObserver &LocObserver) { - LLT LLTy = MRI.getType(MI.getOperand(0).getReg()); - unsigned Size = LLTy.getSizeInBits(); auto &Ctx = MIRBuilder.getMF().getFunction().getContext(); switch (MI.getOpcode()) { @@ -811,6 +935,8 @@ case TargetOpcode::G_SREM: case TargetOpcode::G_UREM: case TargetOpcode::G_CTLZ_ZERO_UNDEF: { + LLT LLTy = MRI.getType(MI.getOperand(0).getReg()); + unsigned Size = LLTy.getSizeInBits(); Type *HLTy = IntegerType::get(Ctx, Size); auto Status = simpleLibcall(MI, MIRBuilder, Size, HLTy); if (Status != Legalized) @@ -841,6 +967,8 @@ case TargetOpcode::G_FRINT: case TargetOpcode::G_FNEARBYINT: case TargetOpcode::G_INTRINSIC_ROUNDEVEN: { + LLT LLTy = MRI.getType(MI.getOperand(0).getReg()); + unsigned Size = LLTy.getSizeInBits(); Type *HLTy = getFloatTypeForLLT(Ctx, LLTy); if (!HLTy || (Size != 32 && Size != 64 && Size != 80 && Size != 128)) { LLVM_DEBUG(dbgs() << "No libcall available for type " << LLTy << ".\n"); @@ -903,6 +1031,24 @@ MI.eraseFromParent(); return Result; } + case TargetOpcode::G_GET_FPMODE: { + LegalizeResult Result = createGetStateLibcall(MIRBuilder, MI); + if (Result != Legalized) + return Result; + break; + } + case TargetOpcode::G_SET_FPMODE: { + LegalizeResult Result = createSetStateLibcall(MIRBuilder, MI); + if (Result != Legalized) + return Result; + break; + } + case TargetOpcode::G_RESET_FPMODE: { + LegalizeResult Result = createResetStateLibcall(MIRBuilder, MI); + if (Result != Legalized) + return Result; + break; + } } MI.eraseFromParent(); diff --git a/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp b/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp --- a/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp +++ b/llvm/lib/CodeGen/GlobalISel/LegalizerInfo.cpp @@ -102,6 +102,7 @@ case Lower: case MoreElements: case FewerElements: + case Libcall: break; default: return Q.Types[Mutation.first] != Mutation.second; @@ -118,6 +119,10 @@ if (Rule.getAction() == Custom || Rule.getAction() == Legal) return true; + // Skip null mutation. + if (!Mutation.second.isValid()) + return true; + const unsigned TypeIdx = Mutation.first; const LLT OldTy = Q.Types[TypeIdx]; const LLT NewTy = Mutation.second; diff --git a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp --- a/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64LegalizerInfo.cpp @@ -972,6 +972,10 @@ getActionDefinitionsBuilder(G_FMAD).lower(); + // Access to floating-point environment. + getActionDefinitionsBuilder({G_GET_FPMODE, G_SET_FPMODE, G_RESET_FPMODE}) + .libcall(); + getLegacyLegalizerInfo().computeTables(); verify(*ST.getInstrInfo()); } diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-fpenv.ll b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-fpenv.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/irtranslator-fpenv.ll @@ -0,0 +1,43 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +; RUN: llc -O0 -mtriple=aarch64-linux-gnu -global-isel -stop-after=irtranslator %s -o - | FileCheck %s + +declare i32 @llvm.get.fpmode.i32() +declare void @llvm.set.fpmode.i32(i32 %fpmode) +declare void @llvm.reset.fpmode() + +define i32 @func_get_fpmode() #0 { + ; CHECK-LABEL: name: func_get_fpmode + ; CHECK: bb.1.entry: + ; CHECK-NEXT: [[GET_FPMODE:%[0-9]+]]:_(s32) = G_GET_FPMODE + ; CHECK-NEXT: $w0 = COPY [[GET_FPMODE]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 +entry: + %fpmode = call i32 @llvm.get.fpmode.i32() + ret i32 %fpmode +} + +define void @func_set_fpmode(i32 %fpmode) #0 { + ; CHECK-LABEL: name: func_set_fpmode + ; CHECK: bb.1.entry: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0 + ; CHECK-NEXT: G_SET_FPMODE [[COPY]](s32) + ; CHECK-NEXT: RET_ReallyLR +entry: + call void @llvm.set.fpmode.i32(i32 %fpmode) + ret void +} + + +define void @func_reset() #0 { + ; CHECK-LABEL: name: func_reset + ; CHECK: bb.1.entry: + ; CHECK-NEXT: G_RESET_FPMODE + ; CHECK-NEXT: RET_ReallyLR +entry: + call void @llvm.reset.fpmode() + ret void +} + +attributes #0 = { nounwind "use-soft-float"="true" } diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalize-fpmode.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-fpmode.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalize-fpmode.mir @@ -0,0 +1,90 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py UTC_ARGS: --version 2 +# RUN: llc -mtriple=aarch64-linux-gnu -run-pass=legalizer %s -o - | FileCheck %s + +--- | + target triple = "aarch64-unknown-linux-gnu" + + declare i32 @llvm.get.fpmode.i32() + declare void @llvm.set.fpmode.i32(i32) + declare void @llvm.reset.fpmode() + + define i32 @func_get_fpmode() #0 { + entry: + %fpmode = call i32 @llvm.get.fpmode.i32() + ret i32 %fpmode + } + + define void @func_set_fpmode(i32 %fpmode) #0 { + entry: + call void @llvm.set.fpmode.i32(i32 %fpmode) + ret void + } + + define void @func_reset() #0 { + entry: + call void @llvm.reset.fpmode() + ret void + } + + attributes #0 = { nounwind "use-soft-float"="true" } + +... +--- +name: func_get_fpmode +tracksRegLiveness: true +body: | + bb.1.entry: + ; CHECK-LABEL: name: func_get_fpmode + ; CHECK: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0 + ; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + ; CHECK-NEXT: $x0 = COPY [[FRAME_INDEX]](p0) + ; CHECK-NEXT: BL &fegetmode, csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit $x0 + ; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + ; CHECK-NEXT: [[LOAD:%[0-9]+]]:_(s32) = G_LOAD [[FRAME_INDEX]](p0) :: (load (s32) from %stack.0) + ; CHECK-NEXT: $w0 = COPY [[LOAD]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %0:_(s32) = G_GET_FPMODE + $w0 = COPY %0(s32) + RET_ReallyLR implicit $w0 + +... +--- +name: func_set_fpmode +tracksRegLiveness: true +body: | + bb.1.entry: + liveins: $w0 + + ; CHECK-LABEL: name: func_set_fpmode + ; CHECK: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: [[COPY:%[0-9]+]]:_(s32) = COPY $w0 + ; CHECK-NEXT: [[FRAME_INDEX:%[0-9]+]]:_(p0) = G_FRAME_INDEX %stack.0 + ; CHECK-NEXT: G_STORE [[COPY]](s32), [[FRAME_INDEX]](p0) :: (store (s32) into %stack.0) + ; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + ; CHECK-NEXT: $x0 = COPY [[FRAME_INDEX]](p0) + ; CHECK-NEXT: BL &fesetmode, csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit $x0 + ; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + ; CHECK-NEXT: RET_ReallyLR + %0:_(s32) = COPY $w0 + G_SET_FPMODE %0(s32) + RET_ReallyLR + +... +--- +name: func_reset +tracksRegLiveness: true +body: | + bb.1.entry: + ; CHECK-LABEL: name: func_reset + ; CHECK: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 -1 + ; CHECK-NEXT: [[INTTOPTR:%[0-9]+]]:_(p0) = G_INTTOPTR [[C]](s64) + ; CHECK-NEXT: ADJCALLSTACKDOWN 0, 0, implicit-def $sp, implicit $sp + ; CHECK-NEXT: $x0 = COPY [[INTTOPTR]](p0) + ; CHECK-NEXT: BL &fesetmode, csr_aarch64_aapcs, implicit-def $lr, implicit $sp, implicit $x0 + ; CHECK-NEXT: ADJCALLSTACKUP 0, 0, implicit-def $sp, implicit $sp + ; CHECK-NEXT: RET_ReallyLR + G_RESET_FPMODE + RET_ReallyLR + +... diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir --- a/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir +++ b/llvm/test/CodeGen/AArch64/GlobalISel/legalizer-info-validation.mir @@ -550,6 +550,17 @@ # DEBUG-NEXT: .. opcode {{[0-9]+}} is aliased to {{[0-9]+}} # DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected # DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected +# DEBUG-NEXT: G_GET_FPMODE (opcode 196): 1 type index, 0 imm indices +# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected +# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected +# DEBUG-NEXT: G_SET_FPMODE (opcode 197): 1 type index, 0 imm indices +# DEBUG-NEXT: .. opcode 197 is aliased to 196 +# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected +# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected +# DEBUG-NEXT: G_RESET_FPMODE (opcode 198): 0 type indices, 0 imm indices +# DEBUG-NEXT: .. opcode 198 is aliased to 196 +# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected +# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected # DEBUG-NEXT: G_PTR_ADD (opcode {{[0-9]+}}): 2 type indices, 0 imm indices # DEBUG-NEXT: .. the first uncovered type index: 2, OK # DEBUG-NEXT: .. the first uncovered imm index: 0, OK diff --git a/llvm/test/CodeGen/AArch64/fpmode.ll b/llvm/test/CodeGen/AArch64/fpmode.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/fpmode.ll @@ -0,0 +1,76 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 2 +; RUN: llc -mtriple=aarch64-none-linux-gnu %s -o - | FileCheck --check-prefix=DAG %s +; RUN: llc -mtriple=aarch64-none-linux-gnu -global-isel %s -o - | FileCheck --check-prefix=GIS %s + +declare i32 @llvm.get.fpmode.i32() +declare void @llvm.set.fpmode.i32(i32 %fpmode) +declare void @llvm.reset.fpmode() + +define i32 @func_get_fpmode_soft() #0 { +; DAG-LABEL: func_get_fpmode_soft: +; DAG: // %bb.0: // %entry +; DAG-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; DAG-NEXT: add x0, sp, #12 +; DAG-NEXT: bl fegetmode +; DAG-NEXT: ldr w0, [sp, #12] +; DAG-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; DAG-NEXT: ret +; +; GIS-LABEL: func_get_fpmode_soft: +; GIS: // %bb.0: // %entry +; GIS-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; GIS-NEXT: add x0, sp, #12 +; GIS-NEXT: bl fegetmode +; GIS-NEXT: ldr w0, [sp, #12] +; GIS-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; GIS-NEXT: ret +entry: + %fpmode = call i32 @llvm.get.fpmode.i32() + ret i32 %fpmode +} + +define void @func_set_fpmode_soft(i32 %fpmode) #0 { +; DAG-LABEL: func_set_fpmode_soft: +; DAG: // %bb.0: // %entry +; DAG-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; DAG-NEXT: str w0, [sp, #12] +; DAG-NEXT: add x0, sp, #12 +; DAG-NEXT: bl fesetmode +; DAG-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; DAG-NEXT: ret +; +; GIS-LABEL: func_set_fpmode_soft: +; GIS: // %bb.0: // %entry +; GIS-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; GIS-NEXT: str w0, [sp, #12] +; GIS-NEXT: add x0, sp, #12 +; GIS-NEXT: bl fesetmode +; GIS-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; GIS-NEXT: ret +entry: + call void @llvm.set.fpmode.i32(i32 %fpmode) + ret void +} + +define void @func_reset_fpmode_soft() #0 { +; DAG-LABEL: func_reset_fpmode_soft: +; DAG: // %bb.0: // %entry +; DAG-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; DAG-NEXT: mov x0, #-1 // =0xffffffffffffffff +; DAG-NEXT: bl fesetmode +; DAG-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; DAG-NEXT: ret +; +; GIS-LABEL: func_reset_fpmode_soft: +; GIS: // %bb.0: // %entry +; GIS-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; GIS-NEXT: mov x0, #-1 // =0xffffffffffffffff +; GIS-NEXT: bl fesetmode +; GIS-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; GIS-NEXT: ret +entry: + call void @llvm.reset.fpmode() + ret void +} + +attributes #0 = { nounwind "use-soft-float"="true" }