Index: lib/Target/Mips/AsmParser/MipsAsmParser.cpp =================================================================== --- lib/Target/Mips/AsmParser/MipsAsmParser.cpp +++ lib/Target/Mips/AsmParser/MipsAsmParser.cpp @@ -1269,6 +1269,44 @@ } } +static const MCSymbol *getSingleMCSymbol(const MCExpr *Expr) { + if (const MCSymbolRefExpr *SRExpr = dyn_cast(Expr)) { + return &SRExpr->getSymbol(); + } + + if (const MCBinaryExpr *BExpr = dyn_cast(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(Expr)) + return getSingleMCSymbol(UExpr->getSubExpr()); + + return nullptr; +} + +static unsigned countMCSymbolRefExpr(const MCExpr *Expr) { + if (isa(Expr)) + return 1; + + if (const MCBinaryExpr *BExpr = dyn_cast(Expr)) + return countMCSymbolRefExpr(BExpr->getLHS()) + + countMCSymbolRefExpr(BExpr->getRHS()); + + if (const MCUnaryExpr *UExpr = dyn_cast(Expr)) + return countMCSymbolRefExpr(UExpr->getSubExpr()); + + return 0; +} + bool MipsAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc, SmallVectorImpl &Instructions) { const MCInstrDesc &MCID = getInstDesc(Inst.getOpcode()); @@ -1419,6 +1457,90 @@ } } + // 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"); + + 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: test/ExecutionEngine/RuntimeDyld/Mips/ELF_Mips64r2N64_PIC_relocations.s =================================================================== --- test/ExecutionEngine/RuntimeDyld/Mips/ELF_Mips64r2N64_PIC_relocations.s +++ test/ExecutionEngine/RuntimeDyld/Mips/ELF_Mips64r2N64_PIC_relocations.s @@ -41,7 +41,9 @@ # 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. Index: test/MC/Mips/expansion-jal-sym-pic.s =================================================================== --- /dev/null +++ test/MC/Mips/expansion-jal-sym-pic.s @@ -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: test/MC/Mips/set-nomacro.s =================================================================== --- test/MC/Mips/set-nomacro.s +++ test/MC/Mips/set-nomacro.s @@ -60,6 +60,11 @@ bgtu $0, $8, local_label bgtu $0, $0, local_label + jal foo + .option pic2 + jal foo + .option pic0 + add $4, $5, $6 .set noreorder @@ -168,5 +173,12 @@ bgtu $0, $0, local_label # CHECK-NOT: [[@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