return false;
}
+ /// Returns true if the target can safely skip saving callee-saved registers
+ /// for noreturn nounwind functions.
+ virtual bool enableCalleeSaveSkip(const MachineFunction &MF) const;
+
/// emitProlog/emitEpilog - These methods insert prolog and epilog code into
/// the function.
virtual void emitPrologue(MachineFunction &MF,
return Attr.getValueAsString() == "true";
}
+bool TargetFrameLowering::enableCalleeSaveSkip(const MachineFunction &MF) const {
+ assert(MF.getFunction().hasFnAttribute(Attribute::NoReturn) &&
+ MF.getFunction().hasFnAttribute(Attribute::NoUnwind) &&
+ !MF.getFunction().hasFnAttribute(Attribute::UWTable));
+ return false;
+}
+
/// Returns the displacement from the frame register to the stack
/// frame of the specified index, along with the frame register used
/// (in output arg FrameReg). This is the default implementation which
if (MF.getFunction().hasFnAttribute(Attribute::Naked))
return;
+ // Noreturn+nounwind functions never restore CSR, so no saves are needed.
+ // Purely noreturn functions may still return through throws, so those must
+ // save CSR for caller exception handlers.
+ //
+ // If the function uses longjmp to break out of its current path of
+ // execution we do not need the CSR spills either: setjmp stores all CSRs
+ // it was called with into the jmp_buf, which longjmp then restores.
+ if (MF.getFunction().hasFnAttribute(Attribute::NoReturn) &&
+ MF.getFunction().hasFnAttribute(Attribute::NoUnwind) &&
+ !MF.getFunction().hasFnAttribute(Attribute::UWTable) &&
+ enableCalleeSaveSkip(MF))
+ return;
+
// Functions which call __builtin_unwind_init get all their registers saved.
bool CallsUnwindInit = MF.callsUnwindInit();
const MachineRegisterInfo &MRI = MF.getRegInfo();
MF.getSubtarget<ARMSubtarget>().useFastISel();
}
+/// Returns true if the target can safely skip saving callee-saved registers
+/// for noreturn nounwind functions.
+bool ARMFrameLowering::enableCalleeSaveSkip(const MachineFunction &MF) const {
+ assert(MF.getFunction().hasFnAttribute(Attribute::NoReturn) &&
+ MF.getFunction().hasFnAttribute(Attribute::NoUnwind) &&
+ !MF.getFunction().hasFnAttribute(Attribute::UWTable));
+
+ // Frame pointer and link register are not treated as normal CSR, thus we
+ // can always skip CSR saves for nonreturning functions.
+ return true;
+}
+
/// hasFP - Return true if the specified function should have a dedicated frame
/// pointer register. This is true if the function has variable sized allocas
/// or if frame pointer elimination is disabled.
bool noFramePointerElim(const MachineFunction &MF) const override;
+ bool enableCalleeSaveSkip(const MachineFunction &MF) const override;
+
bool hasFP(const MachineFunction &MF) const override;
bool hasReservedCallFrame(const MachineFunction &MF) const override;
bool canSimplifyCallFramePseudos(const MachineFunction &MF) const override;
; Shift second argument by one and store into returned register.
; ENABLE: lsl w0, w1, #1
; ENABLE: ret
-define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) #0 {
+define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) nounwind {
entry:
%tobool = icmp eq i32 %cond, 0
br i1 %tobool, label %if.else, label %if.then
; CHECK-NEXT: lsl w0, w1, #1
; DISABLE-NEXT: add sp, sp, #16
; CHECK-NEXT: ret
-define i32 @variadicFunc(i32 %cond, i32 %count, ...) #0 {
+define i32 @variadicFunc(i32 %cond, i32 %count, ...) nounwind {
entry:
%ap = alloca i8*, align 8
%tobool = icmp eq i32 %cond, 0
; DISABLE-NEXT: pop {r4, r7, pc}
;
; ENABLE-NEXT: bx lr
-define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) "no-frame-pointer-elim"="true" #0 {
+define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) "no-frame-pointer-elim"="true" nounwind {
entry:
%tobool = icmp eq i32 %cond, 0
br i1 %tobool, label %if.else, label %if.then
--- /dev/null
+# RUN: llc -mtriple thumbv7m-none-eabi -run-pass prologepilog %s -o - | FileCheck %s
+
+--- |
+ define void @throw() noreturn { unreachable }
+
+ define void @ret() nounwind { ret void }
+
+ define void @tables() nounwind noreturn uwtable { ret void }
+
+ define void @noret() noreturn nounwind {
+ start:
+ %p = alloca i32
+ store i32 42, i32* %p
+ unreachable
+ }
+...
+---
+# This function may return by exception. Check that $r4 is saved and restored.
+# CHECK-LABEL: name: throw
+# CHECK: killed $r4
+# CHECK: def $r4
+name: throw
+body: |
+ bb.0:
+ $r4 = IMPLICIT_DEF
+ tBX_RET 14, $noreg
+---
+---
+# This function may return. Check that $r4 is saved and restored.
+# CHECK-LABEL: name: ret
+# CHECK: killed $r4
+# CHECK: def $r4
+name: ret
+body: |
+ bb.0:
+ $r4 = IMPLICIT_DEF
+ tBX_RET 14, $noreg
+---
+---
+# This function needs correct unwind tables anyway. Check that $r4 is saved and
+# restored.
+# CHECK-LABEL: name: tables
+# CHECK: killed $r4
+# CHECK: def $r4
+name: tables
+body: |
+ bb.0:
+ $r4 = IMPLICIT_DEF
+ tBX_RET 14, $noreg
+---
+---
+# This function does not return. We need not save any CSR, but
+# other stack adjustments in the prologue are still necessary.
+# CHECK-LABEL: name: noret
+# CHECK-NOT: killed $r4
+# CHECK-NOT: def $r4
+# CHECK: $sp = frame-setup
+name: noret
+stack:
+ - { id: 0, name: p, offset: 0, size: 4, alignment: 4, local-offset: -4 }
+body: |
+ bb.0:
+ $r4 = IMPLICIT_DEF
+ tBX_RET 14, $noreg
+---
; Shift second argument by one and store into returned register.
; ENABLE: slwi 3, 4, 1
; ENABLE-NEXT: blr
-define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) #0 {
+define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) nounwind {
entry:
%tobool = icmp eq i32 %cond, 0
br i1 %tobool, label %if.else, label %if.then
;
; ENABLE-V5T-NEXT: {{LBB[0-9_]+}}: @ %if.end
; ENABLE-NEXT: bx lr
-define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) #0 {
+define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) nounwind {
entry:
%tobool = icmp eq i32 %cond, 0
br i1 %tobool, label %if.else, label %if.then
%tupl = type [9 x i32]
-declare fastcc void @l297(i32 %r10, i32 %r9, i32 %r8, i32 %r7, i32 %r6, i32 %r5, i32 %r3, i32 %r2) noreturn nounwind
-declare fastcc void @l298(i32 %r10, i32 %r9, i32 %r4) noreturn nounwind
+declare fastcc void @l297(i32 %r10, i32 %r9, i32 %r8, i32 %r7, i32 %r6, i32 %r5, i32 %r3, i32 %r2) nounwind
+declare fastcc void @l298(i32 %r10, i32 %r9, i32 %r4) nounwind
-define fastcc void @l186(%tupl* %r1) noreturn nounwind {
+define fastcc void @l186(%tupl* %r1) nounwind {
entry:
%ptr1 = getelementptr %tupl, %tupl* %r1, i32 0, i32 0
%r2 = load i32, i32* %ptr1
br i1 %cond, label %true, label %false
true:
- tail call fastcc void @l297(i32 %r10, i32 %r9, i32 %r8, i32 %r7, i32 %r6, i32 %r5, i32 %r3, i32 %r2) noreturn nounwind
+ tail call fastcc void @l297(i32 %r10, i32 %r9, i32 %r8, i32 %r7, i32 %r6, i32 %r5, i32 %r3, i32 %r2) nounwind
ret void
false:
- tail call fastcc void @l298(i32 %r10, i32 %r9, i32 %r4) noreturn nounwind
+ tail call fastcc void @l298(i32 %r10, i32 %r9, i32 %r4) nounwind
ret void
}
; ENABLE: addl %esi, %esi
; ENABLE-NEXT: movl %esi, %eax
; ENABLE-NEXT: retq
-define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) #0 {
+define i32 @loopInfoRestoreOutsideLoop(i32 %cond, i32 %N) nounwind {
entry:
%tobool = icmp eq i32 %cond, 0
br i1 %tobool, label %if.else, label %if.then