}
}
+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());
}
}
+ // 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.
--- /dev/null
+# 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]