Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -73,9 +73,22 @@ // synthesize the desired immedate value into the destination register. void emitLoadImm(unsigned DestReg, int64_t Value, MCStreamer &Out); + // Helper to emit load symbol address. + const MCExpr *emitLoadSymbolAddressHi(const MCExpr *Symbol, + const MCOperand &TmpReg, + 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 lb/lbu/lh/lhu/lw/lwu/ld + // that does a load with symbol. + void emitLoadSymbol(MCInst &Inst, SMLoc IDLoc, MCStreamer &Out); + + // Helper to emit pseudo instruction sb/sh/sw/sd + // that does a store with symbol. + void emitStoreSymbol(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 @@ -980,8 +993,30 @@ // tail imm return Name == "tail" || Name == "call"; case 1: - // lla rdest, imm - return Name == "lla"; + // l[bhw][u] rdest, imm + // ld rdest, imm + // fl[wd] rdest, imm + // s[bhw] rdest, imm, tmp + // fs[wd] rdest, imm, tmp + // lla imm + return StringSwitch(Name) + .Case("lb", true) + .Case("lbu", true) + .Case("lh", true) + .Case("lhu", true) + .Case("lw", true) + .Case("lwu", true) + .Case("ld", true) + .Case("flw", true) + .Case("fld", true) + .Case("sb", true) + .Case("sh", true) + .Case("sw", true) + .Case("sd", true) + .Case("fsw", true) + .Case("fsd", true) + .Case("lla", true) + .Default(false); default: return false; } @@ -1209,42 +1244,131 @@ .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) +/// Emit top half of the load symbol address code sequence. +/// and return the reference of TmpLabel to the symbol address. +/// +/// This function will emit a TmpLabel and an auipc instruction: +/// TmpLabel: AUIPC rdest, %pcrel_hi(symbol) +const MCExpr *RISCVAsmParser::emitLoadSymbolAddressHi(const MCExpr *Symbol, + const MCOperand &TmpReg, + MCStreamer &Out) { 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 *SymbolExpr = RISCVMCExpr::create( + Symbol, RISCVMCExpr::VK_RISCV_PCREL_HI, Ctx); emitToStreamer( - Out, MCInstBuilder(RISCV::AUIPC).addOperand(DestReg).addExpr(Symbol)); + Out, MCInstBuilder(RISCV::AUIPC).addOperand(TmpReg).addExpr(SymbolExpr)); const MCExpr *RefToLinkTmpLabel = RISCVMCExpr::create(MCSymbolRefExpr::create(TmpLabel, Ctx), RISCVMCExpr::VK_RISCV_PCREL_LO, Ctx); + return RefToLinkTmpLabel; +} + +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) + MCOperand DestReg = Inst.getOperand(0); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + + const MCExpr *RefToLinkTmpLabel = + emitLoadSymbolAddressHi(Symbol, DestReg, Out); + emitToStreamer(Out, MCInstBuilder(RISCV::ADDI) .addOperand(DestReg) .addOperand(DestReg) .addExpr(RefToLinkTmpLabel)); } +void RISCVAsmParser::emitLoadSymbol(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // The load pseudo-instruction does a pc-relative load with + // a symbol. + // + // The expansion looks like this + // + // TmpLabel: AUIPC rd, %pcrel_hi(symbol) + // LX rd, %pcrel_lo(TmpLabel)(rd) + MCOperand DestReg = Inst.getOperand(0); + const MCExpr *Symbol = Inst.getOperand(1).getExpr(); + + const MCExpr *RefToLinkTmpLabel = + emitLoadSymbolAddressHi(Symbol, DestReg, Out); + + unsigned Opcode; + switch (Inst.getOpcode()) { + default: + llvm_unreachable("Unexpected opcode!"); + case RISCV::PseudoLB: Opcode = RISCV::LB; break; + case RISCV::PseudoLBU: Opcode = RISCV::LBU; break; + case RISCV::PseudoLH: Opcode = RISCV::LH; break; + case RISCV::PseudoLHU: Opcode = RISCV::LHU; break; + case RISCV::PseudoLW: Opcode = RISCV::LW; break; + case RISCV::PseudoLWU: Opcode = RISCV::LWU; break; + case RISCV::PseudoLD: Opcode = RISCV::LD; break; + case RISCV::PseudoFLW: Opcode = RISCV::FLW; break; + case RISCV::PseudoFLD: Opcode = RISCV::FLD; break; + } + + emitToStreamer(Out, MCInstBuilder(Opcode) + .addOperand(DestReg) + .addOperand(DestReg) + .addExpr(RefToLinkTmpLabel)); +} + +void RISCVAsmParser::emitStoreSymbol(MCInst &Inst, SMLoc IDLoc, + MCStreamer &Out) { + // The store pseudo-instruction does a pc-relative store with + // a symbol. + // + // The expansion looks like this + // + // TmpLabel: AUIPC tmp, %pcrel_hi(symbol) + // SX rd, %pcrel_lo(TmpLabel)(tmp) + MCOperand DestReg = Inst.getOperand(0); + MCOperand TmpReg = Inst.getOperand(1); + const MCExpr *Symbol = Inst.getOperand(2).getExpr(); + + const MCExpr *RefToLinkTmpLabel = + emitLoadSymbolAddressHi(Symbol, TmpReg, Out); + + unsigned Opcode; + switch (Inst.getOpcode()) { + default: + llvm_unreachable("Unexpected opcode!"); + case RISCV::PseudoSB: Opcode = RISCV::SB; break; + case RISCV::PseudoSH: Opcode = RISCV::SH; break; + case RISCV::PseudoSW: Opcode = RISCV::SW; break; + case RISCV::PseudoSD: Opcode = RISCV::SD; break; + case RISCV::PseudoFSW: Opcode = RISCV::FSW; break; + case RISCV::PseudoFSD: Opcode = RISCV::FSD; break; + } + + emitToStreamer(Out, MCInstBuilder(Opcode) + .addOperand(DestReg) + .addOperand(TmpReg) + .addExpr(RefToLinkTmpLabel)); +} + 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: { auto Reg = Inst.getOperand(0).getReg(); int64_t Imm = Inst.getOperand(1).getImm(); // On RV32 the immediate here can either be a signed or an unsigned @@ -1254,9 +1378,29 @@ 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::PseudoLB: + case RISCV::PseudoLBU: + case RISCV::PseudoLH: + case RISCV::PseudoLHU: + case RISCV::PseudoLW: + case RISCV::PseudoLWU: + case RISCV::PseudoLD: + case RISCV::PseudoFLW: + case RISCV::PseudoFLD: + emitLoadSymbol(Inst, IDLoc, Out); + return false; + case RISCV::PseudoSB: + case RISCV::PseudoSH: + case RISCV::PseudoSW: + case RISCV::PseudoSD: + case RISCV::PseudoFSW: + case RISCV::PseudoFSD: + emitStoreSymbol(Inst, IDLoc, Out); + return false; } emitToStreamer(Out, Inst); Index: lib/Target/RISCV/RISCVInstrFormats.td =================================================================== --- lib/Target/RISCV/RISCVInstrFormats.td +++ lib/Target/RISCV/RISCVInstrFormats.td @@ -108,6 +108,26 @@ let isCodeGenOnly = 1; } +// Pseudo load instructions. +class PseudoLOAD + : Pseudo<(outs GPR:$rd), (ins bare_symbol:$addr), [], opcodestr, "$rd, $addr"> { + let hasSideEffects = 0; + let mayLoad = 1; + let mayStore = 0; + let isCodeGenOnly = 0; + let isAsmParserOnly = 1; +} + +// Pseudo store instructions. +class PseudoSTORE + : Pseudo<(outs GPR:$rs, GPR:$tmp), (ins bare_symbol:$addr), [], opcodestr, "$rs, $addr, $tmp"> { + let hasSideEffects = 0; + let mayLoad = 0; + let mayStore = 1; + let isCodeGenOnly = 0; + let isAsmParserOnly = 1; +} + // Instruction formats are listed in the order they appear in the RISC-V // instruction set manual (R, I, S, B, U, J) with sub-formats (e.g. RVInstR4, // RVInstRAtomic) sorted alphabetically. Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -11,8 +11,6 @@ // //===----------------------------------------------------------------------===// -include "RISCVInstrFormats.td" - //===----------------------------------------------------------------------===// // RISC-V specific DAG Nodes. //===----------------------------------------------------------------------===// @@ -211,6 +209,12 @@ }]>; //===----------------------------------------------------------------------===// +// Instruction Formats +//===----------------------------------------------------------------------===// + +include "RISCVInstrFormats.td" + +//===----------------------------------------------------------------------===// // Instruction Class Templates //===----------------------------------------------------------------------===// @@ -454,10 +458,6 @@ //===----------------------------------------------------------------------===// // TODO la -// TODO lb lh lw -// TODO RV64I: ld -// TODO sb sh sw -// TODO RV64I: sd def : InstAlias<"nop", (ADDI X0, X0, 0)>; @@ -470,6 +470,22 @@ def PseudoLI : Pseudo<(outs GPR:$rd), (ins ixlenimm:$imm), [], "li", "$rd, $imm">; +def PseudoLB : PseudoLOAD<"lb">; +def PseudoLBU : PseudoLOAD<"lbu">; +def PseudoLH : PseudoLOAD<"lh">; +def PseudoLHU : PseudoLOAD<"lhu">; +def PseudoLW : PseudoLOAD<"lw">; + +def PseudoSB : PseudoSTORE<"sb">; +def PseudoSH : PseudoSTORE<"sh">; +def PseudoSW : PseudoSTORE<"sw">; + +let Predicates = [IsRV64] in { +def PseudoLWU : PseudoLOAD<"lwu">; +def PseudoLD : PseudoLOAD<"ld">; +def PseudoSD : PseudoSTORE<"sd">; +} // Predicates = [IsRV64] + def : InstAlias<"mv $rd, $rs", (ADDI GPR:$rd, GPR:$rs, 0)>; def : InstAlias<"not $rd, $rs", (XORI GPR:$rd, GPR:$rs, -1)>; def : InstAlias<"neg $rd, $rs", (SUB GPR:$rd, X0, GPR:$rs)>; Index: lib/Target/RISCV/RISCVInstrInfoD.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfoD.td +++ lib/Target/RISCV/RISCVInstrInfoD.td @@ -179,9 +179,6 @@ //===----------------------------------------------------------------------===// let Predicates = [HasStdExtD] in { -// TODO fld -// TODO fsd - def : InstAlias<"fmv.d $rd, $rs", (FSGNJ_D FPR64:$rd, FPR64:$rs, FPR64:$rs)>; def : InstAlias<"fabs.d $rd, $rs", (FSGNJX_D FPR64:$rd, FPR64:$rs, FPR64:$rs)>; def : InstAlias<"fneg.d $rd, $rs", (FSGNJN_D FPR64:$rd, FPR64:$rs, FPR64:$rs)>; @@ -192,6 +189,9 @@ (FLT_D GPR:$rd, FPR64:$rt, FPR64:$rs), 0>; def : InstAlias<"fge.d $rd, $rs, $rt", (FLE_D GPR:$rd, FPR64:$rt, FPR64:$rs), 0>; + +def PseudoFLD : PseudoLOAD<"fld">; +def PseudoFSD : PseudoSTORE<"fsd">; } // Predicates = [HasStdExtD] //===----------------------------------------------------------------------===// Index: lib/Target/RISCV/RISCVInstrInfoF.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfoF.td +++ lib/Target/RISCV/RISCVInstrInfoF.td @@ -193,9 +193,6 @@ //===----------------------------------------------------------------------===// let Predicates = [HasStdExtF] in { -// TODO flw -// TODO fsw - def : InstAlias<"fmv.s $rd, $rs", (FSGNJ_S FPR32:$rd, FPR32:$rs, FPR32:$rs)>; def : InstAlias<"fabs.s $rd, $rs", (FSGNJX_S FPR32:$rd, FPR32:$rs, FPR32:$rs)>; def : InstAlias<"fneg.s $rd, $rs", (FSGNJN_S FPR32:$rd, FPR32:$rs, FPR32:$rs)>; @@ -231,6 +228,9 @@ // spellings should be supported by standard tools. def : MnemonicAlias<"fmv.s.x", "fmv.w.x">; def : MnemonicAlias<"fmv.x.s", "fmv.x.w">; + +def PseudoFLW : PseudoLOAD<"flw">; +def PseudoFSW : PseudoSTORE<"fsw">; } // Predicates = [HasStdExtF] //===----------------------------------------------------------------------===// Index: test/MC/RISCV/rv32d-invalid.s =================================================================== --- test/MC/RISCV/rv32d-invalid.s +++ test/MC/RISCV/rv32d-invalid.s @@ -6,8 +6,8 @@ fsd ft2, 2048(a1) # CHECK: :[[@LINE]]:10: error: immediate must be an integer in the range [-2048, 2047] # Memory operand not formatted correctly -fld ft1, a0, -200 # CHECK: :[[@LINE]]:10: error: immediate must be an integer in the range [-2048, 2047] -fsd ft2, a1, 100 # CHECK: :[[@LINE]]:10: error: immediate must be an integer in the range [-2048, 2047] +fld ft1, a0, -200 # CHECK: :[[@LINE]]:14: error: invalid operand for instruction +fsd ft2, a1, 100 # CHECK: :[[@LINE]]:14: error: invalid operand for instruction # Invalid register names fld ft15, 100(a0) # CHECK: :[[@LINE]]:5: error: invalid operand for instruction Index: test/MC/RISCV/rv32f-invalid.s =================================================================== --- test/MC/RISCV/rv32f-invalid.s +++ test/MC/RISCV/rv32f-invalid.s @@ -6,8 +6,8 @@ fsw ft2, 2048(a1) # CHECK: :[[@LINE]]:10: error: immediate must be an integer in the range [-2048, 2047] # Memory operand not formatted correctly -flw ft1, a0, -200 # CHECK: :[[@LINE]]:10: error: immediate must be an integer in the range [-2048, 2047] -fsw ft2, a1, 100 # CHECK: :[[@LINE]]:10: error: immediate must be an integer in the range [-2048, 2047] +flw ft1, a0, -200 # CHECK: :[[@LINE]]:14: error: invalid operand for instruction +fsw ft2, a1, 100 # CHECK: :[[@LINE]]:14: error: invalid operand for instruction # Invalid register names flw ft15, 100(a0) # CHECK: :[[@LINE]]:5: error: invalid operand for instruction Index: test/MC/RISCV/rv32i-invalid.s =================================================================== --- test/MC/RISCV/rv32i-invalid.s +++ test/MC/RISCV/rv32i-invalid.s @@ -133,7 +133,7 @@ sltiu s2, s3, 0x50, 0x60 # CHECK: :[[@LINE]]:21: error: invalid operand for instruction # Memory operand not formatted correctly -lw a4, a5, 111 # CHECK: :[[@LINE]]:8: error: immediate must be an integer in the range [-2048, 2047] +lw a4, a5, 111 # CHECK: :[[@LINE]]:12: error: invalid operand for instruction # Too few operands ori a0, a1 # CHECK: :[[@LINE]]:1: error: too few operands for instruction Index: test/MC/RISCV/rv64i-pseudos.s =================================================================== --- /dev/null +++ test/MC/RISCV/rv64i-pseudos.s @@ -0,0 +1,16 @@ +# RUN: llvm-mc %s -triple=riscv64 | FileCheck %s + +# CHECK: .Lpcrel_hi0: +# CHECK: auipc a2, %pcrel_hi(a_symbol) +# CHECK: lwu a2, %pcrel_lo(.Lpcrel_hi0)(a2) +lwu a2, a_symbol + +# CHECK: .Lpcrel_hi1: +# CHECK: auipc a3, %pcrel_hi(a_symbol) +# CHECK: ld a3, %pcrel_lo(.Lpcrel_hi1)(a3) +ld a3, a_symbol + +# CHECK: .Lpcrel_hi2: +# CHECK: auipc a4, %pcrel_hi(a_symbol) +# CHECK: sd a3, %pcrel_lo(.Lpcrel_hi2)(a4) +sd a3, a_symbol, a4 Index: test/MC/RISCV/rvd-pseudos.s =================================================================== --- /dev/null +++ test/MC/RISCV/rvd-pseudos.s @@ -0,0 +1,12 @@ +# RUN: llvm-mc %s -triple=riscv32 -mattr=+d | FileCheck %s +# RUN: llvm-mc %s -triple=riscv64 -mattr=+d | FileCheck %s + +# CHECK: .Lpcrel_hi0: +# CHECK: auipc a2, %pcrel_hi(a_symbol) +# CHECK: fld a2, %pcrel_lo(.Lpcrel_hi0)(a2) +fld a2, a_symbol + +# CHECK: .Lpcrel_hi1: +# CHECK: auipc a3, %pcrel_hi(a_symbol) +# CHECK: fsd a2, %pcrel_lo(.Lpcrel_hi1)(a3) +fsd a2, a_symbol, a3 Index: test/MC/RISCV/rvf-pseudos.s =================================================================== --- /dev/null +++ test/MC/RISCV/rvf-pseudos.s @@ -0,0 +1,12 @@ +# RUN: llvm-mc %s -triple=riscv32 -mattr=+f | FileCheck %s +# RUN: llvm-mc %s -triple=riscv64 -mattr=+f | FileCheck %s + +# CHECK: .Lpcrel_hi0: +# CHECK: auipc a2, %pcrel_hi(a_symbol) +# CHECK: flw a2, %pcrel_lo(.Lpcrel_hi0)(a2) +flw a2, a_symbol + +# CHECK: .Lpcrel_hi1: +# CHECK: auipc a3, %pcrel_hi(a_symbol) +# CHECK: fsw a2, %pcrel_lo(.Lpcrel_hi1)(a3) +fsw a2, a_symbol, a3 Index: test/MC/RISCV/rvi-pseudos-invalid.s =================================================================== --- /dev/null +++ test/MC/RISCV/rvi-pseudos-invalid.s @@ -0,0 +1,10 @@ +# 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 +sw a2, %hi(a_symbol), a3 # CHECK: :[[@LINE]]:8: error: immediate must be an integer in the range [-2048, 2047] +sw a2, %lo(a_symbol), a3 # CHECK: :[[@LINE]]:23: error: invalid operand for instruction +sw a2, %lo(a_symbol)(a4), a3 # CHECK: :[[@LINE]]:27: error: invalid operand for instruction + +# Too fwe operands must be rejected +sw a2, a_symbol # CHECK: :[[@LINE]]:1: error: too few operands for instruction Index: test/MC/RISCV/rvi-pseudos.s =================================================================== --- test/MC/RISCV/rvi-pseudos.s +++ test/MC/RISCV/rvi-pseudos.s @@ -26,3 +26,49 @@ # CHECK: auipc a4, %pcrel_hi(f1) # CHECK: addi a4, a4, %pcrel_lo(.Lpcrel_hi4) lla a4, f1 + +# CHECK: .Lpcrel_hi5: +# CHECK: auipc a0, %pcrel_hi(a_symbol) +# CHECK: lb a0, %pcrel_lo(.Lpcrel_hi5)(a0) +lb a0, a_symbol + +# CHECK: .Lpcrel_hi6: +# CHECK: auipc a1, %pcrel_hi(a_symbol) +# CHECK: lh a1, %pcrel_lo(.Lpcrel_hi6)(a1) +lh a1, a_symbol + +# CHECK: .Lpcrel_hi7: +# CHECK: auipc a2, %pcrel_hi(a_symbol) +# CHECK: lhu a2, %pcrel_lo(.Lpcrel_hi7)(a2) +lhu a2, a_symbol + +# CHECK: .Lpcrel_hi8: +# CHECK: auipc a3, %pcrel_hi(a_symbol) +# CHECK: lw a3, %pcrel_lo(.Lpcrel_hi8)(a3) +lw a3, a_symbol + +# CHECK: .Lpcrel_hi9: +# CHECK: auipc a4, %pcrel_hi(a_symbol) +# CHECK: sb a3, %pcrel_lo(.Lpcrel_hi9)(a4) +sb a3, a_symbol, a4 + +# CHECK: .Lpcrel_hi10: +# CHECK: auipc a4, %pcrel_hi(a_symbol) +# CHECK: sh a3, %pcrel_lo(.Lpcrel_hi10)(a4) +sh a3, a_symbol, a4 + +# CHECK: .Lpcrel_hi11: +# CHECK: auipc a4, %pcrel_hi(a_symbol) +# CHECK: sw a3, %pcrel_lo(.Lpcrel_hi11)(a4) +sw a3, a_symbol, a4 + +# Check that we can load the address of symbols that are spelled like a register +# CHECK: .Lpcrel_hi12: +# CHECK: auipc a2, %pcrel_hi(zero) +# CHECK: lw a2, %pcrel_lo(.Lpcrel_hi12)(a2) +lw a2, zero + +# CHECK: .Lpcrel_hi13: +# CHECK: auipc a4, %pcrel_hi(zero) +# CHECK: sw a3, %pcrel_lo(.Lpcrel_hi13)(a4) +sw a3, zero, a4