From 8d3b77e3a6721cf752320ddebe697f7011e23f8d Mon Sep 17 00:00:00 2001 From: Andrew Dona-Couch Date: Thu, 1 Oct 2020 18:49:12 +1300 Subject: [PATCH] [AVR] fix interrupt stack pointer restoration 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. --- llvm/lib/Target/AVR/AVRFrameLowering.cpp | 33 +++++++++++++++------- llvm/test/CodeGen/AVR/interrupts.ll | 35 ++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 10 deletions(-) diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp index c95a553b86acf..757b41466c3f7 100644 --- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp +++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp @@ -131,6 +131,26 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF, .setMIFlag(MachineInstr::FrameSetup); } +static void restoreStatusRegister(MachineFunction &MF, MachineBasicBlock &MBB) { + const AVRMachineFunctionInfo *AFI = MF.getInfo(); + + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + + DebugLoc DL = MBBI->getDebugLoc(); + const AVRSubtarget &STI = MF.getSubtarget(); + 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(); @@ -151,18 +171,9 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF, const AVRSubtarget &STI = MF.getSubtarget(); 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; } @@ -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 diff --git a/llvm/test/CodeGen/AVR/interrupts.ll b/llvm/test/CodeGen/AVR/interrupts.ll index b402d867e12b2..c6550a0fb6ae5 100644 --- a/llvm/test/CodeGen/AVR/interrupts.ll +++ b/llvm/test/CodeGen/AVR/interrupts.ll @@ -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" }