Index: lib/Target/Mips/MipsAsmPrinter.h =================================================================== --- lib/Target/Mips/MipsAsmPrinter.h +++ lib/Target/Mips/MipsAsmPrinter.h @@ -46,6 +46,9 @@ void emitPseudoIndirectBranch(MCStreamer &OutStreamer, const MachineInstr *MI); + void emitERet(MCStreamer &OutStreamer, + const MachineInstr *MI); + // lowerOperand - Convert a MachineOperand into the equivalent MCOperand. bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp); Index: lib/Target/Mips/MipsAsmPrinter.cpp =================================================================== --- lib/Target/Mips/MipsAsmPrinter.cpp +++ lib/Target/Mips/MipsAsmPrinter.cpp @@ -129,6 +129,13 @@ EmitToStreamer(OutStreamer, TmpInst0); } +void MipsAsmPrinter::emitERet(MCStreamer &OutStreamer, + const MachineInstr *MI) { + MCInst TmpInst0; + TmpInst0.setOpcode(Mips::ERET); + EmitToStreamer(OutStreamer, TmpInst0); +} + void MipsAsmPrinter::EmitInstruction(const MachineInstr *MI) { MipsTargetStreamer &TS = getTargetStreamer(); TS.forbidModuleDirective(); @@ -190,6 +197,11 @@ continue; } + if (I->getOpcode() == Mips::ERet) { + emitERet(*OutStreamer, &*I); + continue; + } + // The inMips16Mode() test is not permanent. // Some instructions are marked as pseudo right now which // would make the test fail for the wrong reason but Index: lib/Target/Mips/MipsCallingConv.td =================================================================== --- lib/Target/Mips/MipsCallingConv.td +++ lib/Target/Mips/MipsCallingConv.td @@ -427,3 +427,28 @@ 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)>; Index: lib/Target/Mips/MipsISelLowering.h =================================================================== --- lib/Target/Mips/MipsISelLowering.h +++ lib/Target/Mips/MipsISelLowering.h @@ -67,6 +67,10 @@ // Return Ret, + // Exception Return + ERet, + + // Software Exception Return. EH_RETURN, // Node used to extract integer from accumulator. @@ -475,6 +479,9 @@ 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 Index: lib/Target/Mips/MipsISelLowering.cpp =================================================================== --- lib/Target/Mips/MipsISelLowering.cpp +++ lib/Target/Mips/MipsISelLowering.cpp @@ -121,6 +121,7 @@ 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"; @@ -3095,6 +3096,18 @@ } SDValue +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, @@ -3188,7 +3201,11 @@ if (Flag.getNode()) RetOps.push_back(Flag); - // Return on Mips is always a "jr $ra" + // Return for interrupts overrides the normal return mechanism. + 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); } Index: lib/Target/Mips/MipsInstrInfo.td =================================================================== --- lib/Target/Mips/MipsInstrInfo.td +++ lib/Target/Mips/MipsInstrInfo.td @@ -77,6 +77,8 @@ 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]>; @@ -1074,6 +1076,9 @@ 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)]>; Index: lib/Target/Mips/MipsMachineFunction.h =================================================================== --- lib/Target/Mips/MipsMachineFunction.h +++ lib/Target/Mips/MipsMachineFunction.h @@ -54,8 +54,8 @@ public: MipsFunctionInfo(MachineFunction &MF) : MF(MF), SRetReturnReg(0), GlobalBaseReg(0), Mips16SPAliasReg(0), - VarArgsFrameIndex(0), CallsEhReturn(false), SaveS2(false), - MoveF64ViaSpillFI(-1) {} + VarArgsFrameIndex(0), CallsEhReturn(false), ISR(false), + SaveS2(false), MoveF64ViaSpillFI(-1){} ~MipsFunctionInfo(); @@ -86,6 +86,14 @@ int getEhDataRegFI(unsigned Reg) const { return EhDataRegFI[Reg]; } bool isEhDataRegFI(int FI) const; + // Function with the "interrupt" attribute require extra handling + // with prologue, epilogue and additional spill slots. + bool isISR() const { return ISR; } + void setISR() { ISR = true; } + void createISRRegFI(); + int getISRRegFI(unsigned Reg) const { return ISRDataRegFI[Reg]; } + bool isISRRegFI(int FI) const; + /// \brief Create a MachinePointerInfo that has a MipsCallEntr object /// representing a GOT entry for an external function. MachinePointerInfo callPtrInfo(StringRef Name); @@ -136,6 +144,12 @@ /// Frame objects for spilling eh data registers. int EhDataRegFI[4]; + /// ISR - Whether the function is an Interrupt Service Routine. + bool ISR; + + /// Frame objects for spilling C0_STATUS, C0_EPC + int ISRDataRegFI[2]; + // saveS2 bool SaveS2; Index: lib/Target/Mips/MipsMachineFunction.cpp =================================================================== --- lib/Target/Mips/MipsMachineFunction.cpp +++ lib/Target/Mips/MipsMachineFunction.cpp @@ -111,11 +111,28 @@ } } +void MipsFunctionInfo::createISRRegFI() { + for (int I = 0; I < 2; ++I) { + // ISRs require spill slots for Status & ErrorPC COP0 registers. + // The current implementation only supports Mips32r2+ not Mips64rX. Status + // is always 32 bits, ErrorPC is 32 or 64 bits dependant on architecture, + // however 32 bits is the only supported architecture. + const TargetRegisterClass *RC = &Mips::GPR32RegClass; + + 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 ISR && (FI == ISRDataRegFI[0] || FI == ISRDataRegFI[1]); +} + MachinePointerInfo MipsFunctionInfo::callPtrInfo(StringRef Name) { std::unique_ptr &E = ExternalCallEntries[Name]; Index: lib/Target/Mips/MipsRegisterInfo.cpp =================================================================== --- lib/Target/Mips/MipsRegisterInfo.cpp +++ lib/Target/Mips/MipsRegisterInfo.cpp @@ -84,6 +84,21 @@ 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()){ + if (Subtarget.hasMips64r6()) + return CSR_Interrupt_64R6_SaveList; + else + return CSR_Interrupt_64_SaveList; + } else { + if (Subtarget.hasMips32r6()) + return CSR_Interrupt_32R6_SaveList; + else + return CSR_Interrupt_32_SaveList; + } + } + if (Subtarget.isSingleFloat()) return CSR_SingleFloatOnly_SaveList; Index: lib/Target/Mips/MipsSEFrameLowering.h =================================================================== --- lib/Target/Mips/MipsSEFrameLowering.h +++ lib/Target/Mips/MipsSEFrameLowering.h @@ -37,8 +37,13 @@ void processFunctionBeforeCalleeSavedScan(MachineFunction &MF, 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 Index: lib/Target/Mips/MipsSEFrameLowering.cpp =================================================================== --- lib/Target/Mips/MipsSEFrameLowering.cpp +++ lib/Target/Mips/MipsSEFrameLowering.cpp @@ -407,6 +407,9 @@ 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()) { @@ -522,6 +525,126 @@ } } +void MipsSEFrameLowering::emitInterruptPrologueStub(MachineFunction &MF, + MachineBasicBlock &MBB) const { + MipsFunctionInfo *MipsFI = MF.getInfo(); + + const MipsSEInstrInfo &TII = + *static_cast(STI.getInstrInfo()); + const MipsRegisterInfo &RegInfo = + *static_cast(STI.getRegisterInfo()); + + MachineBasicBlock::iterator MBBI = MBB.begin(); + DebugLoc dl = MBBI != MBB.end() ? MBBI->getDebugLoc() : DebugLoc(); + + // Report error if in unsupported MIPS16 mode or pre-r2 MIPS. + // 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. + + // Mips16 lacks the ehb instruction as well. + if (!STI.hasMips32r2() || STI.inMips16Mode()) + 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 MIPS32 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, TII.get(Mips::MFC0), Mips::K0) + .addReg(Mips::COP013).addImm(0) + .setMIFlag(MachineInstr::FrameSetup); + + BuildMI(MBB, MBBI, dl, TII.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, TII.get(Mips::MFC0), Mips::K1).addReg(Mips::COP014) + .addImm(0).setMIFlag(MachineInstr::FrameSetup); + + TII.storeRegToStack(MBB, MBBI, Mips::K1, false, MipsFI->getISRRegFI(0), + PtrRc, &RegInfo, 0); + + // Fetch and Spill Status + MBB.addLiveIn(Mips::COP012); + BuildMI(MBB, MBBI, dl, TII.get(Mips::MFC0), Mips::K1).addReg(Mips::COP012) + .addImm(0).setMIFlag(MachineInstr::FrameSetup); + + TII.storeRegToStack(MBB, MBBI, Mips::K1, false, MipsFI->getISRRegFI(1), + PtrRc, &RegInfo, 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 if (IntKind == "sw0") + InsSize = 1; + else if (IntKind == "sw1") + InsSize = 2; + else if (IntKind == "hw0") + InsSize = 3; + else if (IntKind == "hw1") + InsSize = 4; + else if (IntKind == "hw2") + InsSize = 5; + else if (IntKind == "hw3") + InsSize = 6; + else if (IntKind == "hw4") + InsSize = 7; + else if (IntKind == "hw5") + InsSize = 8; + else + llvm_unreachable("Unknown Mips interrupt type!"); + + BuildMI(MBB, MBBI, dl, TII.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, TII.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, TII.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, TII.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(); @@ -568,6 +691,9 @@ } } + if (MF.getFunction()->hasFnAttribute("interrupt")) + emitInterruptEpilogueStub(MF, MBB); + // Get the number of bytes from FrameInfo uint64_t StackSize = MFI->getStackSize(); @@ -578,6 +704,38 @@ 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(); + + const MipsSEInstrInfo &TII = + *static_cast(STI.getInstrInfo()); + const MipsRegisterInfo &RegInfo = + *static_cast(STI.getRegisterInfo()); + + // Perform ISR handling like GCC + const TargetRegisterClass *PtrRc = &Mips::GPR32RegClass; + + // Disable Interrupts. + BuildMI(MBB, MBBI, dl, TII.get(Mips::DI), Mips::ZERO); + BuildMI(MBB, MBBI, dl, TII.get(Mips::EHB)); + + // Restore EPC + TII.loadRegFromStackSlot(MBB, MBBI, Mips::K1, MipsFI->getISRRegFI(0), + PtrRc, &RegInfo); + BuildMI(MBB, MBBI, dl, TII.get(Mips::MTC0), Mips::COP014).addReg(Mips::K1) + .addImm(0); + + // Restore Status + TII.loadRegFromStackSlot(MBB, MBBI, Mips::K1, MipsFI->getISRRegFI(1), + PtrRc, &RegInfo); + BuildMI(MBB, MBBI, dl, TII.get(Mips::MTC0), Mips::COP012) + .addReg(Mips::K1).addImm(0); + +} bool MipsSEFrameLowering:: spillCalleeSavedRegisters(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI, @@ -599,6 +757,35 @@ if (!IsRAAndRetAddrIsTaken) EntryBlock->addLiveIn(Reg); + // Handle LO, HI for ISRs. + bool IsLOOrHI = (Reg == Mips::LO0 || Reg == Mips::LO0_64 + || Reg == Mips::HI0 || Reg == Mips::HI0_64); + const Function * Func = MBB.getParent()->getFunction(); + if (IsLOOrHI && Func->hasFnAttribute("interrupt")){ + DebugLoc dl; + if (MI != MBB.end()) dl = MI->getDebugLoc(); + + if (Reg == Mips::HI0 || Reg == Mips::LO0){ + if (Reg == Mips::HI0) + BuildMI(MBB, MI, dl, TII.get(Mips::MFLO), Mips::K0) + .setMIFlag(MachineInstr::FrameSetup); + else + BuildMI(MBB, MI, dl, TII.get(Mips::MFHI), Mips::K0) + .setMIFlag(MachineInstr::FrameSetup); + + Reg = Mips::K0; + } else { + if (Reg == Mips::HI0_64) + BuildMI(MBB, MI, dl, TII.get(Mips::MFLO64), Mips::K0_64) + .setMIFlag(MachineInstr::FrameSetup); + else + BuildMI(MBB, MI, dl, TII.get(Mips::MFHI64), Mips::K0_64) + .setMIFlag(MachineInstr::FrameSetup); + + Reg = Mips::K0_64; + } + } + // Insert the spill to the stack frame. bool IsKill = !IsRAAndRetAddrIsTaken; const TargetRegisterClass *RC = TRI->getMinimalPhysRegClass(Reg); @@ -641,6 +828,10 @@ if (MipsFI->callsEhReturn()) MipsFI->createEhDataRegsFI(); + // Create spill slots for COP0 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()) { Index: lib/Target/Mips/MipsSEISelLowering.cpp =================================================================== --- lib/Target/Mips/MipsSEISelLowering.cpp +++ lib/Target/Mips/MipsSEISelLowering.cpp @@ -1180,6 +1180,10 @@ 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; Index: lib/Target/Mips/MipsSEInstrInfo.h =================================================================== --- lib/Target/Mips/MipsSEInstrInfo.h +++ lib/Target/Mips/MipsSEInstrInfo.h @@ -82,6 +82,8 @@ 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; Index: lib/Target/Mips/MipsSEInstrInfo.cpp =================================================================== --- lib/Target/Mips/MipsSEInstrInfo.cpp +++ lib/Target/Mips/MipsSEInstrInfo.cpp @@ -187,6 +187,23 @@ unsigned Opc = 0; + 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; + } + } + if (Mips::GPR32RegClass.hasSubClassEq(RC)) Opc = Mips::SW; else if (Mips::GPR64RegClass.hasSubClassEq(RC)) @@ -213,6 +230,14 @@ 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; assert(Opc && "Register class not handled!"); BuildMI(MBB, I, DL, get(Opc)).addReg(SrcReg, getKillRegState(isKill)) @@ -228,6 +253,12 @@ 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)) @@ -254,10 +285,38 @@ 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 { + + unsigned Reg = Mips::K0; + unsigned LdOp = Mips::MTLO; + if (DestReg == Mips::HI0) LdOp = Mips::MTHI; + + if (DestReg == Mips::HI0_64 || DestReg == Mips::LO0_64){ + 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 { @@ -271,6 +330,9 @@ 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); @@ -438,6 +500,11 @@ 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 { Index: lib/Target/Mips/MipsSERegisterInfo.cpp =================================================================== --- lib/Target/Mips/MipsSERegisterInfo.cpp +++ lib/Target/Mips/MipsSERegisterInfo.cpp @@ -126,18 +126,20 @@ } bool EhDataRegFI = MipsFI->isEhDataRegFI(FrameIndex); - + bool ISRRegFI = 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 CP0 12, 14 registers. // Everything else is referenced relative to whatever register // getFrameRegister() returns. unsigned FrameReg; - if ((FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI) || EhDataRegFI) - FrameReg = ABI.GetStackPtr(); + if ((FrameIndex >= MinCSFI && FrameIndex <= MaxCSFI) + || EhDataRegFI || ISRRegFI) + FrameReg = ABI.GetStackPtr(); else if (RegInfo->needsStackRealignment(MF)) { if (MFI->hasVarSizedObjects() && !MFI->isFixedObjectIndex(FrameIndex)) FrameReg = ABI.GetBasePtr(); Index: test/CodeGen/Mips/interrupt-attr-fail.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/interrupt-attr-fail.ll @@ -0,0 +1,11 @@ +; RUN: llc -mcpu=mips32 -march=mipsel -relocation-model=static -o - %s | FileCheck --check-prefix=CHECK %s +; XFAIL: * + +define void @isr_sw0() #0 { + call void bitcast (void (...)* @write to void ()*)() +} + +declare void @write(...) + +attributes #0 = { "interrupt"="sw0" } + Index: test/CodeGen/Mips/interrupt-attr.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/interrupt-attr.ll @@ -0,0 +1,235 @@ +; RUN: llc -mcpu=mips32r2 -march=mipsel -relocation-model=static -o - %s | FileCheck --check-prefix=CHECK %s + +define void @isr_sw0() #0 { +; 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, 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, {{[0-9]+}}($sp) +; CHECK: sw $gp, {{[0-9]+}}($sp) +; CHECK: sw $1, {{[0-9]+}}($sp) +; CHECK: mfhi $26 +; CHECK: sw $26, {{[0-9]+}}($sp) +; CHECK: mflo $26 +; CHECK: sw $26, {{[0-9]+}}($sp) + call void bitcast (void (...)* @write to void ()*)() +; CHECK: lw $26, {{[0-9]+}}($sp) +; CHECK: mthi $26 +; CHECK: lw $26, {{[0-9]+}}($sp) +; CHECK: mtlo $26 +; CHECK: lw $1, {{[0-9]+}}($sp) +; CHECK: lw $gp, {{[0-9]+}}($sp) +; CHECK: lw $ra, {{[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, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $14, 0 +; CHECK: lw $27, {{[0-9]+}}($sp) +; CHECK: mtc0 $27, $12, 0 +; CHECK: eret + ret void +} + +declare void @write(...) + +define void @isr_sw1() #2 { +; 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: 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: 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: 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: 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: 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: 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: 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" }