Skip to content

Commit e8b556b

Browse files
authored
[AVR] fix interrupt stack pointer restoration (#78)
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.
1 parent 016e67f commit e8b556b

File tree

2 files changed

+58
-10
lines changed

2 files changed

+58
-10
lines changed

llvm/lib/Target/AVR/AVRFrameLowering.cpp

+23-10
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,26 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
131131
.setMIFlag(MachineInstr::FrameSetup);
132132
}
133133

134+
static void restoreStatusRegister(MachineFunction &MF, MachineBasicBlock &MBB) {
135+
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
136+
137+
MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
138+
139+
DebugLoc DL = MBBI->getDebugLoc();
140+
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
141+
const AVRInstrInfo &TII = *STI.getInstrInfo();
142+
143+
// Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal
144+
// handlers at the very end of the function, just before reti.
145+
if (AFI->isInterruptOrSignalHandler()) {
146+
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
147+
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
148+
.addImm(0x3f)
149+
.addReg(AVR::R0, RegState::Kill);
150+
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPWRd), AVR::R1R0);
151+
}
152+
}
153+
134154
void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
135155
MachineBasicBlock &MBB) const {
136156
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
@@ -151,18 +171,9 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
151171
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
152172
const AVRInstrInfo &TII = *STI.getInstrInfo();
153173

154-
// Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal
155-
// handlers at the very end of the function, just before reti.
156-
if (AFI->isInterruptOrSignalHandler()) {
157-
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
158-
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
159-
.addImm(0x3f)
160-
.addReg(AVR::R0, RegState::Kill);
161-
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPWRd), AVR::R1R0);
162-
}
163-
164174
// Early exit if there is no need to restore the frame pointer.
165175
if (!FrameSize) {
176+
restoreStatusRegister(MF, MBB);
166177
return;
167178
}
168179

@@ -198,6 +209,8 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
198209
// Write back R29R28 to SP and temporarily disable interrupts.
199210
BuildMI(MBB, MBBI, DL, TII.get(AVR::SPWRITE), AVR::SP)
200211
.addReg(AVR::R29R28, RegState::Kill);
212+
213+
restoreStatusRegister(MF, MBB);
201214
}
202215

203216
// Return true if the specified function should have a dedicated frame

llvm/test/CodeGen/AVR/interrupts.ll

+35
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,40 @@ define void @signal_handler_via_attribute() #1 {
6464
ret void
6565
}
6666

67+
define avr_intrcc void @interrupt_alloca() {
68+
; CHECK-LABEL: interrupt_alloca:
69+
; CHECK: sei
70+
; CHECK-NEXT: push r0
71+
; CHECK-NEXT: push r1
72+
; CHECK-NEXT: in r0, 63
73+
; CHECK-NEXT: push r0
74+
; CHECK: clr r0
75+
; CHECK: push r28
76+
; CHECK-NEXT: push r29
77+
; CHECK-NEXT: in r28, 61
78+
; CHECK-NEXT: in r29, 62
79+
; CHECK-NEXT: sbiw r28, 1
80+
; CHECK-NEXT: in r0, 63
81+
; CHECK-NEXT: cli
82+
; CHECK-NEXT: out 62, r29
83+
; CHECK-NEXT: out 63, r0
84+
; CHECK-NEXT: out 61, r28
85+
; CHECK: adiw r28, 1
86+
; CHECK-NEXT: in r0, 63
87+
; CHECK-NEXT: cli
88+
; CHECK-NEXT: out 62, r29
89+
; CHECK-NEXT: out 63, r0
90+
; CHECK-NEXT: out 61, r28
91+
; CHECK-NEXT: pop r29
92+
; CHECK-NEXT: pop r28
93+
; CHECK: pop r0
94+
; CHECK-NEXT: out 63, r0
95+
; CHECK-NEXT: pop r1
96+
; CHECK-NEXT: pop r0
97+
; CHECK-NEXT: reti
98+
alloca i8
99+
ret void
100+
}
101+
67102
attributes #0 = { "interrupt" }
68103
attributes #1 = { "signal" }

0 commit comments

Comments
 (0)