[mips] Add support for .cpload.
authorMatheus Almeida <matheus.almeida@imgtec.com>
Wed, 30 Apr 2014 11:28:42 +0000 (11:28 +0000)
committerMatheus Almeida <matheus.almeida@imgtec.com>
Wed, 30 Apr 2014 11:28:42 +0000 (11:28 +0000)
Summary:
This directive is used for setting up $gp in the beginning of a function.
It expands to three instructions if PIC is enabled:
lui   $gp, %hi(_gp_disp)
addui $gp, $gp, %lo(_gp_disp)
addu  $gp, $gp, $reg

_gp_disp is a special symbol that the linker sets to the distance between
the lui instruction and the context pointer (_gp).

Reviewers: dsanders

Reviewed By: dsanders

Differential Revision: http://reviews.llvm.org/D3480

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@207637 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/Mips/AsmParser/MipsAsmParser.cpp
lib/Target/Mips/MCTargetDesc/MipsTargetStreamer.cpp
lib/Target/Mips/MipsTargetStreamer.h
test/MC/Mips/cpload-bad.s [new file with mode: 0644]
test/MC/Mips/cpload.s [new file with mode: 0644]

index 4bc681b7a2d00ff8528e0690c9592d8b5b3d55db..4449cc20c98f925edc2ab24ddd0c12bf8e8dec5e 100644 (file)
@@ -137,6 +137,7 @@ class MipsAsmParser : public MCTargetAsmParser {
                      SmallVectorImpl<MCInst> &Instructions, bool isLoad,
                      bool isImmOpnd);
   bool reportParseError(StringRef ErrorMsg);
+  bool reportParseError(SMLoc Loc, StringRef ErrorMsg);
 
   bool parseMemOffset(const MCExpr *&Res, bool isParenExpr);
   bool parseRelocOperand(const MCExpr *&Res);
@@ -145,6 +146,7 @@ class MipsAsmParser : public MCTargetAsmParser {
 
   bool isEvaluated(const MCExpr *Expr);
   bool parseSetFeature(uint64_t Feature);
+  bool parseDirectiveCPLoad(SMLoc Loc);
   bool parseDirectiveCPSetup();
   bool parseDirectiveNaN();
   bool parseDirectiveSet();
@@ -2083,6 +2085,10 @@ bool MipsAsmParser::reportParseError(StringRef ErrorMsg) {
   return Error(Loc, ErrorMsg);
 }
 
+bool MipsAsmParser::reportParseError(SMLoc Loc, StringRef ErrorMsg) {
+  return Error(Loc, ErrorMsg);
+}
+
 bool MipsAsmParser::parseSetNoAtDirective() {
   // Line should look like: ".set noat".
   // set at reg to 0.
@@ -2301,6 +2307,30 @@ bool MipsAsmParser::eatComma(StringRef ErrorStr) {
   return true;
 }
 
+bool MipsAsmParser::parseDirectiveCPLoad(SMLoc Loc) {
+  if (Options.isReorder())
+    Warning(Loc, ".cpload in reorder section");
+
+  // FIXME: Warn if cpload is used in Mips16 mode.
+
+  SmallVector<MCParsedAsmOperand *, 1> Reg;
+  OperandMatchResultTy ResTy = ParseAnyRegister(Reg);
+  if (ResTy == MatchOperand_NoMatch || ResTy == MatchOperand_ParseFail) {
+    reportParseError("expected register containing function address");
+    return false;
+  }
+
+  MipsOperand *RegOpnd = static_cast<MipsOperand *>(Reg[0]);
+  if (!RegOpnd->isGPRAsmReg()) {
+    reportParseError(RegOpnd->getStartLoc(), "invalid register");
+    return false;
+  }
+
+  getTargetStreamer().emitDirectiveCpload(RegOpnd->getGPR32Reg());
+  delete RegOpnd;
+  return false;
+}
+
 bool MipsAsmParser::parseDirectiveCPSetup() {
   unsigned FuncReg;
   unsigned Save;
@@ -2550,6 +2580,8 @@ bool MipsAsmParser::parseDirectiveOption() {
 bool MipsAsmParser::ParseDirective(AsmToken DirectiveID) {
   StringRef IDVal = DirectiveID.getString();
 
+  if (IDVal == ".cpload")
+    return parseDirectiveCPLoad(DirectiveID.getLoc());
   if (IDVal == ".dword") {
     parseDataDirective(8, DirectiveID.getLoc());
     return false;
index 053e13e5565f18cb9252b3b192705e9725627df5..ab1b1f60430b67a2a36bfff803e0c02fafbca198 100644 (file)
@@ -144,6 +144,11 @@ void MipsTargetAsmStreamer::emitFMask(unsigned FPUBitmask,
   OS << "," << FPUTopSavedRegOff << '\n';
 }
 
+void MipsTargetAsmStreamer::emitDirectiveCpload(unsigned RegNo) {
+  OS << "\t.cpload\t$"
+     << StringRef(MipsInstPrinter::getRegisterName(RegNo)).lower() << "\n";
+}
+
 // This part is for ELF object output.
 MipsTargetELFStreamer::MipsTargetELFStreamer(MCStreamer &S,
                                              const MCSubtargetInfo &STI)
@@ -402,3 +407,52 @@ void MipsTargetELFStreamer::emitDirectiveSetMips64R2() {
 void MipsTargetELFStreamer::emitDirectiveSetDsp() {
   // No action required for ELF output.
 }
+
+void MipsTargetELFStreamer::emitDirectiveCpload(unsigned RegNo) {
+  // .cpload $reg
+  // This directive expands to:
+  // lui   $gp, %hi(_gp_disp)
+  // addui $gp, $gp, %lo(_gp_disp)
+  // addu  $gp, $gp, $reg
+  // when support for position independent code is enabled.
+  if (!Pic || (isN32() || isN64()))
+    return;
+
+  // There's a GNU extension controlled by -mno-shared that allows
+  // locally-binding symbols to be accessed using absolute addresses.
+  // This is currently not supported. When supported -mno-shared makes
+  // .cpload expand to:
+  //   lui     $gp, %hi(__gnu_local_gp)
+  //   addiu   $gp, $gp, %lo(__gnu_local_gp)
+
+  StringRef SymName("_gp_disp");
+  MCAssembler &MCA = getStreamer().getAssembler();
+  MCSymbol *GP_Disp = MCA.getContext().GetOrCreateSymbol(SymName);
+  MCA.getOrCreateSymbolData(*GP_Disp);
+
+  MCInst TmpInst;
+  TmpInst.setOpcode(Mips::LUi);
+  TmpInst.addOperand(MCOperand::CreateReg(Mips::GP));
+  const MCSymbolRefExpr *HiSym = MCSymbolRefExpr::Create(
+      "_gp_disp", MCSymbolRefExpr::VK_Mips_ABS_HI, MCA.getContext());
+  TmpInst.addOperand(MCOperand::CreateExpr(HiSym));
+  getStreamer().EmitInstruction(TmpInst, STI);
+
+  TmpInst.clear();
+
+  TmpInst.setOpcode(Mips::ADDiu);
+  TmpInst.addOperand(MCOperand::CreateReg(Mips::GP));
+  TmpInst.addOperand(MCOperand::CreateReg(Mips::GP));
+  const MCSymbolRefExpr *LoSym = MCSymbolRefExpr::Create(
+      "_gp_disp", MCSymbolRefExpr::VK_Mips_ABS_LO, MCA.getContext());
+  TmpInst.addOperand(MCOperand::CreateExpr(LoSym));
+  getStreamer().EmitInstruction(TmpInst, STI);
+
+  TmpInst.clear();
+
+  TmpInst.setOpcode(Mips::ADDu);
+  TmpInst.addOperand(MCOperand::CreateReg(Mips::GP));
+  TmpInst.addOperand(MCOperand::CreateReg(Mips::GP));
+  TmpInst.addOperand(MCOperand::CreateReg(RegNo));
+  getStreamer().EmitInstruction(TmpInst, STI);
+}
index 7873ed9a60e36998ecee758b4b94bf59536fe272..e649a4d14f4ab0c4d8f2df6d69399866ad4dcdad 100644 (file)
@@ -47,6 +47,9 @@ public:
   virtual void emitDirectiveSetMips64() = 0;
   virtual void emitDirectiveSetMips64R2() = 0;
   virtual void emitDirectiveSetDsp() = 0;
+
+  // PIC support
+  virtual void emitDirectiveCpload(unsigned RegNo) = 0;
 };
 
 // This part is for ascii assembly output
@@ -83,6 +86,9 @@ public:
   void emitDirectiveSetMips64() override;
   void emitDirectiveSetMips64R2() override;
   void emitDirectiveSetDsp() override;
+
+  // PIC support
+  virtual void emitDirectiveCpload(unsigned RegNo);
 };
 
 // This part is for ELF object output
@@ -128,6 +134,14 @@ public:
   void emitDirectiveSetMips64() override;
   void emitDirectiveSetMips64R2() override;
   void emitDirectiveSetDsp() override;
+
+  // PIC support
+  virtual void emitDirectiveCpload(unsigned RegNo);
+
+protected:
+  bool isO32() const { return STI.getFeatureBits() & Mips::FeatureO32; }
+  bool isN32() const { return STI.getFeatureBits() & Mips::FeatureN32; }
+  bool isN64() const { return STI.getFeatureBits() & Mips::FeatureN64; }
 };
 }
 #endif
diff --git a/test/MC/Mips/cpload-bad.s b/test/MC/Mips/cpload-bad.s
new file mode 100644 (file)
index 0000000..7d186f6
--- /dev/null
@@ -0,0 +1,15 @@
+# RUN: not llvm-mc %s -arch=mips -mcpu=mips32r2 2>%t1
+# RUN: FileCheck %s < %t1 -check-prefix=ASM
+
+        .text
+        .option pic2
+        .set reorder
+        .cpload $25
+# ASM: :[[@LINE-1]]:9: warning: .cpload in reorder section
+        .set noreorder
+        .cpload $32
+# ASM: :[[@LINE-1]]:17: error: invalid register
+        .cpload $foo
+# ASM: :[[@LINE-1]]:17: error: expected register containing function address
+        .cpload bar
+# ASM: :[[@LINE-1]]:17: error: expected register containing function address
diff --git a/test/MC/Mips/cpload.s b/test/MC/Mips/cpload.s
new file mode 100644 (file)
index 0000000..bc5e797
--- /dev/null
@@ -0,0 +1,33 @@
+# RUN: llvm-mc %s -arch=mips -mcpu=mips32r2 | FileCheck %s -check-prefix=ASM
+#
+# RUN: llvm-mc %s -arch=mips -mcpu=mips32r2 -filetype=obj -o -| \
+# RUN: llvm-objdump -d -r -arch=mips - | \
+# RUN: FileCheck %s -check-prefix=OBJ
+
+# RUN: llvm-mc %s -arch=mips64 -mcpu=mips64r2 -filetype=obj -o -| \
+# RUN: llvm-objdump -d -r -arch=mips - | \
+# RUN: FileCheck %s -check-prefix=OBJ64
+
+# ASM:    .text
+# ASM:    .option pic2
+# ASM:    .set noreorder
+# ASM:    .cpload $25
+# ASM:    .set reorder
+
+# OBJ:    .text
+# OBJ:    lui $gp, 0
+# OBJ: R_MIPS_HI16 _gp_disp
+# OBJ:    addiu $gp, $gp, 0
+# OBJ: R_MIPS_LO16 _gp_disp
+# OBJ:    addu $gp, $gp, $25
+
+# OBJ64: .text
+# OBJ64-NOT: lui $gp, 0
+# OBJ64-NOT: addiu $gp, $gp, 0
+# OBJ64-NOT: addu $gp, $gp, $25
+
+        .text
+        .option pic2
+        .set noreorder
+        .cpload $25
+        .set reorder