Skip to content

Commit

Permalink
[AVR] fix interrupt stack pointer restoration (#78)
Browse files Browse the repository at this point in the history
This patch fixes a corruption of the stack pointer and several registers in any AVR interrupt with non-empty stack frame.  Previously, the callee-saved registers were popped before restoring the stack pointer, causing the pointer math to use the wrong base value while also corrupting the caller's register.  This change fixes the code to restore the stack pointer last before exiting the interrupt service routine.

https://bugs.llvm.org/show_bug.cgi?id=47253

Reviewed By: dylanmckay

Differential Revision: https://reviews.llvm.org/D87735

Patch by Andrew Dona-Couch.
  • Loading branch information
couchand authored and cuviper committed Oct 14, 2020
1 parent 70fd068 commit ee3a0e2
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 10 deletions.
33 changes: 23 additions & 10 deletions llvm/lib/Target/AVR/AVRFrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,26 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
.setMIFlag(MachineInstr::FrameSetup);
}

static void restoreStatusRegister(MachineFunction &MF, MachineBasicBlock &MBB) {
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();

MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();

DebugLoc DL = MBBI->getDebugLoc();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo();

// Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal
// handlers at the very end of the function, just before reti.
if (AFI->isInterruptOrSignalHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
.addImm(0x3f)
.addReg(AVR::R0, RegState::Kill);
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPWRd), AVR::R1R0);
}
}

void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
Expand All @@ -151,18 +171,9 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo();

// Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal
// handlers at the very end of the function, just before reti.
if (AFI->isInterruptOrSignalHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
.addImm(0x3f)
.addReg(AVR::R0, RegState::Kill);
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPWRd), AVR::R1R0);
}

// Early exit if there is no need to restore the frame pointer.
if (!FrameSize) {
restoreStatusRegister(MF, MBB);
return;
}

Expand Down Expand Up @@ -198,6 +209,8 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
// Write back R29R28 to SP and temporarily disable interrupts.
BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP)
.addReg(AVR::R29R28, RegState::Kill);

restoreStatusRegister(MF, MBB);
}

// Return true if the specified function should have a dedicated frame
Expand Down
35 changes: 35 additions & 0 deletions llvm/test/CodeGen/AVR/interrupts.ll
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,40 @@ define void @signal_handler_via_attribute() #1 {
ret void
}

define avr_intrcc void @interrupt_alloca() {
; CHECK-LABEL: interrupt_alloca:
; CHECK: sei
; CHECK-NEXT: push r0
; CHECK-NEXT: push r1
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK: clr r0
; CHECK: push r28
; CHECK-NEXT: push r29
; CHECK-NEXT: in r28, 61
; CHECK-NEXT: in r29, 62
; CHECK-NEXT: sbiw r28, 1
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: cli
; CHECK-NEXT: out 62, r29
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: out 61, r28
; CHECK: adiw r28, 1
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: cli
; CHECK-NEXT: out 62, r29
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: out 61, r28
; CHECK-NEXT: pop r29
; CHECK-NEXT: pop r28
; CHECK: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r1
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
alloca i8
ret void
}

attributes #0 = { "interrupt" }
attributes #1 = { "signal" }

0 comments on commit ee3a0e2

Please sign in to comment.