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)>;
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";
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(),
}
SDValue
-MipsTargetLowering::LowerReturn(SDValue Chain,
- CallingConv::ID CallConv, bool IsVarArg,
+MipsTargetLowering::LowerInterruptReturn(SmallVectorImpl<SDValue> &RetOps,
+ SDLoc DL, SelectionDAG &DAG) const {
+
+ MachineFunction &MF = DAG.getMachineFunction();
+ MipsFunctionInfo *MipsFI = MF.getInfo<MipsFunctionInfo>();
+
+ MipsFI->setISR();
+
+ return DAG.getNode(MipsISD::ERet, DL, MVT::Other, RetOps);
+}
+
+SDValue
+MipsTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
+ bool IsVarArg,
const SmallVectorImpl<ISD::OutputArg> &Outs,
const SmallVectorImpl<SDValue> &OutVals,
SDLoc DL, SelectionDAG &DAG) const {
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);
}
// Return
Ret,
+ // Interrupt, exception, error trap Return
+ ERet,
+
+ // Software Exception Return.
EH_RETURN,
// Node used to extract integer from accumulator.
const SmallVectorImpl<SDValue> &OutVals,
SDLoc dl, SelectionDAG &DAG) const override;
+ SDValue LowerInterruptReturn(SmallVectorImpl<SDValue> &RetOps, SDLoc DL,
+ SelectionDAG &DAG) const;
+
bool shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const override;
// Inline asm support
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]>;
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)]>;
}
}
+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));
}
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();
/// 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);
/// 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;
const MCPhysReg *
MipsRegisterInfo::getCalleeSavedRegs(const MachineFunction *MF) const {
const MipsSubtarget &Subtarget = MF->getSubtarget<MipsSubtarget>();
+ 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;
#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"
BuildMI(MBB, MBBI, dl, TII.get(TargetOpcode::CFI_INSTRUCTION))
.addCFIIndex(CFIIndex);
+ if (MF.getFunction()->hasFnAttribute("interrupt"))
+ emitInterruptPrologueStub(MF, MBB);
+
const std::vector<CalleeSavedInfo> &CSI = MFI->getCalleeSavedInfo();
if (CSI.size()) {
}
}
+void MipsSEFrameLowering::emitInterruptPrologueStub(
+ MachineFunction &MF, MachineBasicBlock &MBB) const {
+
+ MipsFunctionInfo *MipsFI = MF.getInfo<MipsFunctionInfo>();
+ 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<unsigned>(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();
const MipsRegisterInfo &RegInfo =
*static_cast<const MipsRegisterInfo *>(STI.getRegisterInfo());
- DebugLoc dl = MBBI->getDebugLoc();
+ DebugLoc DL = MBBI->getDebugLoc();
MipsABIInfo ABI = STI.getABI();
unsigned SP = ABI.GetStackPtr();
unsigned FP = ABI.GetFramePtr();
--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()) {
}
}
+ if (MF.getFunction()->hasFnAttribute("interrupt"))
+ emitInterruptEpilogueStub(MF, MBB);
+
// Get the number of bytes from FrameInfo
uint64_t StackSize = MFI->getStackSize();
TII.adjustStackPtr(SP, StackSize, MBB, MBBI);
}
+void MipsSEFrameLowering::emitInterruptEpilogueStub(
+ MachineFunction &MF, MachineBasicBlock &MBB) const {
+
+ MachineBasicBlock::iterator MBBI = MBB.getLastNonDebugInstr();
+ MipsFunctionInfo *MipsFI = MF.getInfo<MipsFunctionInfo>();
+ 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,
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);
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()) {
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
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;
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))
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))
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 {
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);
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<bool, bool>
MipsSEInstrInfo::compareOpndSize(unsigned Opc,
const MachineFunction &MF) const {
void expandRetRA(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const;
+ void expandERet(MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const;
+
std::pair<bool, bool> compareOpndSize(unsigned Opc,
const MachineFunction &MF) const;
}
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))
--- /dev/null
+; 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" }
+
--- /dev/null
+; 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" }
+
--- /dev/null
+; 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" }
+
--- /dev/null
+; 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" }