forked from OSchip/llvm-project
[RISCV] Add "lla" pseudo-instruction to assembler
This pseudo-instruction is similar to la but uses PC-relative addressing unconditionally. This is, la is only different to lla when using -fPIC. This pseudo-instruction seems often forgotten in several specs but it is definitely mentioned in binutils opcodes/riscv-opc.c. The semantics are defined both in page 37 of the "RISC-V Reader" book but also in function macro found in gas/config/tc-riscv.c. This is a very first step towards adding PIC support for Linux in the RISC-V backend. The lla pseudo-instruction expands to a sequence of auipc + addi with a couple of pc-rel relocations where the second points to the first one. This is described in https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md#pc-relative-symbol-addresses For now, this patch only introduces support of that pseudo instruction at the assembler parser. Differential Revision: https://reviews.llvm.org/D49661 llvm-svn: 339314
This commit is contained in:
parent
7164b7d347
commit
577a97e2b9
|
@ -73,6 +73,9 @@ 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 pseudo instruction "lla" used in PC-rel addressing.
|
||||
void emitLoadLocalAddress(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
|
||||
|
@ -964,6 +967,26 @@ bool RISCVAsmParser::parseOperand(OperandVector &Operands,
|
|||
return true;
|
||||
}
|
||||
|
||||
/// Return true if the operand at the OperandIdx for opcode Name should be
|
||||
/// 'forced' to be parsed as an immediate. This is required for
|
||||
/// pseudoinstructions such as tail or call, which allow bare symbols to be used
|
||||
/// that could clash with register names.
|
||||
static bool shouldForceImediateOperand(StringRef Name, unsigned OperandIdx) {
|
||||
// FIXME: This may not scale so perhaps we want to use a data-driven approach
|
||||
// instead.
|
||||
switch (OperandIdx) {
|
||||
case 0:
|
||||
// call imm
|
||||
// tail imm
|
||||
return Name == "tail" || Name == "call";
|
||||
case 1:
|
||||
// lla rdest, imm
|
||||
return Name == "lla";
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info,
|
||||
StringRef Name, SMLoc NameLoc,
|
||||
OperandVector &Operands) {
|
||||
|
@ -975,18 +998,20 @@ bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info,
|
|||
return false;
|
||||
|
||||
// Parse first operand
|
||||
bool ForceImmediate = (Name == "call" || Name == "tail");
|
||||
if (parseOperand(Operands, ForceImmediate))
|
||||
if (parseOperand(Operands, shouldForceImediateOperand(Name, 0)))
|
||||
return true;
|
||||
|
||||
// Parse until end of statement, consuming commas between operands
|
||||
unsigned OperandIdx = 1;
|
||||
while (getLexer().is(AsmToken::Comma)) {
|
||||
// Consume comma token
|
||||
getLexer().Lex();
|
||||
|
||||
// Parse next operand
|
||||
if (parseOperand(Operands, false))
|
||||
if (parseOperand(Operands, shouldForceImediateOperand(Name, OperandIdx)))
|
||||
return true;
|
||||
|
||||
++OperandIdx;
|
||||
}
|
||||
|
||||
if (getLexer().isNot(AsmToken::EndOfStatement)) {
|
||||
|
@ -1184,6 +1209,39 @@ void RISCVAsmParser::emitLoadImm(unsigned DestReg, int64_t Value,
|
|||
.addImm(Lo12));
|
||||
}
|
||||
|
||||
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)
|
||||
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);
|
||||
|
||||
MCInst &AUIPC =
|
||||
MCInstBuilder(RISCV::AUIPC).addOperand(DestReg).addExpr(Symbol);
|
||||
emitToStreamer(Out, AUIPC);
|
||||
|
||||
const MCExpr *RefToLinkTmpLabel =
|
||||
RISCVMCExpr::create(MCSymbolRefExpr::create(TmpLabel, Ctx),
|
||||
RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx);
|
||||
|
||||
MCInst &ADDI = MCInstBuilder(RISCV::ADDI)
|
||||
.addOperand(DestReg)
|
||||
.addOperand(DestReg)
|
||||
.addExpr(RefToLinkTmpLabel);
|
||||
emitToStreamer(Out, ADDI);
|
||||
}
|
||||
|
||||
bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
|
||||
MCStreamer &Out) {
|
||||
Inst.setLoc(IDLoc);
|
||||
|
@ -1198,6 +1256,9 @@ bool RISCVAsmParser::processInstruction(MCInst &Inst, SMLoc IDLoc,
|
|||
Imm = SignExtend64<32>(Imm);
|
||||
emitLoadImm(Reg, Imm, Out);
|
||||
return false;
|
||||
} else if (Inst.getOpcode() == RISCV::PseudoLLA) {
|
||||
emitLoadLocalAddress(Inst, IDLoc, Out);
|
||||
return false;
|
||||
}
|
||||
|
||||
emitToStreamer(Out, Inst);
|
||||
|
|
|
@ -759,6 +759,11 @@ def : Pat<(Tail (iPTR tglobaladdr:$dst)),
|
|||
def : Pat<(Tail (iPTR texternalsym:$dst)),
|
||||
(PseudoTAIL texternalsym:$dst)>;
|
||||
|
||||
let hasSideEffects = 0, mayLoad = 0, mayStore = 0, isCodeGenOnly = 0,
|
||||
isAsmParserOnly = 1 in
|
||||
def PseudoLLA : Pseudo<(outs GPR:$dst), (ins bare_symbol:$src), [],
|
||||
"lla", "$dst, $src">;
|
||||
|
||||
/// Loads
|
||||
|
||||
multiclass LdPat<PatFrag LoadOp, RVInst Inst> {
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# RUN: not llvm-mc -triple=riscv32 < %s 2>&1 | FileCheck %s
|
||||
# RUN: not llvm-mc -triple=riscv64 < %s 2>&1 | FileCheck %s
|
||||
|
||||
# Non bare symbols must be rejected
|
||||
lla a2, %lo(a_symbol) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
|
||||
lla a2, %hi(a_symbol) # CHECK: :[[@LINE]]:9: error: operand must be a bare symbol name
|
|
@ -0,0 +1,28 @@
|
|||
# RUN: llvm-mc %s -triple=riscv32 | FileCheck %s
|
||||
# RUN: llvm-mc %s -triple=riscv64 | FileCheck %s
|
||||
|
||||
# CHECK: .Lpcrel_hi0:
|
||||
# CHECK: auipc a0, %pcrel_hi(a_symbol)
|
||||
# CHECK: addi a0, a0, %pcrel_lo(.Lpcrel_hi0)
|
||||
lla a0, a_symbol
|
||||
|
||||
# CHECK: .Lpcrel_hi1:
|
||||
# CHECK: auipc a1, %pcrel_hi(another_symbol)
|
||||
# CHECK: addi a1, a1, %pcrel_lo(.Lpcrel_hi1)
|
||||
lla a1, another_symbol
|
||||
|
||||
# Check that we can load the address of symbols that are spelled like a register
|
||||
# CHECK: .Lpcrel_hi2:
|
||||
# CHECK: auipc a2, %pcrel_hi(zero)
|
||||
# CHECK: addi a2, a2, %pcrel_lo(.Lpcrel_hi2)
|
||||
lla a2, zero
|
||||
|
||||
# CHECK: .Lpcrel_hi3:
|
||||
# CHECK: auipc a3, %pcrel_hi(ra)
|
||||
# CHECK: addi a3, a3, %pcrel_lo(.Lpcrel_hi3)
|
||||
lla a3, ra
|
||||
|
||||
# CHECK: .Lpcrel_hi4:
|
||||
# CHECK: auipc a4, %pcrel_hi(f1)
|
||||
# CHECK: addi a4, a4, %pcrel_lo(.Lpcrel_hi4)
|
||||
lla a4, f1
|
Loading…
Reference in New Issue