Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -73,6 +73,9 @@ // 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 @@ 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 @@ 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 @@ .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 @@ 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); Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -720,6 +720,11 @@ 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 { Index: test/MC/RISCV/lla-invalid.s =================================================================== --- /dev/null +++ test/MC/RISCV/lla-invalid.s @@ -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 Index: test/MC/RISCV/rvi-pseudos.s =================================================================== --- /dev/null +++ test/MC/RISCV/rvi-pseudos.s @@ -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