From 9f2026ed3f9b66a84fd537c66fc3d09ce74aedb0 Mon Sep 17 00:00:00 2001 From: Vasileios Kalintiris Date: Mon, 26 Oct 2015 12:38:43 +0000 Subject: [PATCH] [mips] Interrupt attribute support for mips32r2+. Summary: This patch adds support for using the "interrupt" attribute on Mips for interrupt handling functions. At this time only mips32r2+ with the o32 ABI with the static relocation model is supported. Unsupported configurations will be rejected Patch by Simon Dardis (+ clang-format & some trivial changes to follow the LLVM coding standards by me). Reviewers: mpf, dsanders Subscribers: dsanders, vkalintiris, llvm-commits Differential Revision: http://reviews.llvm.org/D10768 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@251286 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/Mips/MipsCallingConv.td | 25 ++ lib/Target/Mips/MipsISelLowering.cpp | 31 ++- lib/Target/Mips/MipsISelLowering.h | 7 + lib/Target/Mips/MipsInstrInfo.td | 6 + lib/Target/Mips/MipsMachineFunction.cpp | 15 ++ lib/Target/Mips/MipsMachineFunction.h | 16 +- lib/Target/Mips/MipsRegisterInfo.cpp | 10 + lib/Target/Mips/MipsSEFrameLowering.cpp | 195 +++++++++++++- lib/Target/Mips/MipsSEFrameLowering.h | 7 +- lib/Target/Mips/MipsSEISelLowering.cpp | 4 + lib/Target/Mips/MipsSEInstrInfo.cpp | 78 +++++- lib/Target/Mips/MipsSEInstrInfo.h | 2 + lib/Target/Mips/MipsSERegisterInfo.cpp | 6 +- test/CodeGen/Mips/interrupt-attr-64-fail.ll | 11 + test/CodeGen/Mips/interrupt-attr-args-fail.ll | 11 + test/CodeGen/Mips/interrupt-attr-fail.ll | 11 + test/CodeGen/Mips/interrupt-attr.ll | 244 ++++++++++++++++++ 17 files changed, 666 insertions(+), 13 deletions(-) create mode 100644 test/CodeGen/Mips/interrupt-attr-64-fail.ll create mode 100644 test/CodeGen/Mips/interrupt-attr-args-fail.ll create mode 100644 test/CodeGen/Mips/interrupt-attr-fail.ll create mode 100644 test/CodeGen/Mips/interrupt-attr.ll diff --git a/lib/Target/Mips/MipsCallingConv.td b/lib/Target/Mips/MipsCallingConv.td index 93e1908083c..0b4b7785af6 100644 --- a/lib/Target/Mips/MipsCallingConv.td +++ b/lib/Target/Mips/MipsCallingConv.td @@ -427,3 +427,28 @@ def CSR_Mips16RetHelper : CalleeSavedRegs<(add V0, V1, FP, (sequence "A%u", 3, 0), (sequence "S%u", 7, 0), (sequence "D%u", 15, 10))>; + +def CSR_Interrupt_32R6 : CalleeSavedRegs<(add (sequence "A%u", 3, 0), + (sequence "S%u", 7, 0), + (sequence "V%u", 1, 0), + (sequence "T%u", 9, 0), + RA, FP, GP, AT)>; + +def CSR_Interrupt_32 : CalleeSavedRegs<(add (sequence "A%u", 3, 0), + (sequence "S%u", 7, 0), + (sequence "V%u", 1, 0), + (sequence "T%u", 9, 0), + RA, FP, GP, AT, LO0, HI0)>; + +def CSR_Interrupt_64R6 : CalleeSavedRegs<(add (sequence "A%u_64", 3, 0), + (sequence "V%u_64", 1, 0), + (sequence "S%u_64", 7, 0), + (sequence "T%u_64", 9, 0), + RA_64, FP_64, GP_64, AT_64)>; + +def CSR_Interrupt_64 : CalleeSavedRegs<(add (sequence "A%u_64", 3, 0), + (sequence "S%u_64", 7, 0), + (sequence "T%u_64", 9, 0), + (sequence "V%u_64", 1, 0), + RA_64, FP_64, GP_64, AT_64, + LO0_64, HI0_64)>; diff --git a/lib/Target/Mips/MipsISelLowering.cpp b/lib/Target/Mips/MipsISelLowering.cpp index 5f97921dccc..a36725a9208 100644 --- a/lib/Target/Mips/MipsISelLowering.cpp +++ b/lib/Target/Mips/MipsISelLowering.cpp @@ -117,6 +117,7 @@ const char *MipsTargetLowering::getTargetNodeName(unsigned Opcode) const { case MipsISD::GPRel: return "MipsISD::GPRel"; case MipsISD::ThreadPointer: return "MipsISD::ThreadPointer"; case MipsISD::Ret: return "MipsISD::Ret"; + case MipsISD::ERet: return "MipsISD::ERet"; case MipsISD::EH_RETURN: return "MipsISD::EH_RETURN"; case MipsISD::FPBrcond: return "MipsISD::FPBrcond"; case MipsISD::FPCmp: return "MipsISD::FPCmp"; @@ -2948,8 +2949,12 @@ MipsTargetLowering::LowerFormalArguments(SDValue Chain, MipsCCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, *DAG.getContext()); CCInfo.AllocateStack(ABI.GetCalleeAllocdArgSizeInBytes(CallConv), 1); - Function::const_arg_iterator FuncArg = - DAG.getMachineFunction().getFunction()->arg_begin(); + const Function *Func = DAG.getMachineFunction().getFunction(); + Function::const_arg_iterator FuncArg = Func->arg_begin(); + + if (Func->hasFnAttribute("interrupt")) + assert(Func->arg_empty() && + "Functions with the interrupt attribute cannot have arguments!"); CCInfo.AnalyzeFormalArguments(Ins, CC_Mips_FixedArg); MipsFI->setFormalArgInfo(CCInfo.getNextStackOffset(), @@ -3101,8 +3106,20 @@ MipsTargetLowering::shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const } SDValue -MipsTargetLowering::LowerReturn(SDValue Chain, - CallingConv::ID CallConv, bool IsVarArg, +MipsTargetLowering::LowerInterruptReturn(SmallVectorImpl &RetOps, + SDLoc DL, SelectionDAG &DAG) const { + + MachineFunction &MF = DAG.getMachineFunction(); + MipsFunctionInfo *MipsFI = MF.getInfo(); + + MipsFI->setISR(); + + return DAG.getNode(MipsISD::ERet, DL, MVT::Other, RetOps); +} + +SDValue +MipsTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, + bool IsVarArg, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, SDLoc DL, SelectionDAG &DAG) const { @@ -3195,7 +3212,11 @@ MipsTargetLowering::LowerReturn(SDValue Chain, if (Flag.getNode()) RetOps.push_back(Flag); - // Return on Mips is always a "jr $ra" + // ISRs must use "eret". + if (DAG.getMachineFunction().getFunction()->hasFnAttribute("interrupt")) + return LowerInterruptReturn(RetOps, DL, DAG); + + // Standard return on Mips is a "jr $ra" return DAG.getNode(MipsISD::Ret, DL, MVT::Other, RetOps); } diff --git a/lib/Target/Mips/MipsISelLowering.h b/lib/Target/Mips/MipsISelLowering.h index 632eefd6009..85462275edb 100644 --- a/lib/Target/Mips/MipsISelLowering.h +++ b/lib/Target/Mips/MipsISelLowering.h @@ -67,6 +67,10 @@ namespace llvm { // Return Ret, + // Interrupt, exception, error trap Return + ERet, + + // Software Exception Return. EH_RETURN, // Node used to extract integer from accumulator. @@ -482,6 +486,9 @@ namespace llvm { const SmallVectorImpl &OutVals, SDLoc dl, SelectionDAG &DAG) const override; + SDValue LowerInterruptReturn(SmallVectorImpl &RetOps, SDLoc DL, + SelectionDAG &DAG) const; + bool shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const override; // Inline asm support diff --git a/lib/Target/Mips/MipsInstrInfo.td b/lib/Target/Mips/MipsInstrInfo.td index ecb535f6d57..9efdafdf5f1 100644 --- a/lib/Target/Mips/MipsInstrInfo.td +++ b/lib/Target/Mips/MipsInstrInfo.td @@ -77,6 +77,9 @@ def MipsThreadPointer: SDNode<"MipsISD::ThreadPointer", SDT_MipsThreadPointer>; def MipsRet : SDNode<"MipsISD::Ret", SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def MipsERet : SDNode<"MipsISD::ERet", SDTNone, + [SDNPHasChain, SDNPOptInGlue, SDNPSideEffect]>; + // These are target-independent nodes, but have target-specific formats. def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_MipsCallSeqStart, [SDNPHasChain, SDNPSideEffect, SDNPOutGlue]>; @@ -1146,6 +1149,9 @@ class TrapBase let isReturn=1, isTerminator=1, hasDelaySlot=1, isBarrier=1, hasCtrlDep=1 in def RetRA : PseudoSE<(outs), (ins), [(MipsRet)]>; +let isReturn=1, isTerminator=1, isBarrier=1, hasCtrlDep=1, hasSideEffects=1 in +def ERet : PseudoSE<(outs), (ins), [(MipsERet)]>; + let Defs = [SP], Uses = [SP], hasSideEffects = 1 in { def ADJCALLSTACKDOWN : MipsPseudo<(outs), (ins i32imm:$amt), [(callseq_start timm:$amt)]>; diff --git a/lib/Target/Mips/MipsMachineFunction.cpp b/lib/Target/Mips/MipsMachineFunction.cpp index 839760cc8a4..c7d2738af1d 100644 --- a/lib/Target/Mips/MipsMachineFunction.cpp +++ b/lib/Target/Mips/MipsMachineFunction.cpp @@ -75,11 +75,26 @@ void MipsFunctionInfo::createEhDataRegsFI() { } } +void MipsFunctionInfo::createISRRegFI() { + // ISRs require spill slots for Status & ErrorPC Coprocessor 0 registers. + // The current implementation only supports Mips32r2+ not Mips64rX. Status + // is always 32 bits, ErrorPC is 32 or 64 bits dependant on architecture, + // however Mips32r2+ is the supported architecture. + const TargetRegisterClass *RC = &Mips::GPR32RegClass; + + for (int I = 0; I < 2; ++I) + ISRDataRegFI[I] = MF.getFrameInfo()->CreateStackObject( + RC->getSize(), RC->getAlignment(), false); +} + bool MipsFunctionInfo::isEhDataRegFI(int FI) const { return CallsEhReturn && (FI == EhDataRegFI[0] || FI == EhDataRegFI[1] || FI == EhDataRegFI[2] || FI == EhDataRegFI[3]); } +bool MipsFunctionInfo::isISRRegFI(int FI) const { + return IsISR && (FI == ISRDataRegFI[0] || FI == ISRDataRegFI[1]); +} MachinePointerInfo MipsFunctionInfo::callPtrInfo(const char *ES) { return MachinePointerInfo(MF.getPSVManager().getExternalSymbolCallEntry(ES)); } diff --git a/lib/Target/Mips/MipsMachineFunction.h b/lib/Target/Mips/MipsMachineFunction.h index a1b2e817371..a2f6ee03604 100644 --- a/lib/Target/Mips/MipsMachineFunction.h +++ b/lib/Target/Mips/MipsMachineFunction.h @@ -34,7 +34,7 @@ class MipsFunctionInfo : public MachineFunctionInfo { public: MipsFunctionInfo(MachineFunction &MF) : MF(MF), SRetReturnReg(0), GlobalBaseReg(0), Mips16SPAliasReg(0), - VarArgsFrameIndex(0), CallsEhReturn(false), SaveS2(false), + VarArgsFrameIndex(0), CallsEhReturn(false), IsISR(false), SaveS2(false), MoveF64ViaSpillFI(-1) {} ~MipsFunctionInfo(); @@ -70,6 +70,14 @@ public: /// object representing a GOT entry for an external function. MachinePointerInfo callPtrInfo(const char *ES); + // Functions with the "interrupt" attribute require special prologues, + // epilogues and additional spill slots. + bool isISR() const { return IsISR; } + void setISR() { IsISR = true; } + void createISRRegFI(); + int getISRRegFI(unsigned Reg) const { return ISRDataRegFI[Reg]; } + bool isISRRegFI(int FI) const; + /// Create a MachinePointerInfo that has a GlobalValuePseudoSourceValue object /// representing a GOT entry for a global function. MachinePointerInfo callPtrInfo(const GlobalValue *GV); @@ -116,6 +124,12 @@ private: /// Frame objects for spilling eh data registers. int EhDataRegFI[4]; + /// ISR - Whether the function is an Interrupt Service Routine. + bool IsISR; + + /// Frame objects for spilling C0_STATUS, C0_EPC + int ISRDataRegFI[2]; + // saveS2 bool SaveS2; diff --git a/lib/Target/Mips/MipsRegisterInfo.cpp b/lib/Target/Mips/MipsRegisterInfo.cpp index 775d8006fdf..28e5a425849 100644 --- a/lib/Target/Mips/MipsRegisterInfo.cpp +++ b/lib/Target/Mips/MipsRegisterInfo.cpp @@ -84,6 +84,16 @@ MipsRegisterInfo::getRegPressureLimit(const TargetRegisterClass *RC, const MCPhysReg * MipsRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const { const MipsSubtarget &Subtarget = MF->getSubtarget(); + const Function *F = MF->getFunction(); + if (F->hasFnAttribute("interrupt")) { + if (Subtarget.hasMips64()) + return Subtarget.hasMips64r6() ? CSR_Interrupt_64R6_SaveList + : CSR_Interrupt_64_SaveList; + else + return Subtarget.hasMips32r6() ? CSR_Interrupt_32R6_SaveList + : CSR_Interrupt_32_SaveList; + } + if (Subtarget.isSingleFloat()) return CSR_SingleFloatOnly_SaveList; diff --git a/lib/Target/Mips/MipsSEFrameLowering.cpp b/lib/Target/Mips/MipsSEFrameLowering.cpp index e498fbfbd84..70f4a637217 100644 --- a/lib/Target/Mips/MipsSEFrameLowering.cpp +++ b/lib/Target/Mips/MipsSEFrameLowering.cpp @@ -17,6 +17,7 @@ #include "MipsMachineFunction.h" #include "MipsSEInstrInfo.h" #include "MipsSubtarget.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstrBuilder.h" @@ -415,6 +416,9 @@ void MipsSEFrameLowering::emitPrologue(MachineFunction &MF, BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION)) .addCFIIndex(CFIIndex); + if (MF.getFunction()->hasFnAttribute("interrupt")) + emitInterruptPrologueStub(MF, MBB); + const std::vector &CSI = MFI->getCalleeSavedInfo(); if (CSI.size()) { @@ -530,6 +534,135 @@ void MipsSEFrameLowering::emitPrologue(MachineFunction &MF, } } +void MipsSEFrameLowering::emitInterruptPrologueStub( + MachineFunction &MF, MachineBasicBlock &MBB) const { + + MipsFunctionInfo *MipsFI = MF.getInfo(); + MachineBasicBlock::iterator MBBI = MBB.begin(); + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + + // Report an error the target doesn't support Mips32r2 or later. + // The epilogue relies on the use of the "ehb" to clear execution + // hazards. Pre R2 Mips relies on an implementation defined number + // of "ssnop"s to clear the execution hazard. Support for ssnop hazard + // clearing is not provided so reject that configuration. + if (!STI.hasMips32r2()) + report_fatal_error( + "\"interrupt\" attribute is not supported on pre-r2 MIPS or" + "Mips16 targets."); + + // The GP register contains the "user" value, so we cannot perform + // any gp relative loads until we restore the "kernel" or "system" gp + // value. Until support is written we shall only accept the static + // relocation model. + if ((STI.getRelocationModel() != Reloc::Static)) + report_fatal_error("\"interrupt\" attribute is only supported for the " + "static relocation model on MIPS at the present time."); + + if (!STI.isABI_O32() || STI.hasMips64()) + report_fatal_error("\"interrupt\" attribute is only supported for the " + "O32 ABI on MIPS32r2+ at the present time."); + + // Perform ISR handling like GCC + StringRef IntKind = + MF.getFunction()->getFnAttribute("interrupt").getValueAsString(); + const TargetRegisterClass *PtrRC = &Mips::GPR32RegClass; + + // EIC interrupt handling needs to read the Cause register to disable + // interrupts. + if (IntKind == "eic") { + // Coprocessor registers are always live per se. + MBB.addLiveIn(Mips::COP013); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::MFC0), Mips::K0) + .addReg(Mips::COP013) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::EXT), Mips::K0) + .addReg(Mips::K0) + .addImm(10) + .addImm(6) + .setMIFlag(MachineInstr::FrameSetup); + } + + // Fetch and spill EPC + MBB.addLiveIn(Mips::COP014); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::MFC0), Mips::K1) + .addReg(Mips::COP014) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + + STI.getInstrInfo()->storeRegToStack(MBB, MBBI, Mips::K1, false, + MipsFI->getISRRegFI(0), PtrRC, + STI.getRegisterInfo(), 0); + + // Fetch and Spill Status + MBB.addLiveIn(Mips::COP012); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::MFC0), Mips::K1) + .addReg(Mips::COP012) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + + STI.getInstrInfo()->storeRegToStack(MBB, MBBI, Mips::K1, false, + MipsFI->getISRRegFI(1), PtrRC, + STI.getRegisterInfo(), 0); + + // Build the configuration for disabling lower priority interrupts. Non EIC + // interrupts need to be masked off with zero, EIC from the Cause register. + unsigned InsPosition = 8; + unsigned InsSize = 0; + unsigned SrcReg = Mips::ZERO; + + // If the interrupt we're tied to is the EIC, switch the source for the + // masking off interrupts to the cause register. + if (IntKind == "eic") { + SrcReg = Mips::K0; + InsPosition = 10; + InsSize = 6; + } else + InsSize = StringSwitch(IntKind) + .Case("sw0", 1) + .Case("sw1", 2) + .Case("hw0", 3) + .Case("hw1", 4) + .Case("hw2", 5) + .Case("hw3", 6) + .Case("hw4", 7) + .Case("hw5", 8) + .Default(0); + assert(InsSize != 0 && "Unknown interrupt type!"); + + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::INS), Mips::K1) + .addReg(SrcReg) + .addImm(InsPosition) + .addImm(InsSize) + .addReg(Mips::K1) + .setMIFlag(MachineInstr::FrameSetup); + + // Mask off KSU, ERL, EXL + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::INS), Mips::K1) + .addReg(Mips::ZERO) + .addImm(1) + .addImm(4) + .addReg(Mips::K1) + .setMIFlag(MachineInstr::FrameSetup); + + // Disable the FPU as we are not spilling those register sets. + if (!STI.useSoftFloat()) + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::INS), Mips::K1) + .addReg(Mips::ZERO) + .addImm(29) + .addImm(1) + .addReg(Mips::K1) + .setMIFlag(MachineInstr::FrameSetup); + + // Set the new status + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::MTC0), Mips::COP012) + .addReg(Mips::K1) + .addImm(0) + .setMIFlag(MachineInstr::FrameSetup); +} + void MipsSEFrameLowering::emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const { MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); @@ -541,7 +674,7 @@ void MipsSEFrameLowering::emitEpilogue(MachineFunction &MF, const MipsRegisterInfo &RegInfo = *static_cast(STI.getRegisterInfo()); - DebugLoc dl = MBBI->getDebugLoc(); + DebugLoc DL = MBBI->getDebugLoc(); MipsABIInfo ABI = STI.getABI(); unsigned SP = ABI.GetStackPtr(); unsigned FP = ABI.GetFramePtr(); @@ -557,7 +690,7 @@ void MipsSEFrameLowering::emitEpilogue(MachineFunction &MF, --I; // Insert instruction "move $sp, $fp" at this location. - BuildMI(MBB, I, dl, TII.get(MOVE), SP).addReg(FP).addReg(ZERO); + BuildMI(MBB, I, DL, TII.get(MOVE), SP).addReg(FP).addReg(ZERO); } if (MipsFI->callsEhReturn()) { @@ -576,6 +709,9 @@ void MipsSEFrameLowering::emitEpilogue(MachineFunction &MF, } } + if (MF.getFunction()->hasFnAttribute("interrupt")) + emitInterruptEpilogueStub(MF, MBB); + // Get the number of bytes from FrameInfo uint64_t StackSize = MFI->getStackSize(); @@ -586,6 +722,37 @@ void MipsSEFrameLowering::emitEpilogue(MachineFunction &MF, TII.adjustStackPtr(SP, StackSize, MBB, MBBI); } +void MipsSEFrameLowering::emitInterruptEpilogueStub( + MachineFunction &MF, MachineBasicBlock &MBB) const { + + MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr(); + MipsFunctionInfo *MipsFI = MF.getInfo(); + DebugLoc DL = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + + // Perform ISR handling like GCC + const TargetRegisterClass *PtrRC = &Mips::GPR32RegClass; + + // Disable Interrupts. + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::DI), Mips::ZERO); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::EHB)); + + // Restore EPC + STI.getInstrInfo()->loadRegFromStackSlot(MBB, MBBI, Mips::K1, + MipsFI->getISRRegFI(0), PtrRC, + STI.getRegisterInfo()); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::MTC0), Mips::COP014) + .addReg(Mips::K1) + .addImm(0); + + // Restore Status + STI.getInstrInfo()->loadRegFromStackSlot(MBB, MBBI, Mips::K1, + MipsFI->getISRRegFI(1), PtrRC, + STI.getRegisterInfo()); + BuildMI(MBB, MBBI, DL, STI.getInstrInfo()->get(Mips::MTC0), Mips::COP012) + .addReg(Mips::K1) + .addImm(0); +} + bool MipsSEFrameLowering:: spillCalleeSavedRegisters(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, @@ -607,6 +774,26 @@ spillCalleeSavedRegisters(MachineBasicBlock &MBB, if (!IsRAAndRetAddrIsTaken) EntryBlock->addLiveIn(Reg); + // ISRs require HI/LO to be spilled into kernel registers to be then + // spilled to the stack frame. + bool IsLOHI = (Reg == Mips::LO0 || Reg == Mips::LO0_64 || + Reg == Mips::HI0 || Reg == Mips::HI0_64); + const Function *Func = MBB.getParent()->getFunction(); + if (IsLOHI && Func->hasFnAttribute("interrupt")) { + DebugLoc DL = MI->getDebugLoc(); + + unsigned Op = 0; + if (!STI.getABI().ArePtrs64bit()) { + Op = (Reg == Mips::HI0) ? Mips::MFHI : Mips::MFLO; + Reg = Mips::K0; + } else { + Op = (Reg == Mips::HI0) ? Mips::MFHI64 : Mips::MFLO64; + Reg = Mips::K0_64; + } + BuildMI(MBB, MI, DL, TII.get(Op), Mips::K0) + .setMIFlag(MachineInstr::FrameSetup); + } + // Insert the spill to the stack frame. bool IsKill = !IsRAAndRetAddrIsTaken; const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); @@ -657,6 +844,10 @@ void MipsSEFrameLowering::determineCalleeSaves(MachineFunction &MF, if (MipsFI->callsEhReturn()) MipsFI->createEhDataRegsFI(); + // Create spill slots for Coprocessor 0 registers if function is an ISR. + if (MipsFI->isISR()) + MipsFI->createISRRegFI(); + // Expand pseudo instructions which load, store or copy accumulators. // Add an emergency spill slot if a pseudo was expanded. if (ExpandPseudo(MF).expand()) { diff --git a/lib/Target/Mips/MipsSEFrameLowering.h b/lib/Target/Mips/MipsSEFrameLowering.h index 9cb32e6c782..f3050d0718b 100644 --- a/lib/Target/Mips/MipsSEFrameLowering.h +++ b/lib/Target/Mips/MipsSEFrameLowering.h @@ -37,8 +37,13 @@ public: void determineCalleeSaves(MachineFunction &MF, BitVector &SavedRegs, RegScavenger *RS) const override; unsigned ehDataReg(unsigned I) const; -}; +private: + void emitInterruptEpilogueStub(MachineFunction &MF, + MachineBasicBlock &MBB) const; + void emitInterruptPrologueStub(MachineFunction &MF, + MachineBasicBlock &MBB) const; +}; } // End llvm namespace #endif diff --git a/lib/Target/Mips/MipsSEISelLowering.cpp b/lib/Target/Mips/MipsSEISelLowering.cpp index 5167b6baf98..efe22fba98c 100644 --- a/lib/Target/Mips/MipsSEISelLowering.cpp +++ b/lib/Target/Mips/MipsSEISelLowering.cpp @@ -1181,6 +1181,10 @@ bool MipsSETargetLowering::isEligibleForTailCallOptimization( if (!EnableMipsTailCalls) return false; + // Exception has to be cleared with eret. + if (FI.isISR()) + return false; + // Return false if either the callee or caller has a byval argument. if (CCInfo.getInRegsParamsCount() > 0 || FI.hasByvalArg()) return false; diff --git a/lib/Target/Mips/MipsSEInstrInfo.cpp b/lib/Target/Mips/MipsSEInstrInfo.cpp index d05f95fbd2b..e6f7fe9aae1 100644 --- a/lib/Target/Mips/MipsSEInstrInfo.cpp +++ b/lib/Target/Mips/MipsSEInstrInfo.cpp @@ -212,6 +212,33 @@ storeRegToStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Opc = Mips::ST_W; else if (RC->hasType(MVT::v2i64) || RC->hasType(MVT::v2f64)) Opc = Mips::ST_D; + else if (Mips::LO32RegClass.hasSubClassEq(RC)) + Opc = Mips::SW; + else if (Mips::LO64RegClass.hasSubClassEq(RC)) + Opc = Mips::SD; + else if (Mips::HI32RegClass.hasSubClassEq(RC)) + Opc = Mips::SW; + else if (Mips::HI64RegClass.hasSubClassEq(RC)) + Opc = Mips::SD; + + // Hi, Lo are normally caller save but they are callee save + // for interrupt handling. + const Function *Func = MBB.getParent()->getFunction(); + if (Func->hasFnAttribute("interrupt")) { + if (Mips::HI32RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Mips::MFHI), Mips::K0); + SrcReg = Mips::K0; + } else if (Mips::HI64RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Mips::MFHI64), Mips::K0_64); + SrcReg = Mips::K0_64; + } else if (Mips::LO32RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Mips::MFLO), Mips::K0); + SrcReg = Mips::K0; + } else if (Mips::LO64RegClass.hasSubClassEq(RC)) { + BuildMI(MBB, I, DL, get(Mips::MFLO64), Mips::K0_64); + SrcReg = Mips::K0_64; + } + } assert(Opc && "Register class not handled!"); BuildMI(MBB, I, DL, get(Opc)).addReg(SrcReg, getKillRegState(isKill)) @@ -227,6 +254,11 @@ loadRegFromStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, MachineMemOperand *MMO = GetMemOperand(MBB, FI, MachineMemOperand::MOLoad); unsigned Opc = 0; + const Function *Func = MBB.getParent()->getFunction(); + bool ReqIndirectLoad = Func->hasFnAttribute("interrupt") && + (DestReg == Mips::LO0 || DestReg == Mips::LO0_64 || + DestReg == Mips::HI0 || DestReg == Mips::HI0_64); + if (Mips::GPR32RegClass.hasSubClassEq(RC)) Opc = Mips::LW; else if (Mips::GPR64RegClass.hasSubClassEq(RC)) @@ -253,10 +285,44 @@ loadRegFromStack(MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Opc = Mips::LD_W; else if (RC->hasType(MVT::v2i64) || RC->hasType(MVT::v2f64)) Opc = Mips::LD_D; + else if (Mips::HI32RegClass.hasSubClassEq(RC)) + Opc = Mips::LW; + else if (Mips::HI64RegClass.hasSubClassEq(RC)) + Opc = Mips::LD; + else if (Mips::LO32RegClass.hasSubClassEq(RC)) + Opc = Mips::LW; + else if (Mips::LO64RegClass.hasSubClassEq(RC)) + Opc = Mips::LD; assert(Opc && "Register class not handled!"); - BuildMI(MBB, I, DL, get(Opc), DestReg).addFrameIndex(FI).addImm(Offset) - .addMemOperand(MMO); + + if (!ReqIndirectLoad) + BuildMI(MBB, I, DL, get(Opc), DestReg) + .addFrameIndex(FI) + .addImm(Offset) + .addMemOperand(MMO); + else { + // Load HI/LO through K0. Notably the DestReg is encoded into the + // instruction itself. + unsigned Reg = Mips::K0; + unsigned LdOp = Mips::MTLO; + if (DestReg == Mips::HI0) + LdOp = Mips::MTHI; + + if (Subtarget.getABI().ArePtrs64bit()) { + Reg = Mips::K0_64; + if (DestReg == Mips::HI0_64) + LdOp = Mips::MTHI64; + else + LdOp = Mips::MTLO64; + } + + BuildMI(MBB, I, DL, get(Opc), Reg) + .addFrameIndex(FI) + .addImm(Offset) + .addMemOperand(MMO); + BuildMI(MBB, I, DL, get(LdOp)).addReg(Reg); + } } bool MipsSEInstrInfo::expandPostRAPseudo(MachineBasicBlock::iterator MI) const { @@ -270,6 +336,9 @@ bool MipsSEInstrInfo::expandPostRAPseudo(MachineBasicBlock::iterator MI) const { case Mips::RetRA: expandRetRA(MBB, MI); break; + case Mips::ERet: + expandERet(MBB, MI); + break; case Mips::PseudoMFHI: Opc = isMicroMips ? Mips::MFHI16_MM : Mips::MFHI; expandPseudoMFHiLo(MBB, MI, Opc); @@ -437,6 +506,11 @@ void MipsSEInstrInfo::expandRetRA(MachineBasicBlock &MBB, BuildMI(MBB, I, I->getDebugLoc(), get(Mips::PseudoReturn)).addReg(Mips::RA); } +void MipsSEInstrInfo::expandERet(MachineBasicBlock &MBB, + MachineBasicBlock::iterator I) const { + BuildMI(MBB, I, I->getDebugLoc(), get(Mips::ERET)); +} + std::pair MipsSEInstrInfo::compareOpndSize(unsigned Opc, const MachineFunction &MF) const { diff --git a/lib/Target/Mips/MipsSEInstrInfo.h b/lib/Target/Mips/MipsSEInstrInfo.h index bebbabf7b83..5d73545ef6b 100644 --- a/lib/Target/Mips/MipsSEInstrInfo.h +++ b/lib/Target/Mips/MipsSEInstrInfo.h @@ -82,6 +82,8 @@ private: void expandRetRA(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const; + void expandERet(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const; + std::pair compareOpndSize(unsigned Opc, const MachineFunction &MF) const; diff --git a/lib/Target/Mips/MipsSERegisterInfo.cpp b/lib/Target/Mips/MipsSERegisterInfo.cpp index 132c3a1001a..b1e2885f5ba 100644 --- a/lib/Target/Mips/MipsSERegisterInfo.cpp +++ b/lib/Target/Mips/MipsSERegisterInfo.cpp @@ -126,17 +126,19 @@ void MipsSERegisterInfo::eliminateFI(MachineBasicBlock::iterator II, } bool EhDataRegFI = MipsFI->isEhDataRegFI(FrameIndex); - + bool IsISRRegFI = MipsFI->isISRRegFI(FrameIndex); // The following stack frame objects are always referenced relative to $sp: // 1. Outgoing arguments. // 2. Pointer to dynamically allocated stack space. // 3. Locations for callee-saved registers. // 4. Locations for eh data registers. + // 5. Locations for ISR saved Coprocessor 0 registers 12 & 14. // Everything else is referenced relative to whatever register // getFrameRegister() returns. unsigned FrameReg; - if ((FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI) || EhDataRegFI) + if ((FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI) || EhDataRegFI || + IsISRRegFI) FrameReg = ABI.GetStackPtr(); else if (RegInfo->needsStackRealignment(MF)) { if (MFI->hasVarSizedObjects() && !MFI->isFixedObjectIndex(FrameIndex)) diff --git a/test/CodeGen/Mips/interrupt-attr-64-fail.ll b/test/CodeGen/Mips/interrupt-attr-64-fail.ll new file mode 100644 index 00000000000..49929d218e5 --- /dev/null +++ b/test/CodeGen/Mips/interrupt-attr-64-fail.ll @@ -0,0 +1,11 @@ +; RUN: llc -mcpu=mips64r6 -march=mipsel -relocation-model=static -o - %s | FileCheck %s +; XFAIL: * + +define void @isr_sw0() #0 { + call void bitcast (void (...)* @write to void ()*)() +} + +declare void @write(...) + +attributes #0 = { "interrupt"="sw0" } + diff --git a/test/CodeGen/Mips/interrupt-attr-args-fail.ll b/test/CodeGen/Mips/interrupt-attr-args-fail.ll new file mode 100644 index 00000000000..9f15a15ca9c --- /dev/null +++ b/test/CodeGen/Mips/interrupt-attr-args-fail.ll @@ -0,0 +1,11 @@ +; RUN: llc -mcpu=mips32r2 -march=mipsel -relocation-model=static -o - %s | FileCheck %s +; XFAIL: * + +define void @isr_sw0(i8 signext %n) #0 { + call void bitcast (void (...)* @write to void ()*)() +} + +declare void @write(...) + +attributes #0 = { "interrupt"="sw0" } + diff --git a/test/CodeGen/Mips/interrupt-attr-fail.ll b/test/CodeGen/Mips/interrupt-attr-fail.ll new file mode 100644 index 00000000000..d3914ee86f6 --- /dev/null +++ b/test/CodeGen/Mips/interrupt-attr-fail.ll @@ -0,0 +1,11 @@ +; RUN: llc -mcpu=mips32 -march=mipsel -relocation-model=static -o - %s | FileCheck %s +; XFAIL: * + +define void @isr_sw0() #0 { + call void bitcast (void (...)* @write to void ()*)() +} + +declare void @write(...) + +attributes #0 = { "interrupt"="sw0" } + diff --git a/test/CodeGen/Mips/interrupt-attr.ll b/test/CodeGen/Mips/interrupt-attr.ll new file mode 100644 index 00000000000..6cfb0c3ebd5 --- /dev/null +++ b/test/CodeGen/Mips/interrupt-attr.ll @@ -0,0 +1,244 @@ +; RUN: llc -mcpu=mips32r2 -march=mipsel -relocation-model=static -o - %s | FileCheck %s + +define void @isr_sw0() #0 { +; CHECK-LABEL: isr_sw0: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, [[R1:[0-9]+]]($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, [[R2:[0-9]+]]($sp) +; CHECK: ins $27, $zero, 8, 1 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ; Must save all registers +; CHECK: sw $7, {{[0-9]+}}($sp) +; CHECK: sw $6, {{[0-9]+}}($sp) +; CHECK: sw $5, {{[0-9]+}}($sp) +; CHECK: sw $4, {{[0-9]+}}($sp) +; CHECK: sw $3, {{[0-9]+}}($sp) +; CHECK: sw $2, {{[0-9]+}}($sp) +; CHECK: sw $25, {{[0-9]+}}($sp) +; CHECK: sw $24, {{[0-9]+}}($sp) +; CHECK: sw $15, {{[0-9]+}}($sp) +; CHECK: sw $14, {{[0-9]+}}($sp) +; CHECK: sw $13, {{[0-9]+}}($sp) +; CHECK: sw $12, {{[0-9]+}}($sp) +; CHECK: sw $11, {{[0-9]+}}($sp) +; CHECK: sw $10, {{[0-9]+}}($sp) +; CHECK: sw $9, {{[0-9]+}}($sp) +; CHECK: sw $8, {{[0-9]+}}($sp) +; CHECK: sw $ra, [[R5:[0-9]+]]($sp) +; CHECK: sw $gp, {{[0-9]+}}($sp) +; CHECK: sw $1, {{[0-9]+}}($sp) +; CHECK: mflo $26 +; CHECK: sw $26, [[R3:[0-9]+]]($sp) +; CHECK: mfhi $26 +; CHECK: sw $26, [[R4:[0-9]+]]($sp) + call void bitcast (void (...)* @write to void ()*)() +; CHECK: lw $26, [[R4:[0-9]+]]($sp) +; CHECK: mthi $26 +; CHECK: lw $26, [[R3:[0-9]+]]($sp) +; CHECK: mtlo $26 +; CHECK: lw $1, {{[0-9]+}}($sp) +; CHECK: lw $gp, {{[0-9]+}}($sp) +; CHECK: lw $ra, [[R5:[0-9]+]]($sp) +; CHECK: lw $8, {{[0-9]+}}($sp) +; CHECK: lw $9, {{[0-9]+}}($sp) +; CHECK: lw $10, {{[0-9]+}}($sp) +; CHECK: lw $11, {{[0-9]+}}($sp) +; CHECK: lw $12, {{[0-9]+}}($sp) +; CHECK: lw $13, {{[0-9]+}}($sp) +; CHECK: lw $14, {{[0-9]+}}($sp) +; CHECK: lw $15, {{[0-9]+}}($sp) +; CHECK: lw $24, {{[0-9]+}}($sp) +; CHECK: lw $25, {{[0-9]+}}($sp) +; CHECK: lw $2, {{[0-9]+}}($sp) +; CHECK: lw $3, {{[0-9]+}}($sp) +; CHECK: lw $4, {{[0-9]+}}($sp) +; CHECK: lw $5, {{[0-9]+}}($sp) +; CHECK: lw $6, {{[0-9]+}}($sp) +; CHECK: lw $7, {{[0-9]+}}($sp) +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, [[R2:[0-9]+]]($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, [[R1:[0-9]+]]($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + ret void +} + +declare void @write(...) + +define void @isr_sw1() #2 { +; CHECK-LABEL: isr_sw1: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $zero, 8, 2 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + +define void @isr_hw0() #3 { +; CHECK-LABEL: isr_hw0: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $zero, 8, 3 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + +define void @isr_hw1() #4 { +; CHECK-LABEL: isr_hw1: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $zero, 8, 4 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + + +define void @isr_hw2() #5 { +; CHECK-LABEL: isr_hw2: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $zero, 8, 5 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + +define void @isr_hw3() #6 { +; CHECK-LABEL: isr_hw3: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $zero, 8, 6 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + +define void @isr_hw4() #7 { +; CHECK-LABEL: isr_hw4: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $zero, 8, 7 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + +define void @isr_hw5() #8 { +; CHECK-LABEL: isr_hw5: +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $zero, 8, 8 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + +define void @isr_eic() #9 { +; CHECK-LABEL: isr_eic: +; CHECK: mfc0 $26, $13, 0 +; CHECK: ext $26, $26, 10, 6 +; CHECK: mfc0 $27, $14, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: mfc0 $27, $12, 0 +; CHECK: sw $27, {{[0-9]+}}($sp) +; CHECK: ins $27, $26, 10, 6 +; CHECK: ins $27, $zero, 1, 4 +; CHECK: ins $27, $zero, 29, 1 +; CHECK: mtc0 $27, $12, 0 + ret void +; CHECK: di +; CHECK: ehb +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + } + +attributes #0 = { "interrupt"="sw0" } +attributes #2 = { "interrupt"="sw1" } +attributes #3 = { "interrupt"="hw0" } +attributes #4 = { "interrupt"="hw1" } +attributes #5 = { "interrupt"="hw2" } +attributes #6 = { "interrupt"="hw3" } +attributes #7 = { "interrupt"="hw4" } +attributes #8 = { "interrupt"="hw5" } +attributes #9 = { "interrupt"="eic" } -- 2.34.1