Index: llvm/trunk/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ llvm/trunk/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -171,6 +171,16 @@ // Predicate methods for AsmOperands defined in RISCVInstrInfo.td + bool isBareSymbol() const { + int64_t Imm; + RISCVMCExpr::VariantKind VK; + // Must be of 'immediate' type but not a constant. + if (!isImm() || evaluateConstantImm(Imm, VK)) + return false; + return RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm) && + VK == RISCVMCExpr::VK_RISCV_None; + } + /// Return true if the operand is a valid for the fence instruction e.g. /// ('iorw'). bool isFenceArg() const { @@ -703,6 +713,10 @@ ErrorLoc, "operand must be a valid floating point rounding mode mnemonic"); } + case Match_InvalidBareSymbol: { + SMLoc ErrorLoc = ((RISCVOperand &)*Operands[ErrorInfo]).getStartLoc(); + return Error(ErrorLoc, "operand must be a bare symbol name"); + } } llvm_unreachable("Unknown match type detected!"); Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -23,6 +23,15 @@ ~RISCVELFObjectWriter() override; + // Return true if the given relocation must be with a symbol rather than + // section plus offset. + bool needsRelocateWithSymbol(const MCSymbol &Sym, + unsigned Type) const override { + // TODO: this is very conservative, update once RISC-V psABI requirements + // are clarified. + return true; + } + protected: unsigned getRelocType(MCContext &Ctx, const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const override; @@ -67,6 +76,8 @@ return ELF::R_RISCV_RVC_JUMP; case RISCV::fixup_riscv_rvc_branch: return ELF::R_RISCV_RVC_BRANCH; + case RISCV::fixup_riscv_call: + return ELF::R_RISCV_CALL; } } Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -47,6 +47,9 @@ // fixup_riscv_rvc_branch - 8-bit fixup for symbol references in the // compressed branch instruction fixup_riscv_rvc_branch, + // fixup_riscv_call - A fixup representing a call attached to the auipc + // instruction in a pair composed of adjacent auipc+jalr instructions. + fixup_riscv_call, // fixup_riscv_invalid - used as a sentinel and a marker, must be last fixup fixup_riscv_invalid, Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -21,6 +21,7 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSymbol.h" @@ -52,6 +53,10 @@ SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const override; + void expandFunctionCall(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const; + /// TableGen'erated function for getting the binary encoding for an /// instruction. uint64_t getBinaryCodeForInstr(const MCInst &MI, @@ -80,6 +85,42 @@ return new RISCVMCCodeEmitter(Ctx, MCII); } +// Expand PseudoCALL to AUIPC and JALR with relocation types. +// We expand PseudoCALL while encoding, meaning AUIPC and JALR won't go through +// RISCV MC to MC compressed instruction transformation. This is acceptable +// because AUIPC has no 16-bit form and C_JALR have no immediate operand field. +// We let linker relaxation deal with it. When linker relaxation enabled, +// AUIPC and JALR have chance relax to JAL. If C extension is enabled, +// JAL has chance relax to C_JAL. +void RISCVMCCodeEmitter::expandFunctionCall(const MCInst &MI, raw_ostream &OS, + SmallVectorImpl &Fixups, + const MCSubtargetInfo &STI) const { + MCInst TmpInst; + MCOperand Func = MI.getOperand(0); + unsigned Ra = RISCV::X1; + uint32_t Binary; + + assert(Func.isExpr() && "Expected expression"); + + const MCExpr *Expr = Func.getExpr(); + + // Create function call expression CallExpr for AUIPC. + const MCExpr *CallExpr = + RISCVMCExpr::create(Expr, RISCVMCExpr::VK_RISCV_CALL, Ctx); + + // Emit AUIPC Ra, Func with R_RISCV_CALL relocation type. + TmpInst = MCInstBuilder(RISCV::AUIPC) + .addReg(Ra) + .addOperand(MCOperand::createExpr(CallExpr)); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::Writer(OS).write(Binary); + + // Emit JALR Ra, Ra, 0 + TmpInst = MCInstBuilder(RISCV::JALR).addReg(Ra).addReg(Ra).addImm(0); + Binary = getBinaryCodeForInstr(TmpInst, Fixups, STI); + support::endian::Writer(OS).write(Binary); +} + void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const { @@ -87,6 +128,12 @@ // Get byte count of instruction. unsigned Size = Desc.getSize(); + if (MI.getOpcode() == RISCV::PseudoCALL) { + expandFunctionCall(MI, OS, Fixups, STI); + MCNumEmitted += 2; + return; + } + switch (Size) { default: llvm_unreachable("Unhandled encodeInstruction length!"); @@ -183,6 +230,9 @@ case RISCVMCExpr::VK_RISCV_PCREL_HI: FixupKind = RISCV::fixup_riscv_pcrel_hi20; break; + case RISCVMCExpr::VK_RISCV_CALL: + FixupKind = RISCV::fixup_riscv_call; + break; } } else if (Kind == MCExpr::SymbolRef && cast(Expr)->getKind() == MCSymbolRefExpr::VK_None) { Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -29,6 +29,7 @@ VK_RISCV_HI, VK_RISCV_PCREL_LO, VK_RISCV_PCREL_HI, + VK_RISCV_CALL, VK_RISCV_Invalid }; Index: llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp =================================================================== --- llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp +++ llvm/trunk/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -32,7 +32,8 @@ } void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { - bool HasVariant = getKind() != VK_RISCV_None; + bool HasVariant = + ((getKind() != VK_RISCV_None) && (getKind() != VK_RISCV_CALL)); if (HasVariant) OS << '%' << getVariantKindName(getKind()) << '('; Expr->print(OS, MAI); @@ -77,7 +78,8 @@ bool RISCVMCExpr::evaluateAsConstant(int64_t &Res) const { MCValue Value; - if (Kind == VK_RISCV_PCREL_HI || Kind == VK_RISCV_PCREL_LO) + if (Kind == VK_RISCV_PCREL_HI || Kind == VK_RISCV_PCREL_LO || + Kind == VK_RISCV_CALL) return false; if (!getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr)) Index: llvm/trunk/lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- llvm/trunk/lib/Target/RISCV/RISCVInstrInfo.td +++ llvm/trunk/lib/Target/RISCV/RISCVInstrInfo.td @@ -153,6 +153,20 @@ }]; } +def BareSymbol : AsmOperandClass { + let Name = "BareSymbol"; + let RenderMethod = "addImmOperands"; + let DiagnosticType = "InvalidBareSymbol"; +} + +// A bare symbol. +def bare_symbol : Operand { + let ParserMatchClass = BareSymbol; + let MCOperandPredicate = [{ + return MCOp.isBareSymbolRef(); + }]; +} + // A parameterized register class alternative to i32imm/i64imm from Target.td. def ixlenimm : Operand; @@ -621,9 +635,20 @@ def : Pat<(brind (add GPR:$rs1, simm12:$imm12)), (PseudoBRIND GPR:$rs1, simm12:$imm12)>; +// PseudoCALL is a pseudo instruction which will eventually expand to auipc +// and jalr. Define AsmString because we want assembler could print "call" +// when compile with -S. Define isCodeGenOnly = 0 because we want parser +// could parsing assembly "call" instruction. +let isCall = 1, Defs = [X1], isCodeGenOnly = 0, + hasSideEffects = 0, mayLoad = 0, mayStore = 0 in +def PseudoCALL : Pseudo<(outs), (ins bare_symbol:$func), + []> { + let AsmString = "call\t$func"; +} + let isCall = 1, Defs = [X1] in -def PseudoCALL : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>, - PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>; +def PseudoCALLIndirect : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>, + PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>; let isBarrier = 1, isReturn = 1, isTerminator = 1 in def PseudoRET : Pseudo<(outs), (ins), [(RetFlag)]>, Index: llvm/trunk/test/MC/RISCV/function-call-invalid.s =================================================================== --- llvm/trunk/test/MC/RISCV/function-call-invalid.s +++ llvm/trunk/test/MC/RISCV/function-call-invalid.s @@ -0,0 +1,11 @@ +# RUN: not llvm-mc -triple riscv32 < %s 2>&1 | FileCheck %s + +call 1234 # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %pcrel_hi(1234) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %pcrel_lo(1234) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %pcrel_hi(foo) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %pcrel_lo(foo) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %hi(1234) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %lo(1234) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %hi(foo) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name +call %lo(foo) # CHECK: :[[@LINE]]:6: error: operand must be a bare symbol name Index: llvm/trunk/test/MC/RISCV/function-call.s =================================================================== --- llvm/trunk/test/MC/RISCV/function-call.s +++ llvm/trunk/test/MC/RISCV/function-call.s @@ -0,0 +1,19 @@ +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-objdump -d - | FileCheck -check-prefix=INSTR %s +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELOC %s +# RUN: llvm-mc -triple riscv32 < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=FIXUP %s + +.long foo + +call foo +# RELOC: R_RISCV_CALL foo 0x0 +# INSTR: auipc ra, 0 +# INSTR: jalr ra +# FIXUP: fixup A - offset: 0, value: foo, kind: +call bar +# RELOC: R_RISCV_CALL bar 0x0 +# INSTR: auipc ra, 0 +# INSTR: jalr ra +# FIXUP: fixup A - offset: 0, value: bar, kind: