#include "llvm/MC/MCExpr.h"
#include "llvm/MC/MCInst.h"
#include "llvm/MC/MCInstBuilder.h"
+#include "llvm/MC/MCObjectFileInfo.h"
#include "llvm/MC/MCParser/MCAsmLexer.h"
#include "llvm/MC/MCParser/MCParsedAsmOperand.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
// synthesize the desired immedate value into the destination register.
void emitLoadImm(unsigned DestReg, int64_t Value, MCStreamer &Out);
+ // Helper to emit a combination of AUIPC and SecondOpcode. Used to implement
+ // helpers such as emitLoadLocalAddress and emitLoadAddress.
+ void emitAuipcInstPair(MCOperand DestReg, MCOperand TmpReg,
+ const MCExpr *Symbol, RISCVMCExpr::VariantKind VKHi,
+ unsigned SecondOpcode, SMLoc IDLoc, MCStreamer &Out);
+
// Helper to emit pseudo instruction "lla" used in PC-rel addressing.
void emitLoadLocalAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+ // Helper to emit pseudo instruction "la" used in GOT/PC-rel addressing.
+ void emitLoadAddress(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out);
+
/// Helper for processing MC instructions that have been successfully matched
/// by MatchAndEmitInstruction. Modifications to the emitted instructions,
/// like the expansion of pseudo instructions (e.g., "li"), can be performed
}
}
-void RISCVAsmParser::emitLoadLocalAddress(MCInst &Inst, SMLoc IDLoc,
- MCStreamer &Out) {
- // The local load address pseudo-instruction "lla" is used in PC-relative
- // addressing of symbols:
- // lla rdest, symbol
- // expands to
- // TmpLabel: AUIPC rdest, %pcrel_hi(symbol)
- // ADDI rdest, %pcrel_lo(TmpLabel)
+void RISCVAsmParser::emitAuipcInstPair(MCOperand DestReg, MCOperand TmpReg,
+ const MCExpr *Symbol,
+ RISCVMCExpr::VariantKind VKHi,
+ unsigned SecondOpcode, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // A pair of instructions for PC-relative addressing; expands to
+ // TmpLabel: AUIPC TmpReg, VKHi(symbol)
+ // OP DestReg, TmpReg, %pcrel_lo(TmpLabel)
MCContext &Ctx = getContext();
MCSymbol *TmpLabel = Ctx.createTempSymbol(
"pcrel_hi", /* AlwaysAddSuffix */ true, /* CanBeUnnamed */ false);
Out.EmitLabel(TmpLabel);
- MCOperand DestReg = Inst.getOperand(0);
- const RISCVMCExpr *Symbol = RISCVMCExpr::create(
- Inst.getOperand(1).getExpr(), RISCVMCExpr::VK_RISCV_PCREL_HI, Ctx);
-
+ const RISCVMCExpr *SymbolHi = RISCVMCExpr::create(Symbol, VKHi, Ctx);
emitToStreamer(
- Out, MCInstBuilder(RISCV::AUIPC).addOperand(DestReg).addExpr(Symbol));
+ Out, MCInstBuilder(RISCV::AUIPC).addOperand(TmpReg).addExpr(SymbolHi));
const MCExpr *RefToLinkTmpLabel =
RISCVMCExpr::create(MCSymbolRefExpr::create(TmpLabel, Ctx),
RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx);
- emitToStreamer(Out, MCInstBuilder(RISCV::ADDI)
- .addOperand(DestReg)
+ emitToStreamer(Out, MCInstBuilder(SecondOpcode)
.addOperand(DestReg)
+ .addOperand(TmpReg)
.addExpr(RefToLinkTmpLabel));
}
+void RISCVAsmParser::emitLoadLocalAddress(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // The load local address pseudo-instruction "lla" is used in PC-relative
+ // addressing of local symbols:
+ // lla rdest, symbol
+ // expands to
+ // TmpLabel: AUIPC rdest, %pcrel_hi(symbol)
+ // ADDI rdest, rdest, %pcrel_lo(TmpLabel)
+ MCOperand DestReg = Inst.getOperand(0);
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ emitAuipcInstPair(DestReg, DestReg, Symbol, RISCVMCExpr::VK_RISCV_PCREL_HI,
+ RISCV::ADDI, IDLoc, Out);
+}
+
+void RISCVAsmParser::emitLoadAddress(MCInst &Inst, SMLoc IDLoc,
+ MCStreamer &Out) {
+ // The load address pseudo-instruction "la" is used in PC-relative and
+ // GOT-indirect addressing of global symbols:
+ // la rdest, symbol
+ // expands to either (for non-PIC)
+ // TmpLabel: AUIPC rdest, %pcrel_hi(symbol)
+ // ADDI rdest, rdest, %pcrel_lo(TmpLabel)
+ // or (for PIC)
+ // TmpLabel: AUIPC rdest, %got_pcrel_hi(symbol)
+ // Lx rdest, %pcrel_lo(TmpLabel)(rdest)
+ MCOperand DestReg = Inst.getOperand(0);
+ const MCExpr *Symbol = Inst.getOperand(1).getExpr();
+ unsigned SecondOpcode;
+ RISCVMCExpr::VariantKind VKHi;
+ // FIXME: Should check .option (no)pic when implemented
+ if (getContext().getObjectFileInfo()->isPositionIndependent()) {
+ SecondOpcode = isRV64() ? RISCV::LD : RISCV::LW;
+ VKHi = RISCVMCExpr::VK_RISCV_GOT_HI;
+ } else {
+ SecondOpcode = RISCV::ADDI;
+ VKHi = RISCVMCExpr::VK_RISCV_PCREL_HI;
+ }
+ emitAuipcInstPair(DestReg, DestReg, Symbol, VKHi, SecondOpcode, IDLoc, Out);
+}
+
bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
MCStreamer &Out) {
Inst.setLoc(IDLoc);
- if (Inst.getOpcode() == RISCV::PseudoLI) {
+ switch (Inst.getOpcode()) {
+ default:
+ break;
+ case RISCV::PseudoLI: {
unsigned Reg = Inst.getOperand(0).getReg();
const MCOperand &Op1 = Inst.getOperand(1);
if (Op1.isExpr()) {
Imm = SignExtend64<32>(Imm);
emitLoadImm(Reg, Imm, Out);
return false;
- } else if (Inst.getOpcode() == RISCV::PseudoLLA) {
+ }
+ case RISCV::PseudoLLA:
emitLoadLocalAddress(Inst, IDLoc, Out);
return false;
+ case RISCV::PseudoLA:
+ emitLoadAddress(Inst, IDLoc, Out);
+ return false;
}
emitToStreamer(Out, Inst);
--- /dev/null
+# RUN: not llvm-mc %s -triple=riscv64 2>&1 | FileCheck %s
+# RUN: not llvm-mc %s -triple=riscv64 2>&1 | FileCheck %s
+
+lla x1, 1234 # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %pcrel_hi(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %pcrel_lo(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %pcrel_hi(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %pcrel_lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %hi(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %lo(1234) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %hi(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+lla x1, %lo(foo) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
+
+la x1, 1234 # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %pcrel_hi(1234) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %pcrel_lo(1234) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %pcrel_hi(foo) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %pcrel_lo(foo) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %hi(1234) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %lo(1234) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %hi(foo) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
+la x1, %lo(foo) # CHECK: :[[@LINE]]:8: error: operand must be a bare symbol name
-# RUN: llvm-mc %s -triple=riscv32 | FileCheck %s
-# RUN: llvm-mc %s -triple=riscv64 | FileCheck %s
+# RUN: llvm-mc %s -triple=riscv32 | FileCheck %s --check-prefixes=CHECK,CHECK-NOPIC
+# RUN: llvm-mc %s -triple=riscv64 | FileCheck %s --check-prefixes=CHECK,CHECK-NOPIC
+# RUN: llvm-mc %s -triple=riscv32 -position-independent \
+# RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-PIC,CHECK-PIC-RV32
+# RUN: llvm-mc %s -triple=riscv64 -position-independent \
+# RUN: | FileCheck %s --check-prefixes=CHECK,CHECK-PIC,CHECK-PIC-RV64
# CHECK: .Lpcrel_hi0:
# CHECK: auipc a0, %pcrel_hi(a_symbol)
# CHECK: auipc a4, %pcrel_hi(f1)
# CHECK: addi a4, a4, %pcrel_lo(.Lpcrel_hi4)
lla a4, f1
+
+# CHECK: .Lpcrel_hi5:
+# CHECK-NOPIC: auipc a0, %pcrel_hi(a_symbol)
+# CHECK-NOPIC: addi a0, a0, %pcrel_lo(.Lpcrel_hi5)
+# CHECK-PIC: auipc a0, %got_pcrel_hi(a_symbol)
+# CHECK-PIC-RV32: lw a0, %pcrel_lo(.Lpcrel_hi5)(a0)
+# CHECK-PIC-RV64: ld a0, %pcrel_lo(.Lpcrel_hi5)(a0)
+la a0, a_symbol
+
+# CHECK: .Lpcrel_hi6:
+# CHECK-NOPIC: auipc a1, %pcrel_hi(another_symbol)
+# CHECK-NOPIC: addi a1, a1, %pcrel_lo(.Lpcrel_hi6)
+# CHECK-PIC: auipc a1, %got_pcrel_hi(another_symbol)
+# CHECK-PIC-RV32: lw a1, %pcrel_lo(.Lpcrel_hi6)(a1)
+# CHECK-PIC-RV64: ld a1, %pcrel_lo(.Lpcrel_hi6)(a1)
+la a1, another_symbol
+
+# Check that we can load the address of symbols that are spelled like a register
+# CHECK: .Lpcrel_hi7:
+# CHECK-NOPIC: auipc a2, %pcrel_hi(zero)
+# CHECK-NOPIC: addi a2, a2, %pcrel_lo(.Lpcrel_hi7)
+# CHECK-PIC: auipc a2, %got_pcrel_hi(zero)
+# CHECK-PIC-RV32: lw a2, %pcrel_lo(.Lpcrel_hi7)(a2)
+# CHECK-PIC-RV64: ld a2, %pcrel_lo(.Lpcrel_hi7)(a2)
+la a2, zero
+
+# CHECK: .Lpcrel_hi8:
+# CHECK-NOPIC: auipc a3, %pcrel_hi(ra)
+# CHECK-NOPIC: addi a3, a3, %pcrel_lo(.Lpcrel_hi8)
+# CHECK-PIC: auipc a3, %got_pcrel_hi(ra)
+# CHECK-PIC-RV32: lw a3, %pcrel_lo(.Lpcrel_hi8)(a3)
+# CHECK-PIC-RV64: ld a3, %pcrel_lo(.Lpcrel_hi8)(a3)
+la a3, ra
+
+# CHECK: .Lpcrel_hi9:
+# CHECK-NOPIC: auipc a4, %pcrel_hi(f1)
+# CHECK-NOPIC: addi a4, a4, %pcrel_lo(.Lpcrel_hi9)
+# CHECK-PIC: auipc a4, %got_pcrel_hi(f1)
+# CHECK-PIC-RV32: lw a4, %pcrel_lo(.Lpcrel_hi9)(a4)
+# CHECK-PIC-RV64: ld a4, %pcrel_lo(.Lpcrel_hi9)(a4)
+la a4, f1