[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();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
const AVRInstrInfo &TII = *STI.getInstrInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
bool HasFP = hasFP(MF);
// 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))
.addImm(0x07)
.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
// handlers before saving any other registers.
if (CallConv == CallingConv::AVR_INTR ||
CallConv == CallingConv::AVR_SIGNAL) {
if (AFI->isInterruptHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHWRr))
.addReg(AVR::R1R0, RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
@ -99,7 +99,6 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
}
const MachineFrameInfo &MFI = MF.getFrameInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize();
// Skip the callee-saved push instructions.
@ -142,13 +141,11 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF,
void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
MachineBasicBlock &MBB) const {
CallingConv::ID CallConv = MF.getFunction().getCallingConv();
bool isHandler = (CallConv == CallingConv::AVR_INTR ||
CallConv == CallingConv::AVR_SIGNAL);
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
// Early exit if the frame pointer is not needed in this function except for
// signal/interrupt handlers where special code generation is required.
if (!hasFP(MF) && !isHandler) {
if (!hasFP(MF) && !AFI->isInterruptHandler()) {
return;
}
@ -158,14 +155,13 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
DebugLoc DL = MBBI->getDebugLoc();
const MachineFrameInfo &MFI = MF.getFrameInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
unsigned FrameSize = MFI.getStackSize() - AFI->getCalleeSavedFrameSize();
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 (isHandler) {
if (AFI->isInterruptHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
.addImm(0x3f)

View File

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

View File

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

View File

@ -22,6 +22,7 @@
#include "AVR.h"
#include "AVRInstrInfo.h"
#include "AVRMachineFunctionInfo.h"
#include "AVRTargetMachine.h"
#include "MCTargetDesc/AVRMCTargetDesc.h"
@ -34,19 +35,21 @@ AVRRegisterInfo::AVRRegisterInfo() : AVRGenRegisterInfo(0) {}
const uint16_t *
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_Normal_SaveList);
: CSR_Normal_SaveList;
}
const uint32_t *
AVRRegisterInfo::getCallPreservedMask(const MachineFunction &MF,
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_Normal_RegMask);
: CSR_Normal_RegMask;
}
BitVector AVRRegisterInfo::getReservedRegs(const MachineFunction &MF) const {

View File

@ -16,6 +16,22 @@ define avr_intrcc void @interrupt_handler() {
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() {
; CHECK-LABEL: signal_handler:
; CHECK-NOT: sei
@ -31,3 +47,6 @@ define avr_signalcc void @signal_handler() {
; CHECK-NEXT: reti
ret void
}
attributes #0 = { "interrupt" }
attributes #1 = { "signal" }