Index: lib/Target/ARM/ARMFrameLowering.cpp =================================================================== --- lib/Target/ARM/ARMFrameLowering.cpp +++ lib/Target/ARM/ARMFrameLowering.cpp @@ -1537,14 +1537,20 @@ MachineRegisterInfo &MRI = MF.getRegInfo(); unsigned FramePtr = RegInfo->getFrameRegister(MF); - // Spill R4 if Thumb2 function requires stack realignment - it will be used as - // scratch register. Also spill R4 if Thumb2 function has varsized objects, - // since it's not always possible to restore sp from fp in a single - // instruction. - // FIXME: It will be better just to find spare register here. - if (AFI->isThumb2Function() && - (MFI->hasVarSizedObjects() || RegInfo->needsStackRealignment(MF))) - SavedRegs.set(ARM::R4); + if (AFI->isThumb2Function()) { + // Spill R4 if Thumb2 function requires stack realignment - it will be used as + // scratch register. Also spill R4 if Thumb2 function has varsized objects, + // since it's not always possible to restore sp from fp in a single + // instruction. + // FIXME: It will be better just to find spare register here. + if (MFI->hasVarSizedObjects() || RegInfo->needsStackRealignment(MF)) + SavedRegs.set(ARM::R4); + + // If the current function is noreturn and has calls it must preserve + // ARM::LR in order to guarantee sane backtraces. + if (AFI->shouldForceLRSpill()) + SavedRegs.set(ARM::LR); + } if (AFI->isThumb1OnlyFunction()) { // Spill LR if Thumb1 function uses variable length argument lists. Index: lib/Target/ARM/ARMISelLowering.cpp =================================================================== --- lib/Target/ARM/ARMISelLowering.cpp +++ lib/Target/ARM/ARMISelLowering.cpp @@ -1827,6 +1827,10 @@ CallOpc = ARMISD::CALL_NOLINK; else CallOpc = isARMFunc ? ARMISD::CALL : ARMISD::tCALL; + // If the current function is noreturn and has direct calls it must preserve + // ARM::LR in order to guarantee sane backtraces. + if (!isARMFunc && doesNotRet && isDirect) + AFI->setForceLRSpill(true); } else { if (!isDirect && !Subtarget->hasV5TOps()) CallOpc = ARMISD::CALL_NOLINK; Index: lib/Target/ARM/ARMMachineFunctionInfo.h =================================================================== --- lib/Target/ARM/ARMMachineFunctionInfo.h +++ lib/Target/ARM/ARMMachineFunctionInfo.h @@ -59,6 +59,11 @@ /// emitPrologue. bool RestoreSPFromFP; + /// ForceLRSpill - True if the LR register needs to be spilled. This is + /// necessary to force LR spill in noreturn function and is used in + /// processFunctionBeforeCalleeSavedScan(). + bool ForceLRSpill; + /// LRSpilledForFarJump - True if the LR register has been for spilled to /// enable far jump. bool LRSpilledForFarJump; @@ -128,7 +133,7 @@ isThumb(false), hasThumb2(false), ArgRegsSaveSize(0), ReturnRegsCount(0), HasStackFrame(false), - RestoreSPFromFP(false), + RestoreSPFromFP(false), ForceLRSpill(false), LRSpilledForFarJump(false), FramePtrSpillOffset(0), GPRCS1Offset(0), GPRCS2Offset(0), DPRCSOffset(0), GPRCS1Size(0), GPRCS2Size(0), DPRCSAlignGapSize(0), DPRCSSize(0), @@ -156,6 +161,9 @@ bool shouldRestoreSPFromFP() const { return RestoreSPFromFP; } void setShouldRestoreSPFromFP(bool s) { RestoreSPFromFP = s; } + bool shouldForceLRSpill() const { return ForceLRSpill; } + void setForceLRSpill(bool s) { ForceLRSpill = s; } + bool isLRSpilledForFarJump() const { return LRSpilledForFarJump; } void setLRIsSpilledForFarJump(bool s) { LRSpilledForFarJump = s; } Index: test/CodeGen/Thumb2/call-noret-direct.ll =================================================================== --- /dev/null +++ test/CodeGen/Thumb2/call-noret-direct.ll @@ -0,0 +1,21 @@ +;RUN: llc < %s -mtriple=thumbv7-apple-ios | FileCheck %s + +define void @a() noreturn nounwind ssp { +; CHECK-LABEL: _a: +; CHECK: push {r7, lr} +; CHECK-NEXT: mov r7, sp +; CHECK-NEXT: bl _b + tail call void @b() + unreachable +} + +define void @b() noreturn nounwind ssp { +; CHECK-LABEL: _b: +; CHECK: push {r7, lr} +; CHECK-NEXT: mov r7, sp +; CHECK-NEXT: blx _abort + tail call void @abort() noreturn nounwind + unreachable +} + +declare void @abort() noreturn