diff --git a/llvm/lib/Target/Hexagon/Hexagon.td b/llvm/lib/Target/Hexagon/Hexagon.td index aaa0f3e9b3d3..f6a2e4f1f823 100644 --- a/llvm/lib/Target/Hexagon/Hexagon.td +++ b/llvm/lib/Target/Hexagon/Hexagon.td @@ -27,11 +27,12 @@ def ArchV5: SubtargetFeature<"v5", "HexagonArchVersion", "V5", "Hexagon V5">; def ArchV55: SubtargetFeature<"v55", "HexagonArchVersion", "V55", "Hexagon V55">; def ArchV60: SubtargetFeature<"v60", "HexagonArchVersion", "V60", "Hexagon V60">; -// Hexagon ISA Extensions -def ExtensionHVX: SubtargetFeature<"hvx", "UseHVXOps", - "true", "Hexagon HVX instructions">; -def ExtensionHVXDbl: SubtargetFeature<"hvx-double", "UseHVXDblOps", - "true", "Hexagon HVX Double instructions">; +def FeatureHVX: SubtargetFeature<"hvx", "UseHVXOps", "true", + "Hexagon HVX instructions">; +def FeatureHVXDbl: SubtargetFeature<"hvx-double", "UseHVXDblOps", "true", + "Hexagon HVX Double instructions">; +def FeatureLongCalls: SubtargetFeature<"long-calls", "UseLongCalls", "true", + "Use constant-extended calls">; //===----------------------------------------------------------------------===// // Hexagon Instruction Predicate Definitions. @@ -45,10 +46,10 @@ def HasV60T : Predicate<"HST->hasV60TOps()">, def UseMEMOP : Predicate<"HST->useMemOps()">; def IEEERndNearV5T : Predicate<"HST->modeIEEERndNear()">; def UseHVXDbl : Predicate<"HST->useHVXDblOps()">, - AssemblerPredicate<"ExtensionHVXDbl">; + AssemblerPredicate<"FeatureHVXDbl">; def UseHVXSgl : Predicate<"HST->useHVXSglOps()">; def UseHVX : Predicate<"HST->useHVXSglOps() ||HST->useHVXDblOps()">, - AssemblerPredicate<"ExtensionHVX">; + AssemblerPredicate<"FeatureHVX">; //===----------------------------------------------------------------------===// // Classes used for relation maps. @@ -269,7 +270,7 @@ def : Proc<"hexagonv5", HexagonModelV4, def : Proc<"hexagonv55", HexagonModelV55, [ArchV4, ArchV5, ArchV55]>; def : Proc<"hexagonv60", HexagonModelV60, - [ArchV4, ArchV5, ArchV55, ArchV60, ExtensionHVX]>; + [ArchV4, ArchV5, ArchV55, ArchV60, FeatureHVX]>; //===----------------------------------------------------------------------===// // Declare the target which we are implementing diff --git a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp index 25402147bf53..43ff10b98cb0 100644 --- a/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp +++ b/llvm/lib/Target/Hexagon/HexagonFrameLowering.cpp @@ -149,6 +149,10 @@ static cl::opt ShrinkLimit("shrink-frame-limit", cl::init(UINT_MAX), cl::Hidden, cl::ZeroOrMore, cl::desc("Max count of stack frame " "shrink-wraps")); +static cl::opt EnableSaveRestoreLong("enable-save-restore-long", + cl::Hidden, cl::desc("Enable long calls for save-restore stubs."), + cl::init(false), cl::ZeroOrMore); + static cl::opt UseAllocframe("use-allocframe", cl::init(true), cl::Hidden, cl::desc("Use allocframe more conservatively")); @@ -342,7 +346,7 @@ void HexagonFrameLowering::findShrunkPrologEpilog(MachineFunction &MF, ShrinkCounter++; } - auto &HST = static_cast(MF.getSubtarget()); + auto &HST = MF.getSubtarget(); auto &HRI = *HST.getRegisterInfo(); MachineDominatorTree MDT; @@ -440,7 +444,7 @@ void HexagonFrameLowering::findShrunkPrologEpilog(MachineFunction &MF, /// in one place allows shrink-wrapping of the stack frame. void HexagonFrameLowering::emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const { - auto &HST = static_cast(MF.getSubtarget()); + auto &HST = MF.getSubtarget(); auto &HRI = *HST.getRegisterInfo(); MachineFrameInfo *MFI = MF.getFrameInfo(); @@ -581,7 +585,7 @@ void HexagonFrameLowering::insertEpilogueInBlock(MachineBasicBlock &MBB) const { if (!hasFP(MF)) return; - auto &HST = static_cast(MF.getSubtarget()); + auto &HST = MF.getSubtarget(); auto &HII = *HST.getInstrInfo(); auto &HRI = *HST.getRegisterInfo(); unsigned SP = HRI.getStackRegister(); @@ -1049,7 +1053,8 @@ bool HexagonFrameLowering::insertCSRSpillsInBlock(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI = MBB.begin(); PrologueStubs = false; MachineFunction &MF = *MBB.getParent(); - auto &HII = *MF.getSubtarget().getInstrInfo(); + auto &HST = MF.getSubtarget(); + auto &HII = *HST.getInstrInfo(); if (useSpillFunction(MF, CSI)) { PrologueStubs = true; @@ -1059,20 +1064,31 @@ bool HexagonFrameLowering::insertCSRSpillsInBlock(MachineBasicBlock &MBB, StkOvrFlowEnabled); auto &HTM = static_cast(MF.getTarget()); bool IsPIC = HTM.isPositionIndependent(); + bool LongCalls = HST.useLongCalls() || EnableSaveRestoreLong; // Call spill function. DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() : DebugLoc(); unsigned SpillOpc; - if (StkOvrFlowEnabled) - SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4STK_PIC - : Hexagon::SAVE_REGISTERS_CALL_V4STK; - else - SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4_PIC - : Hexagon::SAVE_REGISTERS_CALL_V4; + if (StkOvrFlowEnabled) { + if (LongCalls) + SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4STK_EXT_PIC + : Hexagon::SAVE_REGISTERS_CALL_V4STK_EXT; + else + SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4STK_PIC + : Hexagon::SAVE_REGISTERS_CALL_V4STK; + } else { + if (LongCalls) + SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4_EXT_PIC + : Hexagon::SAVE_REGISTERS_CALL_V4_EXT; + else + SpillOpc = IsPIC ? Hexagon::SAVE_REGISTERS_CALL_V4_PIC + : Hexagon::SAVE_REGISTERS_CALL_V4; + } MachineInstr *SaveRegsCall = BuildMI(MBB, MI, DL, HII.get(SpillOpc)) .addExternalSymbol(SpillFun); + // Add callee-saved registers as use. addCalleeSaveRegistersAsImpOperand(SaveRegsCall, CSI, false, true); // Add live in registers. @@ -1104,7 +1120,8 @@ bool HexagonFrameLowering::insertCSRRestoresInBlock(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI = MBB.getFirstTerminator(); MachineFunction &MF = *MBB.getParent(); - auto &HII = *MF.getSubtarget().getInstrInfo(); + auto &HST = MF.getSubtarget(); + auto &HII = *HST.getInstrInfo(); if (useRestoreFunction(MF, CSI)) { bool HasTC = hasTailCall(MBB) || !hasReturn(MBB); @@ -1113,6 +1130,7 @@ bool HexagonFrameLowering::insertCSRRestoresInBlock(MachineBasicBlock &MBB, const char *RestoreFn = getSpillFunctionFor(MaxR, Kind); auto &HTM = static_cast(MF.getTarget()); bool IsPIC = HTM.isPositionIndependent(); + bool LongCalls = HST.useLongCalls() || EnableSaveRestoreLong; // Call spill function. DebugLoc DL = MI != MBB.end() ? MI->getDebugLoc() @@ -1120,17 +1138,27 @@ bool HexagonFrameLowering::insertCSRRestoresInBlock(MachineBasicBlock &MBB, MachineInstr *DeallocCall = nullptr; if (HasTC) { - unsigned ROpc = IsPIC ? Hexagon::RESTORE_DEALLOC_BEFORE_TAILCALL_V4_PIC - : Hexagon::RESTORE_DEALLOC_BEFORE_TAILCALL_V4; - DeallocCall = BuildMI(MBB, MI, DL, HII.get(ROpc)) + unsigned RetOpc; + if (LongCalls) + RetOpc = IsPIC ? Hexagon::RESTORE_DEALLOC_BEFORE_TAILCALL_V4_EXT_PIC + : Hexagon::RESTORE_DEALLOC_BEFORE_TAILCALL_V4_EXT; + else + RetOpc = IsPIC ? Hexagon::RESTORE_DEALLOC_BEFORE_TAILCALL_V4_PIC + : Hexagon::RESTORE_DEALLOC_BEFORE_TAILCALL_V4; + DeallocCall = BuildMI(MBB, MI, DL, HII.get(RetOpc)) .addExternalSymbol(RestoreFn); } else { // The block has a return. MachineBasicBlock::iterator It = MBB.getFirstTerminator(); assert(It->isReturn() && std::next(It) == MBB.end()); - unsigned ROpc = IsPIC ? Hexagon::RESTORE_DEALLOC_RET_JMP_V4_PIC - : Hexagon::RESTORE_DEALLOC_RET_JMP_V4; - DeallocCall = BuildMI(MBB, It, DL, HII.get(ROpc)) + unsigned RetOpc; + if (LongCalls) + RetOpc = IsPIC ? Hexagon::RESTORE_DEALLOC_RET_JMP_V4_EXT_PIC + : Hexagon::RESTORE_DEALLOC_RET_JMP_V4_EXT; + else + RetOpc = IsPIC ? Hexagon::RESTORE_DEALLOC_RET_JMP_V4_PIC + : Hexagon::RESTORE_DEALLOC_RET_JMP_V4; + DeallocCall = BuildMI(MBB, It, DL, HII.get(RetOpc)) .addExternalSymbol(RestoreFn); // Transfer the function live-out registers. DeallocCall->copyImplicitOps(MF, *It); diff --git a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp index cdd4c2f8617d..2362ef8048fc 100644 --- a/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp +++ b/llvm/lib/Target/Hexagon/HexagonISelLowering.cpp @@ -842,14 +842,17 @@ HexagonTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, InFlag = SDValue(); } + bool LongCalls = MF.getSubtarget().useLongCalls(); + unsigned Flags = LongCalls ? HexagonII::HMOTF_ConstExtended : 0; + // If the callee is a GlobalAddress/ExternalSymbol node (quite common, every // direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol // node so that legalize doesn't hack it. if (GlobalAddressSDNode *G = dyn_cast(Callee)) { - Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, PtrVT); + Callee = DAG.getTargetGlobalAddress(G->getGlobal(), dl, PtrVT, 0, Flags); } else if (ExternalSymbolSDNode *S = dyn_cast(Callee)) { - Callee = DAG.getTargetExternalSymbol(S->getSymbol(), PtrVT); + Callee = DAG.getTargetExternalSymbol(S->getSymbol(), PtrVT, Flags); } // Returns a chain & a flag for retval copy to use. diff --git a/llvm/lib/Target/Hexagon/HexagonSelectionDAGInfo.cpp b/llvm/lib/Target/Hexagon/HexagonSelectionDAGInfo.cpp index 00dfed754995..10730536080e 100644 --- a/llvm/lib/Target/Hexagon/HexagonSelectionDAGInfo.cpp +++ b/llvm/lib/Target/Hexagon/HexagonSelectionDAGInfo.cpp @@ -44,14 +44,17 @@ SDValue HexagonSelectionDAGInfo::EmitTargetCodeForMemcpy( const char *SpecialMemcpyName = "__hexagon_memcpy_likely_aligned_min32bytes_mult8bytes"; + const MachineFunction &MF = DAG.getMachineFunction(); + bool LongCalls = MF.getSubtarget().useLongCalls(); + unsigned Flags = LongCalls ? HexagonII::HMOTF_ConstExtended : 0; TargetLowering::CallLoweringInfo CLI(DAG); CLI.setDebugLoc(dl) .setChain(Chain) .setCallee(TLI.getLibcallCallingConv(RTLIB::MEMCPY), Type::getVoidTy(*DAG.getContext()), - DAG.getTargetExternalSymbol( - SpecialMemcpyName, TLI.getPointerTy(DAG.getDataLayout())), + DAG.getTargetExternalSymbol(SpecialMemcpyName, + TLI.getPointerTy(DAG.getDataLayout()), Flags), std::move(Args)) .setDiscardResult(); diff --git a/llvm/lib/Target/Hexagon/HexagonSubtarget.cpp b/llvm/lib/Target/Hexagon/HexagonSubtarget.cpp index 73d3bd13d9fd..2771142bac6e 100644 --- a/llvm/lib/Target/Hexagon/HexagonSubtarget.cpp +++ b/llvm/lib/Target/Hexagon/HexagonSubtarget.cpp @@ -69,6 +69,10 @@ static cl::opt EnableSubregLiveness("hexagon-subreg-liveness", cl::Hidden, cl::ZeroOrMore, cl::init(false), cl::desc("Enable subregister liveness tracking for Hexagon")); +static cl::opt OverrideLongCalls("hexagon-long-calls", + cl::Hidden, cl::ZeroOrMore, cl::init(false), + cl::desc("If present, forces/disables the use of long calls")); + void HexagonSubtarget::initializeEnvironment() { UseMemOps = false; ModeIEEERndNear = false; @@ -94,12 +98,15 @@ HexagonSubtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS) { UseHVXOps = false; UseHVXDblOps = false; + UseLongCalls = false; ParseSubtargetFeatures(CPUString, FS); if (EnableHexagonHVX.getPosition()) UseHVXOps = EnableHexagonHVX; if (EnableHexagonHVXDouble.getPosition()) UseHVXDblOps = EnableHexagonHVXDouble; + if (OverrideLongCalls.getPosition()) + UseLongCalls = OverrideLongCalls; return *this; } diff --git a/llvm/lib/Target/Hexagon/HexagonSubtarget.h b/llvm/lib/Target/Hexagon/HexagonSubtarget.h index 77d096608d45..56939cf2ddd7 100644 --- a/llvm/lib/Target/Hexagon/HexagonSubtarget.h +++ b/llvm/lib/Target/Hexagon/HexagonSubtarget.h @@ -34,6 +34,7 @@ class HexagonSubtarget : public HexagonGenSubtargetInfo { virtual void anchor(); bool UseMemOps, UseHVXOps, UseHVXDblOps; + bool UseLongCalls; bool ModeIEEERndNear; public: @@ -101,6 +102,7 @@ public: bool useHVXOps() const { return UseHVXOps; } bool useHVXDblOps() const { return UseHVXOps && UseHVXDblOps; } bool useHVXSglOps() const { return UseHVXOps && !UseHVXDblOps; } + bool useLongCalls() const { return UseLongCalls; } bool useBSBScheduling() const { return UseBSBScheduling; } bool enableMachineScheduler() const override; diff --git a/llvm/test/CodeGen/Hexagon/long-calls.ll b/llvm/test/CodeGen/Hexagon/long-calls.ll new file mode 100644 index 000000000000..9f9a527a542f --- /dev/null +++ b/llvm/test/CodeGen/Hexagon/long-calls.ll @@ -0,0 +1,73 @@ +; RUN: llc -march=hexagon -enable-save-restore-long < %s | FileCheck %s + +; Check that the -long-calls feature is supported by the backend. + +; CHECK: call ##foo +; CHECK: jump ##__restore +define i64 @test_longcall(i32 %x, i32 %y) #0 { +entry: + %add = add nsw i32 %x, 5 + %call = tail call i64 @foo(i32 %add) #6 + %conv = sext i32 %y to i64 + %add1 = add nsw i64 %call, %conv + ret i64 %add1 +} + +; CHECK: jump ##foo +define i64 @test_longtailcall(i32 %x, i32 %y) #1 { +entry: + %add = add nsw i32 %x, 5 + %call = tail call i64 @foo(i32 %add) #6 + ret i64 %call +} + +; CHECK: call ##bar +define i64 @test_longnoret(i32 %x, i32 %y) #2 { +entry: + %add = add nsw i32 %x, 5 + %0 = tail call i64 @bar(i32 %add) #7 + unreachable +} + +; CHECK: call foo +; CHECK: jump ##__restore +; The restore call will still be long because of the enable-save-restore-long +; option being used. +define i64 @test_shortcall(i32 %x, i32 %y) #3 { +entry: + %add = add nsw i32 %x, 5 + %call = tail call i64 @foo(i32 %add) #6 + %conv = sext i32 %y to i64 + %add1 = add nsw i64 %call, %conv + ret i64 %add1 +} + +; CHECK: jump foo +define i64 @test_shorttailcall(i32 %x, i32 %y) #4 { +entry: + %add = add nsw i32 %x, 5 + %call = tail call i64 @foo(i32 %add) #6 + ret i64 %call +} + +; CHECK: call bar +define i64 @test_shortnoret(i32 %x, i32 %y) #5 { +entry: + %add = add nsw i32 %x, 5 + %0 = tail call i64 @bar(i32 %add) #7 + unreachable +} + +declare i64 @foo(i32) #6 +declare i64 @bar(i32) #7 + +attributes #0 = { minsize nounwind "target-cpu"="hexagonv60" "target-features"="+long-calls" } +attributes #1 = { nounwind "target-cpu"="hexagonv60" "target-features"="+long-calls" } +attributes #2 = { noreturn nounwind "target-cpu"="hexagonv60" "target-features"="+long-calls" } + +attributes #3 = { minsize nounwind "target-cpu"="hexagonv60" "target-features"="-long-calls" } +attributes #4 = { nounwind "target-cpu"="hexagonv60" "target-features"="-long-calls" } +attributes #5 = { noreturn nounwind "target-cpu"="hexagonv60" "target-features"="-long-calls" } + +attributes #6 = { noreturn "target-cpu"="hexagonv60" } +attributes #7 = { noreturn nounwind "target-cpu"="hexagonv60" }