[mips] Expand JAL instructions when PIC is enabled.
authorDaniel Sanders <daniel.sanders@imgtec.com>
Tue, 18 Aug 2015 16:18:09 +0000 (16:18 +0000)
committerDaniel Sanders <daniel.sanders@imgtec.com>
Tue, 18 Aug 2015 16:18:09 +0000 (16:18 +0000)
Summary: This is the correct way to handle JAL instructions when PIC is enabled.

Patch by Toma Tabacu

Reviewers: seanbruno, tomatabacu

Subscribers: brooks, seanbruno, emaste, llvm-commits

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

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

lib/Target/Mips/AsmParser/MipsAsmParser.cpp
test/ExecutionEngine/RuntimeDyld/Mips/ELF_Mips64r2N64_PIC_relocations.s
test/MC/Mips/expansion-jal-sym-pic.s [new file with mode: 0644]
test/MC/Mips/set-nomacro.s

index 54649fa7080d4f0d7494442ea7556fdd4476bbc0..45115ee0e1b89e22726ad956238f5f19af9ff7fe 100644 (file)
@@ -1318,6 +1318,44 @@ static bool hasShortDelaySlot(unsigned Opcode) {
   }
 }
 
+static const MCSymbol *getSingleMCSymbol(const MCExpr *Expr) {
+  if (const MCSymbolRefExpr *SRExpr = dyn_cast<MCSymbolRefExpr>(Expr)) {
+    return &SRExpr->getSymbol();
+  }
+
+  if (const MCBinaryExpr *BExpr = dyn_cast<MCBinaryExpr>(Expr)) {
+    const MCSymbol *LHSSym = getSingleMCSymbol(BExpr->getLHS());
+    const MCSymbol *RHSSym = getSingleMCSymbol(BExpr->getRHS());
+
+    if (LHSSym)
+      return LHSSym;
+
+    if (RHSSym)
+      return RHSSym;
+
+    return nullptr;
+  }
+
+  if (const MCUnaryExpr *UExpr = dyn_cast<MCUnaryExpr>(Expr))
+    return getSingleMCSymbol(UExpr->getSubExpr());
+
+  return nullptr;
+}
+
+static unsigned countMCSymbolRefExpr(const MCExpr *Expr) {
+  if (isa<MCSymbolRefExpr>(Expr))
+    return 1;
+
+  if (const MCBinaryExpr *BExpr = dyn_cast<MCBinaryExpr>(Expr))
+    return countMCSymbolRefExpr(BExpr->getLHS()) +
+           countMCSymbolRefExpr(BExpr->getRHS());
+
+  if (const MCUnaryExpr *UExpr = dyn_cast<MCUnaryExpr>(Expr))
+    return countMCSymbolRefExpr(UExpr->getSubExpr());
+
+  return 0;
+}
+
 bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
                                        SmallVectorImpl<MCInst> &Instructions) {
   const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode());
@@ -1468,6 +1506,94 @@ bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
     }
   }
 
+  // This expansion is not in a function called by expandInstruction() because
+  // the pseudo-instruction doesn't have a distinct opcode.
+  if ((Inst.getOpcode() == Mips::JAL || Inst.getOpcode() == Mips::JAL_MM) &&
+      inPicMode()) {
+    warnIfNoMacro(IDLoc);
+
+    const MCExpr *JalExpr = Inst.getOperand(0).getExpr();
+
+    // We can do this expansion if there's only 1 symbol in the argument
+    // expression.
+    if (countMCSymbolRefExpr(JalExpr) > 1)
+      return Error(IDLoc, "jal doesn't support multiple symbols in PIC mode");
+
+    // FIXME: This is checking the expression can be handled by the later stages
+    //        of the assembler. We ought to leave it to those later stages but
+    //        we can't do that until we stop evaluateRelocExpr() rewriting the
+    //        expressions into non-equivalent forms.
+    const MCSymbol *JalSym = getSingleMCSymbol(JalExpr);
+
+    // FIXME: Add support for label+offset operands (currently causes an error).
+    // FIXME: Add support for forward-declared local symbols.
+    // FIXME: Add expansion for when the LargeGOT option is enabled.
+    if (JalSym->isInSection() || JalSym->isTemporary()) {
+      if (isABI_O32()) {
+        // If it's a local symbol and the O32 ABI is being used, we expand to:
+        //  lw    $25, 0($gp)
+        //    R_(MICRO)MIPS_GOT16  label
+        //  addiu $25, $25, 0
+        //    R_(MICRO)MIPS_LO16   label
+        //  jalr  $25
+        const MCExpr *Got16RelocExpr = evaluateRelocExpr(JalExpr, "got");
+        const MCExpr *Lo16RelocExpr = evaluateRelocExpr(JalExpr, "lo");
+
+        MCInst LwInst;
+        LwInst.setOpcode(Mips::LW);
+        LwInst.addOperand(MCOperand::createReg(Mips::T9));
+        LwInst.addOperand(MCOperand::createReg(Mips::GP));
+        LwInst.addOperand(MCOperand::createExpr(Got16RelocExpr));
+        Instructions.push_back(LwInst);
+
+        MCInst AddiuInst;
+        AddiuInst.setOpcode(Mips::ADDiu);
+        AddiuInst.addOperand(MCOperand::createReg(Mips::T9));
+        AddiuInst.addOperand(MCOperand::createReg(Mips::T9));
+        AddiuInst.addOperand(MCOperand::createExpr(Lo16RelocExpr));
+        Instructions.push_back(AddiuInst);
+      } else if (isABI_N32() || isABI_N64()) {
+        // If it's a local symbol and the N32/N64 ABIs are being used,
+        // we expand to:
+        //  lw/ld    $25, 0($gp)
+        //    R_(MICRO)MIPS_GOT_DISP  label
+        //  jalr  $25
+        const MCExpr *GotDispRelocExpr = evaluateRelocExpr(JalExpr, "got_disp");
+
+        MCInst LoadInst;
+        LoadInst.setOpcode(ABI.ArePtrs64bit() ? Mips::LD : Mips::LW);
+        LoadInst.addOperand(MCOperand::createReg(Mips::T9));
+        LoadInst.addOperand(MCOperand::createReg(Mips::GP));
+        LoadInst.addOperand(MCOperand::createExpr(GotDispRelocExpr));
+        Instructions.push_back(LoadInst);
+      }
+    } else {
+      // If it's an external/weak symbol, we expand to:
+      //  lw/ld    $25, 0($gp)
+      //    R_(MICRO)MIPS_CALL16  label
+      //  jalr  $25
+      const MCExpr *Call16RelocExpr = evaluateRelocExpr(JalExpr, "call16");
+
+      MCInst LoadInst;
+      LoadInst.setOpcode(ABI.ArePtrs64bit() ? Mips::LD : Mips::LW);
+      LoadInst.addOperand(MCOperand::createReg(Mips::T9));
+      LoadInst.addOperand(MCOperand::createReg(Mips::GP));
+      LoadInst.addOperand(MCOperand::createExpr(Call16RelocExpr));
+      Instructions.push_back(LoadInst);
+    }
+
+    MCInst JalrInst;
+    JalrInst.setOpcode(inMicroMipsMode() ? Mips::JALR_MM : Mips::JALR);
+    JalrInst.addOperand(MCOperand::createReg(Mips::RA));
+    JalrInst.addOperand(MCOperand::createReg(Mips::T9));
+
+    // FIXME: Add an R_(MICRO)MIPS_JALR relocation after the JALR.
+    // This relocation is supposed to be an optimization hint for the linker
+    // and is not necessary for correctness.
+
+    Inst = JalrInst;
+  }
+
   if (MCID.mayLoad() || MCID.mayStore()) {
     // Check the offset of memory operand, if it is a symbol
     // reference or immediate we may have to expand instructions.
index 1d8d293a26a4f94ca32df398a8e46c87fc669937..4d71b641025ce3e174ec6f0ff331bce921e46ffa 100644 (file)
@@ -41,7 +41,9 @@ bar:
 # Test R_MIPS_26 relocation.
 # rtdyld-check:  decode_operand(insn1, 0)[25:0] = foo
 insn1:
+       .option pic0
        jal   foo
+       .option pic2
        nop
 
 # Test R_MIPS_PC16 relocation.
diff --git a/test/MC/Mips/expansion-jal-sym-pic.s b/test/MC/Mips/expansion-jal-sym-pic.s
new file mode 100644 (file)
index 0000000..23a4396
--- /dev/null
@@ -0,0 +1,183 @@
+# RUN: llvm-mc %s -arch=mips -mcpu=mips32 -show-encoding |\
+# RUN:   FileCheck %s -check-prefix=ALL -check-prefix=NORMAL -check-prefix=O32
+
+# RUN: llvm-mc %s -arch=mips -mcpu=mips64 -target-abi n32 -show-encoding |\
+# RUN:   FileCheck %s -check-prefix=ALL -check-prefix=NORMAL -check-prefix=N32
+
+# RUN: llvm-mc %s -arch=mips64 -mcpu=mips64 -target-abi n64 -show-encoding |\
+# RUN:   FileCheck %s -check-prefix=ALL -check-prefix=NORMAL -check-prefix=N64
+
+# RUN: llvm-mc %s -arch=mips -mcpu=mips32 -mattr=micromips -show-encoding |\
+# RUN:   FileCheck %s -check-prefix=ALL -check-prefix=MICROMIPS -check-prefix=O32-MICROMIPS
+
+# RUN: llvm-mc %s -arch=mips -mcpu=mips64 -target-abi n32 -mattr=micromips -show-encoding |\
+# RUN:   FileCheck %s -check-prefix=ALL -check-prefix=MICROMIPS -check-prefix=N32-MICROMIPS
+
+# RUN: llvm-mc %s -arch=mips64 -mcpu=mips64 -target-abi n64 -mattr=micromips -show-encoding |\
+# RUN:   FileCheck %s -check-prefix=ALL -check-prefix=MICROMIPS -check-prefix=N64-MICROMIPS
+
+  .weak weak_label
+
+  .text
+  .option pic2
+
+  .ent local_label
+local_label:
+  .frame  $sp, 0, $ra
+  .set noreorder
+
+  jal local_label
+  nop
+
+  jal weak_label
+  nop
+
+  jal global_label
+  nop
+
+  jal .text
+  nop
+
+  # local labels ($tmp symbols)
+  jal 1f
+  nop
+
+  .end local_label
+
+1:
+  nop
+  add $8, $8, $8
+  nop
+
+# Expanding "jal local_label":
+# O32: lw     $25, %got(local_label)($gp)   # encoding: [0x8f,0x99,A,A]
+# O32:                                      #   fixup A - offset: 0, value: local_label@GOT, kind:   fixup_Mips_GOT_Local
+# O32: addiu  $25, $25, %lo(local_label)    # encoding: [0x27,0x39,A,A]
+# O32:                                      #   fixup A - offset: 0, value: local_label@ABS_LO, kind:   fixup_Mips_LO16
+
+# N32: lw  $25, %got_disp(local_label)($gp) # encoding: [0x8f,0x99,A,A]
+# N32:                                      #   fixup A - offset: 0, value: local_label@GOT_DISP, kind:   fixup_Mips_GOT_DISP
+
+# N64: ld  $25, %got_disp(local_label)($gp) # encoding: [0xdf,0x99,A,A]
+# N64:                                      #   fixup A - offset: 0, value: local_label@GOT_DISP, kind:   fixup_Mips_GOT_DISP
+
+# O32-MICROMIPS: lw    $25, %got(local_label)($gp)      # encoding: [0xff,0x3c,A,A]
+# O32-MICROMIPS:                                        #   fixup A - offset: 0, value: local_label@GOT, kind:   fixup_MICROMIPS_GOT16
+# O32-MICROMIPS: addiu $25, $25, %lo(local_label)       # encoding: [0x33,0x39,A,A]
+# O32-MICROMIPS:                                        #   fixup A - offset: 0, value: local_label@ABS_LO, kind:   fixup_MICROMIPS_LO16
+
+# N32-MICROMIPS: lw    $25, %got_disp(local_label)($gp) # encoding: [0xff,0x3c,A,A]
+# N32-MICROMIPS:                                        #   fixup A - offset: 0, value: local_label@GOT_DISP, kind: fixup_MICROMIPS_GOT_DISP
+
+# N64-MICROMIPS: ld    $25, %got_disp(local_label)($gp) # encoding: [0xdf,0x99,A,A]
+# N64-MICROMIPS:                                        #   fixup A - offset: 0, value: local_label@GOT_DISP, kind: fixup_MICROMIPS_GOT_DISP
+
+# NORMAL:    jalr $25      # encoding: [0x03,0x20,0xf8,0x09]
+# MICROMIPS: jalr $ra, $25 # encoding: [0x03,0xf9,0x0f,0x3c]
+# ALL:       nop           # encoding: [0x00,0x00,0x00,0x00]
+
+
+# Expanding "jal weak_label":
+# O32: lw  $25, %call16(weak_label)($gp) # encoding: [0x8f,0x99,A,A]
+# O32:                                   #   fixup A - offset: 0, value: weak_label@GOT_CALL, kind:   fixup_Mips_CALL16
+
+# N32: lw  $25, %call16(weak_label)($gp) # encoding: [0x8f,0x99,A,A]
+# N32:                                   #   fixup A - offset: 0, value: weak_label@GOT_CALL, kind:   fixup_Mips_CALL16
+
+# N64: ld  $25, %call16(weak_label)($gp) # encoding: [0xdf,0x99,A,A]
+# N64:                                   #   fixup A - offset: 0, value: weak_label@GOT_CALL, kind:   fixup_Mips_CALL16
+
+# O32-MICROMIPS: lw  $25, %call16(weak_label)($gp) # encoding: [0xff,0x3c,A,A]
+# O32-MICROMIPS:                                   #   fixup A - offset: 0, value: weak_label@GOT_CALL, kind:   fixup_MICROMIPS_CALL16
+
+# N32-MICROMIPS: lw  $25, %call16(weak_label)($gp) # encoding: [0xff,0x3c,A,A]
+# N32-MICROMIPS:                                   #   fixup A - offset: 0, value: weak_label@GOT_CALL, kind: fixup_MICROMIPS_CALL16
+
+# N64-MICROMIPS: ld  $25, %call16(weak_label)($gp) # encoding: [0xdf,0x99,A,A]
+# N64-MICROMIPS:                                   #   fixup A - offset: 0, value: weak_label@GOT_CALL, kind: fixup_MICROMIPS_CALL16
+
+# NORMAL:    jalr $25      # encoding: [0x03,0x20,0xf8,0x09]
+# MICROMIPS: jalr $ra, $25 # encoding: [0x03,0xf9,0x0f,0x3c]
+# ALL:       nop           # encoding: [0x00,0x00,0x00,0x00]
+
+
+# Expanding "jal global_label":
+# O32: lw  $25, %call16(global_label)($gp) # encoding: [0x8f,0x99,A,A]
+# O32:                                     #   fixup A - offset: 0, value: global_label@GOT_CALL, kind:   fixup_Mips_CALL16
+
+# N32: lw  $25, %call16(global_label)($gp) # encoding: [0x8f,0x99,A,A]
+# N32:                                     #   fixup A - offset: 0, value: global_label@GOT_CALL, kind:   fixup_Mips_CALL16
+
+# N64: ld  $25, %call16(global_label)($gp) # encoding: [0xdf,0x99,A,A]
+# N64:                                     #   fixup A - offset: 0, value: global_label@GOT_CALL, kind:   fixup_Mips_CALL16
+
+# O32-MICROMIPS: lw  $25, %call16(global_label)($gp) # encoding: [0xff,0x3c,A,A]
+# O32-MICROMIPS:                                     #   fixup A - offset: 0, value: global_label@GOT_CALL, kind: fixup_MICROMIPS_CALL16
+
+# N32-MICROMIPS: lw  $25, %call16(global_label)($gp) # encoding: [0xff,0x3c,A,A]
+# N32-MICROMIPS:                                     #   fixup A - offset: 0, value: global_label@GOT_CALL, kind: fixup_MICROMIPS_CALL16
+
+# N64-MICROMIPS: ld  $25, %call16(global_label)($gp) # encoding: [0xdf,0x99,A,A]
+# N64-MICROMIPS:                                     #   fixup A - offset: 0, value: global_label@GOT_CALL, kind: fixup_MICROMIPS_CALL16
+
+# NORMAL:    jalr $25      # encoding: [0x03,0x20,0xf8,0x09]
+# MICROMIPS: jalr $ra, $25 # encoding: [0x03,0xf9,0x0f,0x3c]
+# ALL:       nop           # encoding: [0x00,0x00,0x00,0x00]
+
+
+# FIXME: The .text section MCSymbol isn't created when printing assembly. However,
+# it is created when generating an ELF object file.
+# Expanding "jal .text":
+# O32-FIXME: lw    $25, %got(.text)($gp)           # encoding: [0x8f,0x99,A,A]
+# O32-FIXME:                                       #   fixup A - offset: 0, value: .text@GOT, kind: fixup_Mips_GOT_Local
+# O32-FIXME: addiu $25, $25, %lo(.text)            # encoding: [0x27,0x39,A,A]
+# O32-FIXME:                                       #   fixup A - offset: 0, value: .text@ABS_LO, kind: fixup_Mips_LO16
+
+# N32-FIXME: lw  $25, %got_disp(.text)($gp)        # encoding: [0x8f,0x99,A,A]
+# N32-FIXME:                                       #   fixup A - offset: 0, value: .text@GOT_DISP, kind: fixup_Mips_GOT_DISP
+
+# N64-FIXME: ld  $25, %got_disp(.text)($gp)        # encoding: [0xdf,0x99,A,A]
+# N64-FIXME:                                       #   fixup A - offset: 0, value: .text@GOT_DISP, kind: fixup_Mips_GOT_DISP
+
+# O32-MICROMIPS-FIXME: lw    $25, %got(.text)($gp)      # encoding: [0xff,0x3c,A,A]
+# O32-MICROMIPS-FIXME:                                  #   fixup A - offset: 0, value: .text@GOT, kind: fixup_MICROMIPS_GOT16
+# O32-MICROMIPS-FIXME: addiu $25, $25, %lo(.text)       # encoding: [0x33,0x39,A,A]
+# O32-MICROMIPS-FIXME:                                  #   fixup A - offset: 0, value: .text@ABS_LO, kind: fixup_MICROMIPS_LO16
+
+# N32-MICROMIPS-FIXME: lw    $25, %got_disp(.text)($gp) # encoding: [0xff,0x3c,A,A]
+# N32-MICROMIPS-FIXME:                                  #   fixup A - offset: 0, value: .text@GOT_DISP, kind: fixup_MICROMIPS_GOT_DISP
+
+# N64-MICROMIPS-FIXME: ld    $25, %got_disp(.text)($gp) # encoding: [0xdf,0x99,A,A]
+# N64-MICROMIPS-FIXME:                                  #   fixup A - offset: 0, value: .text@GOT_DISP, kind: fixup_MICROMIPS_GOT_DISP
+
+# NORMAL:    jalr $25      # encoding: [0x03,0x20,0xf8,0x09]
+# MICROMIPS: jalr $ra, $25 # encoding: [0x03,0xf9,0x0f,0x3c]
+# ALL:       nop           # encoding: [0x00,0x00,0x00,0x00]
+
+
+# Expanding "jal 1f":
+# O32: lw     $25, %got($tmp0)($gp)   # encoding: [0x8f,0x99,A,A]
+# O32:                                #   fixup A - offset: 0, value: ($tmp0)@GOT, kind:   fixup_Mips_GOT_Local
+# O32: addiu  $25, $25, %lo($tmp0)    # encoding: [0x27,0x39,A,A]
+# O32:                                #   fixup A - offset: 0, value: ($tmp0)@ABS_LO, kind:   fixup_Mips_LO16
+
+# N32: lw  $25, %got_disp($tmp0)($gp) # encoding: [0x8f,0x99,A,A]
+# N32:                                #   fixup A - offset: 0, value: ($tmp0)@GOT_DISP, kind:   fixup_Mips_GOT_DISP
+
+# N64: ld  $25, %got_disp($tmp0)($gp) # encoding: [0xdf,0x99,A,A]
+# N64:                                #   fixup A - offset: 0, value: ($tmp0)@GOT_DISP, kind:   fixup_Mips_GOT_DISP
+
+# O32-MICROMIPS: lw    $25, %got($tmp0)($gp)    # encoding: [0xff,0x3c,A,A]
+# O32-MICROMIPS:                                #   fixup A - offset: 0, value: ($tmp0)@GOT, kind: fixup_MICROMIPS_GOT16
+# O32-MICROMIPS: addiu $25, $25, %lo($tmp0)     # encoding: [0x33,0x39,A,A]
+# O32-MICROMIPS:                                #   fixup A - offset: 0, value: ($tmp0)@ABS_LO, kind: fixup_MICROMIPS_LO16
+
+# N32-MICROMIPS: lw  $25, %got_disp($tmp0)($gp) # encoding: [0xff,0x3c,A,A]
+# N32-MICROMIPS:                                #   fixup A - offset: 0, value: ($tmp0)@GOT_DISP, kind: fixup_MICROMIPS_GOT_DISP
+
+# N64-MICROMIPS: ld  $25, %got_disp($tmp0)($gp) # encoding: [0xdf,0x99,A,A]
+# N64-MICROMIPS:                                #   fixup A - offset: 0, value: ($tmp0)@GOT_DISP, kind: fixup_MICROMIPS_GOT_DISP
+
+# NORMAL:    jalr $25      # encoding: [0x03,0x20,0xf8,0x09]
+# MICROMIPS: jalr $ra, $25 # encoding: [0x03,0xf9,0x0f,0x3c]
+# ALL:       nop           # encoding: [0x00,0x00,0x00,0x00]
index 3f82f81613158162845326d2677901bad75db0dd..3bfee363351c5d8ee905d6dae6cd2ed23bbaa892 100644 (file)
   ulw $8, 2($9)
   ulw $8, 0x8000($9)
 
+  jal foo
+  .option pic2
+  jal foo
+  .option pic0
+
   add $4, $5, $6
 
   .set noreorder
   ulw $8, 0x8000($9)
 # CHECK: [[@LINE-1]]:3: warning: macro instruction expanded into multiple instructions
 
+  jal foo
+# CHECK-NOT: [[@LINE-1]]:3: warning: macro instruction expanded into multiple instructions
+  .option pic2
+  jal foo
+# CHECK: [[@LINE-1]]:3: warning: macro instruction expanded into multiple instructions
+  .option pic0
+
   add $4, $5, $6
 # CHECK-NOT: [[@LINE-1]]:3: warning: macro instruction expanded into multiple instructions