Skip to content

Commit

Permalink
[RISCV] Add assembler support for LA pseudo-instruction
Browse files Browse the repository at this point in the history
This patch also introduces the emitAuipcInstPair helper, which is then used
for both emitLoadAddress and emitLoadLocalAddress.

Differential Revision: https://reviews.llvm.org/D55325
Patch by James Clarke.

llvm-svn: 354111
  • Loading branch information
asb committed Feb 15, 2019
1 parent 8eb87e5 commit 22531c4
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 20 deletions.
88 changes: 71 additions & 17 deletions llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#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"
Expand Down Expand Up @@ -78,9 +79,18 @@ class RISCVAsmParser : public MCTargetAsmParser {
// 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
Expand Down Expand Up @@ -1409,42 +1419,82 @@ void RISCVAsmParser::emitLoadImm(unsigned DestReg, int64_t Value,
}
}

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()) {
Expand All @@ -1464,9 +1514,13 @@ bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
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);
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/Target/RISCV/RISCVInstrInfo.td
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,6 @@ def SFENCE_VMA : RVInstR<0b0001001, 0b000, OPC_SYSTEM, (outs),
// Assembler Pseudo Instructions (User-Level ISA, Version 2.2, Chapter 20)
//===----------------------------------------------------------------------===//

// TODO la
// TODO lb lh lw
// TODO RV64I: ld
// TODO sb sh sw
Expand Down Expand Up @@ -845,6 +844,11 @@ let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0,
def PseudoLLA : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
"lla", "$dst, $src">;

let hasSideEffects = 0, mayLoad = 1, mayStore = 0, isCodeGenOnly = 0,
isAsmParserOnly = 1 in
def PseudoLA : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
"la", "$dst, $src">;

/// Loads

multiclass LdPat<PatFrag LoadOp, RVInst Inst> {
Expand Down
22 changes: 22 additions & 0 deletions llvm/test/MC/RISCV/rvi-pseudos-invalid.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# 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
49 changes: 47 additions & 2 deletions llvm/test/MC/RISCV/rvi-pseudos.s
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# 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)
Expand All @@ -26,3 +30,44 @@ lla a3, ra
# 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

0 comments on commit 22531c4

Please sign in to comment.