[AVR] Respect the 'interrupt' function attribute

In the past, AVR functions were only lowered with interrupt-specific
machine code if the function was defined with the "avr-interrupt" or
"avr-signal" calling conventions.

This patch modifies the backend so that if the function does not have a
special calling convention, but does have an "interrupt" attribute,
that function is interpreted as a function with interrupts.

This also extracts the "is this function an interrupt" logic from
several disparate places in the backend into one AVRMachineFunctionInfo
attribute.

Bug found by Wilhelm Meier.
This commit is contained in:
Dylan McKay 2020-03-31 19:00:18 +13:00
parent ebad678857
commit 339b34266c
5 changed files with 53 additions and 20 deletions

View File

@ -56,10 +56,11 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
DebugLoc DL = (MBBI != MBB.end()) ? MBBI->getDebugLoc() : DebugLoc(); DebugLoc DL = (MBBI != MBB.end()) ? MBBI->getDebugLoc() : DebugLoc();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo(); const AVRInstrInfo &TII = *STI.getInstrInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
bool HasFP = hasFP(MF); bool HasFP = hasFP(MF);
// Interrupt handlers re-enable interrupts in function entry. // Interrupt handlers re-enable interrupts in function entry.
if (CallConv == CallingConv::AVR_INTR) { if (AFI->isInterruptHandler() && CallConv != CallingConv::AVR_SIGNAL) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::BSETs)) BuildMI(MBB, MBBI, DL, TII.get(AVR::BSETs))
.addImm(0x07) .addImm(0x07)
.setMIFlag(MachineInstr::FrameSetup); .setMIFlag(MachineInstr::FrameSetup);
@ -74,8 +75,7 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
// Emit special prologue code to save R1, R0 and SREG in interrupt/signal // Emit special prologue code to save R1, R0 and SREG in interrupt/signal
// handlers before saving any other registers. // handlers before saving any other registers.
if (CallConv == CallingConv::AVR_INTR || if (AFI->isInterruptHandler()) {
CallConv == CallingConv::AVR_SIGNAL) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHWRr)) BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHWRr))
.addReg(AVR::R1R0, RegState::Kill) .addReg(AVR::R1R0, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup); .setMIFlag(MachineInstr::FrameSetup);
@ -99,7 +99,6 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
} }
const MachineFrameInfo &MFI = MF.getFrameInfo(); const MachineFrameInfo &MFI = MF.getFrameInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize(); unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize();
// Skip the callee-saved push instructions. // Skip the callee-saved push instructions.
@ -142,13 +141,11 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
void AVRFrameLowering::emitEpilogue(MachineFunction &MF, void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const { MachineBasicBlock &MBB) const {
CallingConv::ID CallConv = MF.getFunction().getCallingConv(); const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
bool isHandler = (CallConv == CallingConv::AVR_INTR ||
CallConv == CallingConv::AVR_SIGNAL);
// Early exit if the frame pointer is not needed in this function except for // Early exit if the frame pointer is not needed in this function except for
// signal/interrupt handlers where special code generation is required. // signal/interrupt handlers where special code generation is required.
if (!hasFP(MF) && !isHandler) { if (!hasFP(MF) && !AFI->isInterruptHandler()) {
return; return;
} }
@ -158,14 +155,13 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
DebugLoc DL = MBBI->getDebugLoc(); DebugLoc DL = MBBI->getDebugLoc();
const MachineFrameInfo &MFI = MF.getFrameInfo(); const MachineFrameInfo &MFI = MF.getFrameInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize(); unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>(); const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo(); const AVRInstrInfo &TII = *STI.getInstrInfo();
// Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal // Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal
// handlers at the very end of the function, just before reti. // handlers at the very end of the function, just before reti.
if (isHandler) { if (AFI->isInterruptHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0); BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr)) BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
.addImm(0x3f) .addImm(0x3f)

View File

@ -1429,10 +1429,12 @@ AVRTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
return Chain; return Chain;
} }
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
unsigned RetOpc = unsigned RetOpc =
(CallConv == CallingConv::AVR_INTR || CallConv == CallingConv::AVR_SIGNAL) AFI->isInterruptHandler()
? AVRISD::RETI_FLAG ? AVRISD::RETI_FLAG
: AVRISD::RET_FLAG; : AVRISD::RET_FLAG;
RetOps[0] = Chain; // Update chain. RetOps[0] = Chain; // Update chain.

View File

@ -31,6 +31,9 @@ class AVRMachineFunctionInfo : public MachineFunctionInfo {
/// used inside the function. /// used inside the function.
bool HasStackArgs; bool HasStackArgs;
/// Whether or not the function is an interrupt handler.
bool IsInterruptHandler;
/// Size of the callee-saved register portion of the /// Size of the callee-saved register portion of the
/// stack frame in bytes. /// stack frame in bytes.
unsigned CalleeSavedFrameSize; unsigned CalleeSavedFrameSize;
@ -41,11 +44,19 @@ class AVRMachineFunctionInfo : public MachineFunctionInfo {
public: public:
AVRMachineFunctionInfo() AVRMachineFunctionInfo()
: HasSpills(false), HasAllocas(false), HasStackArgs(false), : HasSpills(false), HasAllocas(false), HasStackArgs(false),
CalleeSavedFrameSize(0), VarArgsFrameIndex(0) {} IsInterruptHandler(false), CalleeSavedFrameSize(0),
VarArgsFrameIndex(0) {}
explicit AVRMachineFunctionInfo(MachineFunction &MF) explicit AVRMachineFunctionInfo(MachineFunction &MF)
: HasSpills(false), HasAllocas(false), HasStackArgs(false), : HasSpills(false), HasAllocas(false), HasStackArgs(false),
CalleeSavedFrameSize(0), VarArgsFrameIndex(0) {} CalleeSavedFrameSize(0), VarArgsFrameIndex(0) {
unsigned CallConv = MF.getFunction().getCallingConv();
this->IsInterruptHandler =
CallConv == CallingConv::AVR_INTR ||
CallConv == CallingConv::AVR_SIGNAL ||
MF.getFunction().hasFnAttribute("interrupt");
}
bool getHasSpills() const { return HasSpills; } bool getHasSpills() const { return HasSpills; }
void setHasSpills(bool B) { HasSpills = B; } void setHasSpills(bool B) { HasSpills = B; }
@ -56,6 +67,8 @@ public:
bool getHasStackArgs() const { return HasStackArgs; } bool getHasStackArgs() const { return HasStackArgs; }
void setHasStackArgs(bool B) { HasStackArgs = B; } void setHasStackArgs(bool B) { HasStackArgs = B; }
bool isInterruptHandler() const { return IsInterruptHandler; }
unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; } unsigned getCalleeSavedFrameSize() const { return CalleeSavedFrameSize; }
void setCalleeSavedFrameSize(unsigned Bytes) { CalleeSavedFrameSize = Bytes; } void setCalleeSavedFrameSize(unsigned Bytes) { CalleeSavedFrameSize = Bytes; }

View File

@ -22,6 +22,7 @@
#include "AVR.h" #include "AVR.h"
#include "AVRInstrInfo.h" #include "AVRInstrInfo.h"
#include "AVRMachineFunctionInfo.h"
#include "AVRTargetMachine.h" #include "AVRTargetMachine.h"
#include "MCTargetDesc/AVRMCTargetDesc.h" #include "MCTargetDesc/AVRMCTargetDesc.h"
@ -34,19 +35,21 @@ AVRRegisterInfo::AVRRegisterInfo() : AVRGenRegisterInfo(0) {}
const uint16_t * const uint16_t *
AVRRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { AVRRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
CallingConv::ID CC = MF->getFunction().getCallingConv(); const AVRMachineFunctionInfo *AFI = MF->getInfo<AVRMachineFunctionInfo>();
return ((CC == CallingConv::AVR_INTR || CC == CallingConv::AVR_SIGNAL) return AFI->isInterruptHandler()
? CSR_Interrupts_SaveList ? CSR_Interrupts_SaveList
: CSR_Normal_SaveList); : CSR_Normal_SaveList;
} }
const uint32_t * const uint32_t *
AVRRegisterInfo::getCallPreservedMask(const MachineFunction &MF, AVRRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
CallingConv::ID CC) const { CallingConv::ID CC) const {
return ((CC == CallingConv::AVR_INTR || CC == CallingConv::AVR_SIGNAL) const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
return AFI->isInterruptHandler()
? CSR_Interrupts_RegMask ? CSR_Interrupts_RegMask
: CSR_Normal_RegMask); : CSR_Normal_RegMask;
} }
BitVector AVRRegisterInfo::getReservedRegs(const MachineFunction &MF) const { BitVector AVRRegisterInfo::getReservedRegs(const MachineFunction &MF) const {

View File

@ -16,6 +16,22 @@ define avr_intrcc void @interrupt_handler() {
ret void ret void
} }
define void @interrupt_handler_via_ir_attribute() #0 {
; CHECK-LABEL: interrupt_handler_via_ir_attribute:
; CHECK: sei
; CHECK-NEXT: push r0
; CHECK-NEXT: push r1
; CHECK-NEXT: in r0, 63
; CHECK-NEXT: push r0
; CHECK: clr r0
; CHECK: pop r0
; CHECK-NEXT: out 63, r0
; CHECK-NEXT: pop r1
; CHECK-NEXT: pop r0
; CHECK-NEXT: reti
ret void
}
define avr_signalcc void @signal_handler() { define avr_signalcc void @signal_handler() {
; CHECK-LABEL: signal_handler: ; CHECK-LABEL: signal_handler:
; CHECK-NOT: sei ; CHECK-NOT: sei
@ -31,3 +47,6 @@ define avr_signalcc void @signal_handler() {
; CHECK-NEXT: reti ; CHECK-NEXT: reti
ret void ret void
} }
attributes #0 = { "interrupt" }
attributes #1 = { "signal" }