summaryrefslogtreecommitdiff
path: root/lib/Target/RISCV
diff options
context:
space:
mode:
authorShiva Chen <shiva0217@gmail.com>2018-04-25 14:18:55 +0000
committerShiva Chen <shiva0217@gmail.com>2018-04-25 14:18:55 +0000
commit7b0e2e0edb14e1465b191c810b332f0743b66dda (patch)
tree48a78891ecea0ffdd6139120ef32c5035a850db8 /lib/Target/RISCV
parentd7511a6154f96ba7af699fbe5f13d453ef4c90d0 (diff)
[RISCV] Support "call" pseudoinstruction in the MC layer
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 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@330826 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Target/RISCV')
-rw-r--r--lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp14
-rw-r--r--lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp11
-rw-r--r--lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h3
-rw-r--r--lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp50
-rw-r--r--lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp6
-rw-r--r--lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h1
-rw-r--r--lib/Target/RISCV/RISCVInstrInfo.td29
7 files changed, 110 insertions, 4 deletions
diff --git a/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
index 521d72b546d..02f075a8532 100644
--- a/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
+++ b/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
@@ -171,6 +171,16 @@ public:
// 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 @@ 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!");
diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
index 0dc371c46f2..c0a005c9de1 100644
--- a/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
+++ b/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
@@ -23,6 +23,15 @@ public:
~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 @@ 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;
}
}
diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
index 604c3dc9921..7d9f8fc9b94 100644
--- a/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
+++ b/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
@@ -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,
diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
index a1ecb3e5937..58275faa163 100644
--- a/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
+++ b/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 @@ public:
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,
@@ -80,6 +85,42 @@ 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 {
@@ -87,6 +128,12 @@ void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS,
// 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 @@ 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) {
diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
index 4d1573ab8ef..844039f08f9 100644
--- a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
+++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
@@ -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);
@@ -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))
diff --git a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
index e428b0d30d3..d2e0f6b6cda 100644
--- a/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
+++ b/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
@@ -29,6 +29,7 @@ public:
VK_RISCV_HI,
VK_RISCV_PCREL_LO,
VK_RISCV_PCREL_HI,
+ VK_RISCV_CALL,
VK_RISCV_Invalid
};
diff --git a/lib/Target/RISCV/RISCVInstrInfo.td b/lib/Target/RISCV/RISCVInstrInfo.td
index 00f0342cb44..0309fe95000 100644
--- a/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/lib/Target/RISCV/RISCVInstrInfo.td
@@ -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>;
@@ -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)]>,