Skip to content

Commit

Permalink
[RISCV] Support "call" pseudoinstruction in the MC layer
Browse files Browse the repository at this point in the history
To do this:
1. Add PseudoCALLIndirct to match indirect function call.

2. Add PseudoCALL to support parsing and print pseudo `call` in assembly

3. Expand PseudoCALL to the following form with R_RISCV_CALL relocation type
   while encoding:
        auipc ra, func
        jalr ra, ra, 0

If we expand PseudoCALL before emitting assembly, we will see auipc and jalr
pair when compile with -S. It's hard for assembly parser to parsing this
pair and identify it's semantic is function call and then insert R_RISCV_CALL
relocation type. Although we could insert R_RISCV_PCREL_HI20 and
R_RISCV_PCREL_LO12_I relocation types instead of R_RISCV_CALL.
Due to RISCV relocation design, auipc and jalr pair only can relax to jal with
R_RISCV_CALL + R_RISCV_RELAX relocation types.

We expand PseudoCALL as late as encoding(RISCVMCCodeEmitter) instead of before
emitting assembly(RISCVAsmPrinter) because we want to preserve call
pseudoinstruction in assembly code. It's more readable and assembly parser
could identify call assembly and insert R_RISCV_CALL relocation type.

Differential Revision: https://reviews.llvm.org/D45859

llvm-svn: 330826
  • Loading branch information
ShivaChen committed Apr 25, 2018
1 parent 0f2f597 commit 98f9389
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 4 deletions.
14 changes: 14 additions & 0 deletions llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
Expand Up @@ -171,6 +171,16 @@ struct RISCVOperand : public MCParsedAsmOperand {

// 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 {
Expand Down Expand Up @@ -703,6 +713,10 @@ bool RISCVAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode,
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!");
Expand Down
11 changes: 11 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
Expand Up @@ -23,6 +23,15 @@ class RISCVELFObjectWriter : public MCELFObjectTargetWriter {

~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;
Expand Down Expand Up @@ -67,6 +76,8 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
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;
}
}

Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
Expand Up @@ -47,6 +47,9 @@ enum Fixups {
// 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,
Expand Down
50 changes: 50 additions & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -52,6 +53,10 @@ class RISCVMCCodeEmitter : public MCCodeEmitter {
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const override;

void expandFunctionCall(const MCInst &MI, raw_ostream &OS,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;

/// TableGen'erated function for getting the binary encoding for an
/// instruction.
uint64_t getBinaryCodeForInstr(const MCInst &MI,
Expand Down Expand Up @@ -80,13 +85,55 @@ MCCodeEmitter *llvm::createRISCVMCCodeEmitter(const MCInstrInfo &MCII,
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<MCFixup> &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<support::little>(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<support::little>(OS).write(Binary);
}

void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const {
const MCInstrDesc &Desc = MCII.get(MI.getOpcode());
// 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!");
Expand Down Expand Up @@ -183,6 +230,9 @@ unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
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<MCSymbolRefExpr>(Expr)->getKind() == MCSymbolRefExpr::VK_None) {
Expand Down
6 changes: 4 additions & 2 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
Expand Up @@ -32,7 +32,8 @@ const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, VariantKind Kind,
}

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);
Expand Down Expand Up @@ -77,7 +78,8 @@ StringRef RISCVMCExpr::getVariantKindName(VariantKind Kind) {
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))
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
Expand Up @@ -29,6 +29,7 @@ class RISCVMCExpr : public MCTargetExpr {
VK_RISCV_HI,
VK_RISCV_PCREL_LO,
VK_RISCV_PCREL_HI,
VK_RISCV_CALL,
VK_RISCV_Invalid
};

Expand Down
29 changes: 27 additions & 2 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.td
Expand Up @@ -153,6 +153,20 @@ def simm21_lsb0 : Operand<OtherVT> {
}];
}

def BareSymbol : AsmOperandClass {
let Name = "BareSymbol";
let RenderMethod = "addImmOperands";
let DiagnosticType = "InvalidBareSymbol";
}

// A bare symbol.
def bare_symbol : Operand<XLenVT> {
let ParserMatchClass = BareSymbol;
let MCOperandPredicate = [{
return MCOp.isBareSymbolRef();
}];
}

// A parameterized register class alternative to i32imm/i64imm from Target.td.
def ixlenimm : Operand<XLenVT>;

Expand Down Expand Up @@ -621,9 +635,20 @@ def : Pat<(brind GPR:$rs1), (PseudoBRIND GPR:$rs1, 0)>;
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)]>,
Expand Down
11 changes: 11 additions & 0 deletions llvm/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
19 changes: 19 additions & 0 deletions llvm/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:

0 comments on commit 98f9389

Please sign in to comment.