diff --git a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp --- a/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp +++ b/llvm/lib/Target/Mips/AsmParser/MipsAsmParser.cpp @@ -146,6 +146,7 @@ bool IsPicEnabled; bool IsCpRestoreSet; int CpRestoreOffset; + unsigned GPReg; unsigned CpSaveLocation; /// If true, then CpSaveLocation is a register, otherwise it's an offset. bool CpSaveLocationIsRegister; @@ -334,6 +335,7 @@ bool parseSetFeature(uint64_t Feature); bool isPicAndNotNxxAbi(); // Used by .cpload, .cprestore, and .cpsetup. bool parseDirectiveCpLoad(SMLoc Loc); + bool parseDirectiveCpLocal(SMLoc Loc); bool parseDirectiveCpRestore(SMLoc Loc); bool parseDirectiveCPSetup(); bool parseDirectiveCPReturn(); @@ -527,6 +529,7 @@ IsCpRestoreSet = false; CpRestoreOffset = -1; + GPReg = ABI.GetGlobalPtr(); const Triple &TheTriple = sti.getTargetTriple(); IsLittleEndian = TheTriple.isLittleEndian(); @@ -2040,7 +2043,7 @@ const MCExpr *Lo16RelocExpr = MipsMCExpr::create(MipsMCExpr::MEK_LO, JalExpr, getContext()); - TOut.emitRRX(Mips::LW, Mips::T9, Mips::GP, + TOut.emitRRX(Mips::LW, Mips::T9, GPReg, MCOperand::createExpr(Got16RelocExpr), IDLoc, STI); TOut.emitRRX(Mips::ADDiu, Mips::T9, Mips::T9, MCOperand::createExpr(Lo16RelocExpr), IDLoc, STI); @@ -2054,7 +2057,7 @@ MipsMCExpr::create(MipsMCExpr::MEK_GOT_DISP, JalExpr, getContext()); TOut.emitRRX(ABI.ArePtrs64bit() ? Mips::LD : Mips::LW, Mips::T9, - Mips::GP, MCOperand::createExpr(GotDispRelocExpr), IDLoc, + GPReg, MCOperand::createExpr(GotDispRelocExpr), IDLoc, STI); } } else { @@ -2065,7 +2068,7 @@ const MCExpr *Call16RelocExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_CALL, JalExpr, getContext()); - TOut.emitRRX(ABI.ArePtrs64bit() ? Mips::LD : Mips::LW, Mips::T9, Mips::GP, + TOut.emitRRX(ABI.ArePtrs64bit() ? Mips::LD : Mips::LW, Mips::T9, GPReg, MCOperand::createExpr(Call16RelocExpr), IDLoc, STI); } @@ -2893,8 +2896,8 @@ ELF::STB_LOCAL))) { const MCExpr *CallExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_CALL, SymExpr, getContext()); - TOut.emitRRX(Mips::LW, DstReg, ABI.GetGlobalPtr(), - MCOperand::createExpr(CallExpr), IDLoc, STI); + TOut.emitRRX(Mips::LW, DstReg, GPReg, MCOperand::createExpr(CallExpr), + IDLoc, STI); return false; } @@ -2933,8 +2936,8 @@ TmpReg = ATReg; } - TOut.emitRRX(Mips::LW, TmpReg, ABI.GetGlobalPtr(), - MCOperand::createExpr(GotExpr), IDLoc, STI); + TOut.emitRRX(Mips::LW, TmpReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, + STI); if (LoExpr) TOut.emitRRX(Mips::ADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), @@ -2969,8 +2972,8 @@ ELF::STB_LOCAL))) { const MCExpr *CallExpr = MipsMCExpr::create(MipsMCExpr::MEK_GOT_CALL, SymExpr, getContext()); - TOut.emitRRX(Mips::LD, DstReg, ABI.GetGlobalPtr(), - MCOperand::createExpr(CallExpr), IDLoc, STI); + TOut.emitRRX(Mips::LD, DstReg, GPReg, MCOperand::createExpr(CallExpr), + IDLoc, STI); return false; } @@ -3012,8 +3015,8 @@ TmpReg = ATReg; } - TOut.emitRRX(Mips::LD, TmpReg, ABI.GetGlobalPtr(), - MCOperand::createExpr(GotExpr), IDLoc, STI); + TOut.emitRRX(Mips::LD, TmpReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, + STI); if (LoExpr) TOut.emitRRX(Mips::DADDiu, TmpReg, TmpReg, MCOperand::createExpr(LoExpr), @@ -3243,10 +3246,10 @@ MipsMCExpr::create(MipsMCExpr::MEK_GOT, GotSym, getContext()); if(isABI_O32() || isABI_N32()) { - TOut.emitRRX(Mips::LW, ATReg, Mips::GP, MCOperand::createExpr(GotExpr), + TOut.emitRRX(Mips::LW, ATReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, STI); } else { //isABI_N64() - TOut.emitRRX(Mips::LD, ATReg, Mips::GP, MCOperand::createExpr(GotExpr), + TOut.emitRRX(Mips::LD, ATReg, GPReg, MCOperand::createExpr(GotExpr), IDLoc, STI); } } else { //!IsPicEnabled @@ -7241,6 +7244,40 @@ return false; } +bool MipsAsmParser::parseDirectiveCpLocal(SMLoc Loc) { + if (!isABI_N32() && !isABI_N64()) { + reportParseError(".cplocal is allowed only in N32 or N64 mode"); + return false; + } + + SmallVector, 1> Reg; + OperandMatchResultTy ResTy = parseAnyRegister(Reg); + if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) { + reportParseError("expected register containing global pointer"); + return false; + } + + MipsOperand &RegOpnd = static_cast(*Reg[0]); + if (!RegOpnd.isGPRAsmReg()) { + reportParseError(RegOpnd.getStartLoc(), "invalid register"); + return false; + } + + // If this is not the end of the statement, report an error. + if (getLexer().isNot(AsmToken::EndOfStatement)) { + reportParseError("unexpected token, expected end of statement"); + return false; + } + getParser().Lex(); // Consume the EndOfStatement. + + unsigned NewReg = RegOpnd.getGPR32Reg(); + if (IsPicEnabled) + GPReg = NewReg; + + getTargetStreamer().emitDirectiveCpLocal(NewReg); + return false; +} + bool MipsAsmParser::parseDirectiveCpRestore(SMLoc Loc) { MCAsmParser &Parser = getParser(); @@ -8091,6 +8128,10 @@ parseDirectiveCpRestore(DirectiveID.getLoc()); return false; } + if (IDVal == ".cplocal") { + parseDirectiveCpLocal(DirectiveID.getLoc()); + return false; + } if (IDVal == ".ent") { StringRef SymbolName; diff --git a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp --- a/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp +++ b/llvm/lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp @@ -35,7 +35,7 @@ } // end anonymous namespace MipsTargetStreamer::MipsTargetStreamer(MCStreamer &S) - : MCTargetStreamer(S), ModuleDirectiveAllowed(true) { + : MCTargetStreamer(S), GPReg(Mips::GP), ModuleDirectiveAllowed(true) { GPRInfoSet = FPRInfoSet = FrameInfoSet = false; } void MipsTargetStreamer::emitDirectiveSetMicroMips() {} @@ -106,6 +106,23 @@ void MipsTargetStreamer::emitDirectiveSetDspr2() { forbidModuleDirective(); } void MipsTargetStreamer::emitDirectiveSetNoDsp() { forbidModuleDirective(); } void MipsTargetStreamer::emitDirectiveCpLoad(unsigned RegNo) {} +void MipsTargetStreamer::emitDirectiveCpLocal(unsigned RegNo) { + // .cplocal $reg + // This directive forces to use the alternate register for context pointer. + // For example + // .cplocal $4 + // jal foo + // expands to + // ld $25, %call16(foo)($4) + // jalr $25 + + if (!getABI().IsN32() && !getABI().IsN64()) + return; + + GPReg = RegNo; + + forbidModuleDirective(); +} bool MipsTargetStreamer::emitDirectiveCpRestore( int Offset, function_ref GetATReg, SMLoc IDLoc, const MCSubtargetInfo *STI) { @@ -257,8 +274,7 @@ /// Emit the $gp restore operation for .cprestore. void MipsTargetStreamer::emitGPRestore(int Offset, SMLoc IDLoc, const MCSubtargetInfo *STI) { - emitLoadWithImmOffset(Mips::LW, Mips::GP, Mips::SP, Offset, Mips::GP, IDLoc, - STI); + emitLoadWithImmOffset(Mips::LW, GPReg, Mips::SP, Offset, GPReg, IDLoc, STI); } /// Emit a store instruction with an immediate offset. @@ -665,6 +681,12 @@ forbidModuleDirective(); } +void MipsTargetAsmStreamer::emitDirectiveCpLocal(unsigned RegNo) { + OS << "\t.cplocal\t$" + << StringRef(MipsInstPrinter::getRegisterName(RegNo)).lower() << "\n"; + MipsTargetStreamer::emitDirectiveCpLocal(RegNo); +} + bool MipsTargetAsmStreamer::emitDirectiveCpRestore( int Offset, function_ref GetATReg, SMLoc IDLoc, const MCSubtargetInfo *STI) { @@ -1135,7 +1157,7 @@ MCInst TmpInst; TmpInst.setOpcode(Mips::LUi); - TmpInst.addOperand(MCOperand::createReg(Mips::GP)); + TmpInst.addOperand(MCOperand::createReg(GPReg)); const MCExpr *HiSym = MipsMCExpr::create( MipsMCExpr::MEK_HI, MCSymbolRefExpr::create("_gp_disp", MCSymbolRefExpr::VK_None, @@ -1147,8 +1169,8 @@ TmpInst.clear(); TmpInst.setOpcode(Mips::ADDiu); - TmpInst.addOperand(MCOperand::createReg(Mips::GP)); - TmpInst.addOperand(MCOperand::createReg(Mips::GP)); + TmpInst.addOperand(MCOperand::createReg(GPReg)); + TmpInst.addOperand(MCOperand::createReg(GPReg)); const MCExpr *LoSym = MipsMCExpr::create( MipsMCExpr::MEK_LO, MCSymbolRefExpr::create("_gp_disp", MCSymbolRefExpr::VK_None, @@ -1160,14 +1182,19 @@ TmpInst.clear(); TmpInst.setOpcode(Mips::ADDu); - TmpInst.addOperand(MCOperand::createReg(Mips::GP)); - TmpInst.addOperand(MCOperand::createReg(Mips::GP)); + TmpInst.addOperand(MCOperand::createReg(GPReg)); + TmpInst.addOperand(MCOperand::createReg(GPReg)); TmpInst.addOperand(MCOperand::createReg(RegNo)); getStreamer().EmitInstruction(TmpInst, STI); forbidModuleDirective(); } +void MipsTargetELFStreamer::emitDirectiveCpLocal(unsigned RegNo) { + if (Pic) + MipsTargetStreamer::emitDirectiveCpLocal(RegNo); +} + bool MipsTargetELFStreamer::emitDirectiveCpRestore( int Offset, function_ref GetATReg, SMLoc IDLoc, const MCSubtargetInfo *STI) { @@ -1184,7 +1211,7 @@ return true; // Store the $gp on the stack. - emitStoreWithImmOffset(Mips::SW, Mips::GP, Mips::SP, Offset, GetATReg, IDLoc, + emitStoreWithImmOffset(Mips::SW, GPReg, Mips::SP, Offset, GetATReg, IDLoc, STI); return true; } @@ -1205,10 +1232,10 @@ // Either store the old $gp in a register or on the stack if (IsReg) { // move $save, $gpreg - emitRRR(Mips::OR64, RegOrOffset, Mips::GP, Mips::ZERO, SMLoc(), &STI); + emitRRR(Mips::OR64, RegOrOffset, GPReg, Mips::ZERO, SMLoc(), &STI); } else { // sd $gpreg, offset($sp) - emitRRI(Mips::SD, Mips::GP, Mips::SP, RegOrOffset, SMLoc(), &STI); + emitRRI(Mips::SD, GPReg, Mips::SP, RegOrOffset, SMLoc(), &STI); } if (getABI().IsN32()) { @@ -1221,11 +1248,11 @@ MCA.getContext()); // lui $gp, %hi(__gnu_local_gp) - emitRX(Mips::LUi, Mips::GP, MCOperand::createExpr(HiExpr), SMLoc(), &STI); + emitRX(Mips::LUi, GPReg, MCOperand::createExpr(HiExpr), SMLoc(), &STI); // addiu $gp, $gp, %lo(__gnu_local_gp) - emitRRX(Mips::ADDiu, Mips::GP, Mips::GP, MCOperand::createExpr(LoExpr), - SMLoc(), &STI); + emitRRX(Mips::ADDiu, GPReg, GPReg, MCOperand::createExpr(LoExpr), SMLoc(), + &STI); return; } @@ -1238,14 +1265,14 @@ MCA.getContext()); // lui $gp, %hi(%neg(%gp_rel(funcSym))) - emitRX(Mips::LUi, Mips::GP, MCOperand::createExpr(HiExpr), SMLoc(), &STI); + emitRX(Mips::LUi, GPReg, MCOperand::createExpr(HiExpr), SMLoc(), &STI); // addiu $gp, $gp, %lo(%neg(%gp_rel(funcSym))) - emitRRX(Mips::ADDiu, Mips::GP, Mips::GP, MCOperand::createExpr(LoExpr), - SMLoc(), &STI); + emitRRX(Mips::ADDiu, GPReg, GPReg, MCOperand::createExpr(LoExpr), SMLoc(), + &STI); // daddu $gp, $gp, $funcreg - emitRRR(Mips::DADDu, Mips::GP, Mips::GP, RegNo, SMLoc(), &STI); + emitRRR(Mips::DADDu, GPReg, GPReg, RegNo, SMLoc(), &STI); } void MipsTargetELFStreamer::emitDirectiveCpreturn(unsigned SaveLocation, @@ -1258,12 +1285,12 @@ // Either restore the old $gp from a register or on the stack if (SaveLocationIsRegister) { Inst.setOpcode(Mips::OR); - Inst.addOperand(MCOperand::createReg(Mips::GP)); + Inst.addOperand(MCOperand::createReg(GPReg)); Inst.addOperand(MCOperand::createReg(SaveLocation)); Inst.addOperand(MCOperand::createReg(Mips::ZERO)); } else { Inst.setOpcode(Mips::LD); - Inst.addOperand(MCOperand::createReg(Mips::GP)); + Inst.addOperand(MCOperand::createReg(GPReg)); Inst.addOperand(MCOperand::createReg(Mips::SP)); Inst.addOperand(MCOperand::createImm(SaveLocation)); } diff --git a/llvm/lib/Target/Mips/MipsTargetStreamer.h b/llvm/lib/Target/Mips/MipsTargetStreamer.h --- a/llvm/lib/Target/Mips/MipsTargetStreamer.h +++ b/llvm/lib/Target/Mips/MipsTargetStreamer.h @@ -91,6 +91,7 @@ // PIC support virtual void emitDirectiveCpLoad(unsigned RegNo); + virtual void emitDirectiveCpLocal(unsigned RegNo); virtual bool emitDirectiveCpRestore(int Offset, function_ref GetATReg, SMLoc IDLoc, const MCSubtargetInfo *STI); @@ -199,6 +200,7 @@ bool FrameInfoSet; int FrameOffset; unsigned FrameReg; + unsigned GPReg; unsigned ReturnReg; private: @@ -274,6 +276,7 @@ // PIC support void emitDirectiveCpLoad(unsigned RegNo) override; + void emitDirectiveCpLocal(unsigned RegNo) override; /// Emit a .cprestore directive. If the offset is out of range then it will /// be synthesized using the assembler temporary. @@ -345,6 +348,7 @@ // PIC support void emitDirectiveCpLoad(unsigned RegNo) override; + void emitDirectiveCpLocal(unsigned RegNo) override; bool emitDirectiveCpRestore(int Offset, function_ref GetATReg, SMLoc IDLoc, const MCSubtargetInfo *STI) override; void emitDirectiveCpsetup(unsigned RegNo, int RegOrOffset, diff --git a/llvm/test/MC/Mips/cplocal-bad.s b/llvm/test/MC/Mips/cplocal-bad.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/Mips/cplocal-bad.s @@ -0,0 +1,20 @@ +# RUN: not llvm-mc -triple=mips-unknown-linux-gnu %s 2>&1 \ +# RUN: | FileCheck -check-prefix=O32 %s +# RUN: not llvm-mc -triple=mips64-unknown-linux-gnuabin32 %s 2>&1 \ +# RUN: | FileCheck -check-prefix=NABI %s +# RUN: not llvm-mc -triple=mips64-unknown-linux-gnu %s 2>&1 \ +# RUN: | FileCheck -check-prefix=NABI %s + + .text + .cplocal $32 +# O32: :[[@LINE-1]]:{{[0-9]+}}: error: .cplocal is allowed only in N32 or N64 mode +# NABI: :[[@LINE-2]]:{{[0-9]+}}: error: invalid register + .cplocal $foo +# O32: :[[@LINE-1]]:{{[0-9]+}}: error: .cplocal is allowed only in N32 or N64 mode +# NABI: :[[@LINE-2]]:{{[0-9]+}}: error: expected register containing global pointer + .cplocal bar +# O32: :[[@LINE-1]]:{{[0-9]+}}: error: .cplocal is allowed only in N32 or N64 mode +# NABI: :[[@LINE-2]]:{{[0-9]+}}: error: expected register containing global pointer + .cplocal $25 foobar +# O32: :[[@LINE-1]]:{{[0-9]+}}: error: .cplocal is allowed only in N32 or N64 mode +# NABI: :[[@LINE-2]]:{{[0-9]+}}: error: unexpected token, expected end of statement diff --git a/llvm/test/MC/Mips/cplocal.s b/llvm/test/MC/Mips/cplocal.s new file mode 100644 --- /dev/null +++ b/llvm/test/MC/Mips/cplocal.s @@ -0,0 +1,45 @@ +# RUN: llvm-mc -triple=mips64-unknown-linux-gnuabin32 -position-independent %s \ +# RUN: | FileCheck -check-prefix=ASM-PIC32 %s +# RUN: llvm-mc -triple=mips64-unknown-linux-gnu -position-independent %s \ +# RUN: | FileCheck -check-prefix=ASM-PIC64 %s +# RUN: llvm-mc -triple=mips64-unknown-linux-gnuabin32 %s \ +# RUN: | FileCheck -check-prefix=ASM-NPIC %s +# RUN: llvm-mc -triple=mips64-unknown-linux-gnu %s \ +# RUN: | FileCheck -check-prefix=ASM-NPIC %s + +# RUN: llvm-mc -triple=mips64-unknown-linux-gnuabin32 \ +# RUN: -position-independent -filetype=obj -o - %s \ +# RUN: | llvm-objdump -d -r - | FileCheck -check-prefix=OBJ32 %s +# RUN: llvm-mc -triple=mips64-unknown-linux-gnu \ +# RUN: -position-independent -filetype=obj -o - %s \ +# RUN: | llvm-objdump -d -r - | FileCheck -check-prefix=OBJ64 %s + +# ASM-PIC32: .text +# ASM-PIC32: .cplocal $4 +# ASM-PIC32: lw $25, %call16(foo)($4) +# ASM-PIC32: jalr $25 + +# ASM-PIC64: .text +# ASM-PIC64: .cplocal $4 +# ASM-PIC64: ld $25, %call16(foo)($4) +# ASM-PIC64: jalr $25 + +# ASM-NPIC: .text +# ASM-NPIC: .cplocal $4 +# ASM-NPIC: jal foo + +# OBJ32: lw $25, 0($4) +# OBJ32: R_MIPS_CALL16 foo +# OBJ32: jalr $25 +# OBJ32: R_MIPS_JALR foo + +# OBJ64: ld $25, 0($4) +# OBJ64: R_MIPS_CALL16/R_MIPS_NONE/R_MIPS_NONE foo +# OBJ64: jalr $25 +# OBJ64: R_MIPS_JALR/R_MIPS_NONE/R_MIPS_NONE foo + + .text + .cplocal $4 + jal foo +foo: + nop