Index: lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp =================================================================== --- lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp +++ lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp @@ -9,6 +9,7 @@ #include "MCTargetDesc/RISCVBaseInfo.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "MCTargetDesc/RISCVMCExpr.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCParsedAsmOperand.h" #include "llvm/MC/MCParser/MCTargetAsmParser.h" @@ -53,6 +54,7 @@ OperandMatchResultTy parseImmediate(OperandVector &Operands); OperandMatchResultTy parseRegister(OperandVector &Operands); OperandMatchResultTy parseMemOpBaseReg(OperandVector &Operands); + OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands); bool parseOperand(OperandVector &Operands); @@ -64,6 +66,10 @@ #undef GET_OPERAND_DIAGNOSTIC_TYPES }; + static bool classifySymbolRef(const MCExpr *Expr, + RISCVMCExpr::VariantKind &Kind, + int64_t &Addend); + RISCVAsmParser(const MCSubtargetInfo &STI, MCAsmParser &Parser, const MCInstrInfo &MII, const MCTargetOptions &Options) : MCTargetAsmParser(Options, STI) { @@ -158,7 +164,16 @@ } bool isSImm12() const { - return (isConstantImm() && isInt<12>(getConstantImm())); + if (isConstantImm()) { + return isInt<12>(getConstantImm()); + } else if (isImm()) { + RISCVMCExpr::VariantKind VK; + int64_t Addend; + if (!RISCVAsmParser::classifySymbolRef(getImm(), VK, Addend)) + return false; + return VK == RISCVMCExpr::VK_RISCV_LO; + } + return false; } bool isUImm12() const { @@ -166,15 +181,38 @@ } bool isSImm13Lsb0() const { - return (isConstantImm() && isShiftedInt<12, 1>(getConstantImm())); + if (isConstantImm()) { + return isShiftedInt<12, 1>(getConstantImm()); + } else if (isImm()) { + RISCVMCExpr::VariantKind VK; + int64_t Addend; + return RISCVAsmParser::classifySymbolRef(getImm(), VK, Addend); + } + return false; } bool isUImm20() const { - return (isConstantImm() && isUInt<20>(getConstantImm())); + if (isConstantImm()) { + return isUInt<20>(getConstantImm()); + } else if (isImm()) { + RISCVMCExpr::VariantKind VK; + int64_t Addend; + if (!RISCVAsmParser::classifySymbolRef(getImm(), VK, Addend)) + return false; + return VK == RISCVMCExpr::VK_RISCV_HI || VK == RISCVMCExpr::VK_RISCV_PCREL_HI; + } + return false; } bool isSImm21Lsb0() const { - return (isConstantImm() && isShiftedInt<20, 1>(getConstantImm())); + if (isConstantImm()) { + return isShiftedInt<20, 1>(getConstantImm()); + } else if (isImm()) { + RISCVMCExpr::VariantKind VK; + int64_t Addend; + return RISCVAsmParser::classifySymbolRef(getImm(), VK, Addend); + } + return false; } /// getStartLoc - Gets location of the first token of this operand @@ -230,8 +268,13 @@ } static std::unique_ptr createImm(const MCExpr *Val, SMLoc S, - SMLoc E) { + SMLoc E, MCContext &Ctx) { auto Op = make_unique(Immediate); + if (const RISCVMCExpr *RE = dyn_cast(Val)) { + int64_t Res; + if (RE->evaluateAsConstant(Res)) + Val = MCConstantExpr::create(Res, Ctx); + } Op->Imm.Val = Val; Op->StartLoc = S; Op->EndLoc = E; @@ -407,9 +450,52 @@ Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext()); break; } + case AsmToken::Percent: + return parseOperandWithModifier(Operands); + break; } - Operands.push_back(RISCVOperand::createImm(Res, S, E)); + Operands.push_back(RISCVOperand::createImm(Res, S, E, getContext())); + return MatchOperand_Success; +} + +OperandMatchResultTy +RISCVAsmParser::parseOperandWithModifier(OperandVector &Operands) { + SMLoc S = getLoc(); + SMLoc E = SMLoc::getFromPointer(S.getPointer() - 1); + + if (getLexer().getKind() != AsmToken::Percent) { + Error(getLoc(), "expected '%' for operand modifier"); + return MatchOperand_ParseFail; + } + + getParser().Lex(); // Eat '%' + + if (getLexer().getKind() != AsmToken::Identifier) { + Error(getLoc(), "expected valid identifier for operand modifier"); + return MatchOperand_ParseFail; + } + StringRef Identifier = getParser().getTok().getString(); + RISCVMCExpr::VariantKind VK = RISCVMCExpr::getVariantKindForName(Identifier); + if (VK == RISCVMCExpr::VK_RISCV_None) { + Error(getLoc(), "unrecognized operand modifier"); + return MatchOperand_ParseFail; + } + + getParser().Lex(); // Eat the identifier + if (getLexer().getKind() != AsmToken::LParen) { + Error(getLoc(), "expected '('"); + return MatchOperand_ParseFail; + } + getParser().Lex(); // Eat '(' + + const MCExpr *SubExpr; + if (getParser().parseParenExpression(SubExpr, E)) { + return MatchOperand_ParseFail; + } + + const MCExpr *ModExpr = RISCVMCExpr::create(SubExpr, VK, getContext()); + Operands.push_back(RISCVOperand::createImm(ModExpr, S, E, getContext())); return MatchOperand_Success; } @@ -494,6 +580,54 @@ return false; } +bool RISCVAsmParser::classifySymbolRef(const MCExpr *Expr, + RISCVMCExpr::VariantKind &Kind, + int64_t &Addend) { + Kind = RISCVMCExpr::VK_RISCV_None; + Addend = 0; + + if (const RISCVMCExpr *RE = dyn_cast(Expr)) { + Kind = RE->getKind(); + Expr = RE->getSubExpr(); + } + + assert(!isa(Expr) && + "simple constants should have been evaluated already"); + + // It's a simple symbol reference with no addend. + if (isa(Expr)) + return true; + + const MCBinaryExpr *BE = dyn_cast(Expr); + if (!BE) + return false; + + if (!isa(BE->getLHS())) + return false; + + if (BE->getOpcode() != MCBinaryExpr::Add && + BE->getOpcode() != MCBinaryExpr::Sub) + return false; + + // We are able to support the subtraction of two symbol references + if (BE->getOpcode() == MCBinaryExpr::Sub && + isa(BE->getRHS())) + return true; + + // See if the addend is is a constant, otherwise there's more going + // on here than we can deal with. + auto AddendExpr = dyn_cast(BE->getRHS()); + if (!AddendExpr) + return false; + + Addend = AddendExpr->getValue(); + if (BE->getOpcode() == MCBinaryExpr::Sub) + Addend = -Addend; + + // It's some symbol reference + a constant addend + return Kind != RISCVMCExpr::VK_RISCV_Invalid; +} + bool RISCVAsmParser::ParseDirective(AsmToken DirectiveID) { return true; } extern "C" void LLVMInitializeRISCVAsmParser() { Index: lib/Target/RISCV/MCTargetDesc/CMakeLists.txt =================================================================== --- lib/Target/RISCV/MCTargetDesc/CMakeLists.txt +++ lib/Target/RISCV/MCTargetDesc/CMakeLists.txt @@ -2,6 +2,7 @@ RISCVAsmBackend.cpp RISCVELFObjectWriter.cpp RISCVMCAsmInfo.cpp - RISCVMCTargetDesc.cpp RISCVMCCodeEmitter.cpp + RISCVMCExpr.cpp + RISCVMCTargetDesc.cpp ) Index: lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp @@ -7,9 +7,12 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/RISCVFixupKinds.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" +#include "llvm/ADT/APInt.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCExpr.h" @@ -44,7 +47,32 @@ return false; } - unsigned getNumFixupKinds() const override { return 1; } + unsigned getNumFixupKinds() const override { + return RISCV::NumTargetFixupKinds; + } + + const MCFixupKindInfo &getFixupKindInfo(MCFixupKind Kind) const override { + const static MCFixupKindInfo Infos[RISCV::NumTargetFixupKinds] = { + // This table *must* be in the order that the fixup_* kinds are defined in + // RISCVFixupKinds.h. + // + // name offset bits flags + { "fixup_riscv_hi20", 12, 20, 0 }, + { "fixup_riscv_lo12_i", 20, 12, 0 }, + { "fixup_riscv_lo12_s", 0, 32, 0 }, + { "fixup_riscv_pcrel_hi20", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_jal", 12, 20, MCFixupKindInfo::FKF_IsPCRel }, + { "fixup_riscv_branch", 0, 32, MCFixupKindInfo::FKF_IsPCRel } + }; + + + if (Kind < FirstTargetFixupKind) + return MCAsmBackend::getFixupKindInfo(Kind); + + assert(unsigned(Kind - FirstTargetFixupKind) < getNumFixupKinds() && + "Invalid kind!"); + return Infos[Kind - FirstTargetFixupKind]; + } bool mayNeedRelaxation(const MCInst &Inst) const override { return false; } @@ -70,10 +98,89 @@ return true; } +static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value, + MCContext &Ctx) { + unsigned Kind = Fixup.getKind(); + switch (Kind) { + default: + llvm_unreachable("Unknown fixup kind!"); + case FK_Data_1: + case FK_Data_2: + case FK_Data_4: + case FK_Data_8: + return Value; + case RISCV::fixup_riscv_lo12_i: + return Value & 0xfff; + case RISCV::fixup_riscv_lo12_s: + return (((Value >> 5) & 0x7f) << 25) | ((Value & 0x1f) << 7); + case RISCV::fixup_riscv_hi20: + case RISCV::fixup_riscv_pcrel_hi20: + // Add 1 if bit 11 is 1, to compensate for low 12 bits being negative. + return ((Value + 0x800) >> 12) & 0xfffff; + case RISCV::fixup_riscv_jal: { + if (!isInt<21>(Value)) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + if (Value & 0x1) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned"); + // Need to produce imm[19|10:1|11|19:12] from the 21-bit Value. + unsigned Sbit = (Value >> 20) & 0x1; + unsigned Hi8 = (Value >> 12) & 0xff; + unsigned Mid1 = (Value >> 11) & 0x1; + unsigned Lo10 = (Value >> 1) & 0x3ff; + // Inst{31} = Sbit; + // Inst{30-21} = Lo10; + // Inst{20} = Mid1; + // Inst{19-12} = Hi8; + Value = (Sbit << 19) | (Lo10 << 9) | (Mid1 << 8) | Hi8; + return Value; + } + case RISCV::fixup_riscv_branch: { + if (!isInt<13>(Value)) + Ctx.reportError(Fixup.getLoc(), "fixup value out of range"); + if (Value & 0x1) + Ctx.reportError(Fixup.getLoc(), "fixup value must be 2-byte aligned"); + // Need to extract imm[12], imm[10:5], imm[4:1], imm[11] from the 13-bit + // Value. + unsigned Sbit = (Value >> 12) & 0x1; + unsigned Hi1 = (Value >> 11) & 0x1; + unsigned Mid6 = (Value >> 5) & 0x3f; + unsigned Lo4 = (Value >> 1) & 0xf; + // Inst{31} = Sbit; + // Inst{30-25} = Mid6; + // Inst{11-8} = Lo4; + // Inst{7} = Hi1; + Value = (Sbit << 31) | (Mid6 << 25) | (Lo4 << 8) | (Hi1 << 7); + return Value; + } + + } +} + void RISCVAsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup, const MCValue &Target, MutableArrayRef Data, uint64_t Value, bool IsResolved) const { + MCContext &Ctx = Asm.getContext(); + MCFixupKind Kind = Fixup.getKind(); + unsigned NumBytes = (getFixupKindInfo(Kind).TargetSize + 7) / 8; + if (!Value) + return; // Doesn't change encoding. + MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind()); + // Apply any target-specific value adjustments. + Value = adjustFixupValue(Fixup, Value, Ctx); + + // Shift the value into position. + Value <<= Info.TargetOffset; + + unsigned Offset = Fixup.getOffset(); + assert(Offset + NumBytes <= Data.size() && "Invalid fixup offset!"); + + // For each byte of the fragment that the fixup touches, mask in the + // bits from the fixup value. + for (unsigned i = 0; i != 4; ++i) { + unsigned Idx = i; + Data[Offset + i] |= uint8_t((Value >> (Idx * 8)) & 0xff); + } return; } Index: lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h +++ lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h @@ -32,6 +32,7 @@ FormMask = 15 }; + enum { MO_None, MO_LO, Index: lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/RISCVFixupKinds.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "llvm/MC/MCELFObjectWriter.h" #include "llvm/MC/MCFixup.h" @@ -37,7 +38,27 @@ const MCValue &Target, const MCFixup &Fixup, bool IsPCRel) const { - llvm_unreachable("invalid fixup kind!"); + // Determine the type of the relocation + switch ((unsigned)Fixup.getKind()) { + default: + llvm_unreachable("invalid fixup kind!"); + case FK_Data_4: + return ELF::R_RISCV_32; + case FK_Data_8: + return ELF::R_RISCV_64; + case RISCV::fixup_riscv_hi20: + return ELF::R_RISCV_HI20; + case RISCV::fixup_riscv_lo12_i: + return ELF::R_RISCV_LO12_I; + case RISCV::fixup_riscv_lo12_s: + return ELF::R_RISCV_LO12_S; + case RISCV::fixup_riscv_pcrel_hi20: + return ELF::R_RISCV_PCREL_HI20; + case RISCV::fixup_riscv_jal: + return ELF::R_RISCV_JAL; + case RISCV::fixup_riscv_branch: + return ELF::R_RISCV_BRANCH; + } } MCObjectWriter *llvm::createRISCVELFObjectWriter(raw_pwrite_stream &OS, Index: lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h =================================================================== --- /dev/null +++ lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h @@ -0,0 +1,46 @@ +//===-- RISCVFixupKinds.h - RISCV Specific Fixup Entries --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVFIXUPKINDS_H +#define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVFIXUPKINDS_H + +#include "llvm/MC/MCFixup.h" + +#undef RISCV + +namespace llvm { +namespace RISCV { +enum Fixups { + // fixup_riscv_hi20 - 20-bit fixup corresponding to hi(foo) for + // instructions like lui + fixup_riscv_hi20 = FirstTargetFixupKind, + // fixup_riscv_lo12_i - 12-bit fixup corresponding to lo(foo) for + // instructions like addi + fixup_riscv_lo12_i, + // fixup_riscv_lo12_s - 12-bit fixup corresponding to lo(foo) for + // the S-type store instructions + fixup_riscv_lo12_s, + // fixup_riscv_pcrel_hi20 - 20-bit fixup corresponding to pcrel_hi(foo) for + // instructions like auipc + fixup_riscv_pcrel_hi20, + // fixup_riscv_jal - 20-bit fixup for symbol references in the jal + // instruction + fixup_riscv_jal, + // fixup_riscv_branch - 12-bit fixup for symbol references in the branch + // instructions + fixup_riscv_branch, + + // Marker + LastTargetFixupKind, + NumTargetFixupKinds = LastTargetFixupKind - FirstTargetFixupKind +}; +} // end namespace RISCV +} // end namespace llvm + +#endif Index: lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp =================================================================== --- lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp +++ lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp @@ -11,6 +11,9 @@ // //===----------------------------------------------------------------------===// +#include "MCTargetDesc/RISCVBaseInfo.h" +#include "MCTargetDesc/RISCVFixupKinds.h" +#include "MCTargetDesc/RISCVMCExpr.h" #include "MCTargetDesc/RISCVMCTargetDesc.h" #include "llvm/ADT/Statistic.h" #include "llvm/MC/MCAsmInfo.h" @@ -18,8 +21,10 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSymbol.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/EndianStream.h" #include "llvm/Support/raw_ostream.h" @@ -28,15 +33,18 @@ #define DEBUG_TYPE "mccodeemitter" STATISTIC(MCNumEmitted, "Number of MC instructions emitted"); +STATISTIC(MCNumFixups, "Number of MC fixups created."); namespace { class RISCVMCCodeEmitter : public MCCodeEmitter { RISCVMCCodeEmitter(const RISCVMCCodeEmitter &) = delete; void operator=(const RISCVMCCodeEmitter &) = delete; MCContext &Ctx; + MCInstrInfo const &MCII; public: - RISCVMCCodeEmitter(MCContext &ctx) : Ctx(ctx) {} + RISCVMCCodeEmitter(MCContext &ctx, MCInstrInfo const &MCII) + : Ctx(ctx), MCII(MCII) {} ~RISCVMCCodeEmitter() override {} @@ -59,6 +67,7 @@ unsigned getImmOpValueAsr1(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; + unsigned getImmOpValue(const MCInst &MI, unsigned OpNo, SmallVectorImpl &Fixups, const MCSubtargetInfo &STI) const; @@ -68,7 +77,7 @@ MCCodeEmitter *llvm::createRISCVMCCodeEmitter(const MCInstrInfo &MCII, const MCRegisterInfo &MRI, MCContext &Ctx) { - return new RISCVMCCodeEmitter(Ctx); + return new RISCVMCCodeEmitter(Ctx, MCII); } void RISCVMCCodeEmitter::encodeInstruction(const MCInst &MI, raw_ostream &OS, @@ -107,9 +116,7 @@ return Res >> 1; } - llvm_unreachable("Unhandled expression!"); - - return 0; + return getImmOpValue(MI, OpNo, Fixups, STI); } unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo, @@ -118,11 +125,53 @@ const MCOperand &MO = MI.getOperand(OpNo); + MCInstrDesc const &Desc = MCII.get(MI.getOpcode()); + unsigned MIFrm = Desc.TSFlags & RISCVII::FormMask; + // If the destination is an immediate, there is nothing to do if (MO.isImm()) return MO.getImm(); - llvm_unreachable("Unhandled expression!"); + assert(MO.isExpr() && + "getImmOpValue expects only expressions or immediates"); + const MCExpr *Expr = MO.getExpr(); + MCExpr::ExprKind Kind = Expr->getKind(); + RISCV::Fixups FixupKind; + if (Kind == MCExpr::Target) { + const RISCVMCExpr *RVExpr = cast(Expr); + + switch (RVExpr->getKind()) { + case RISCVMCExpr::VK_RISCV_None: + case RISCVMCExpr::VK_RISCV_Invalid: + llvm_unreachable("Unhandled fixup kind!"); + break; + case RISCVMCExpr::VK_RISCV_LO: + FixupKind = MIFrm == RISCVII::FrmI ? RISCV::fixup_riscv_lo12_i + : RISCV::fixup_riscv_lo12_s; + break; + case RISCVMCExpr::VK_RISCV_HI: + FixupKind = RISCV::fixup_riscv_hi20; + break; + case RISCVMCExpr::VK_RISCV_PCREL_HI: + FixupKind = RISCV::fixup_riscv_pcrel_hi20; + break; + } + } else if (Kind == MCExpr::SymbolRef && + cast(Expr)->getKind() == MCSymbolRefExpr::VK_None) { + if (Desc.getOpcode() == RISCV::JAL) { + FixupKind = RISCV::fixup_riscv_jal; + } else if (MIFrm == RISCVII::FrmSB) { + FixupKind = RISCV::fixup_riscv_branch; + } else { + llvm_unreachable("Unhandled expression!"); + } + } else { + llvm_unreachable("Unhandled expression!"); + } + + Fixups.push_back( + MCFixup::create(0, Expr, MCFixupKind(FixupKind), MI.getLoc())); + ++MCNumFixups; return 0; } Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h =================================================================== --- /dev/null +++ lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h @@ -0,0 +1,75 @@ +//===-- RISCVMCExpr.h - RISCV specific MC expression classes ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file describes RISCV-specific MCExprs, used for modifiers like +// "%hi" or "%lo" etc., +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCEXPR_H +#define LLVM_LIB_TARGET_RISCV_MCTARGETDESC_RISCVMCEXPR_H + +#include "llvm/MC/MCExpr.h" + +namespace llvm { + +class StringRef; +class RISCVMCExpr : public MCTargetExpr { +public: + enum VariantKind { + VK_RISCV_None, + VK_RISCV_LO, + VK_RISCV_HI, + VK_RISCV_PCREL_HI, + VK_RISCV_Invalid + }; + +private: + const MCExpr *Expr; + const VariantKind Kind; + + int64_t evaluateAsInt64(int64_t Value) const; + + explicit RISCVMCExpr(const MCExpr *Expr, VariantKind Kind) + : Expr(Expr), Kind(Kind) {} + +public: + static const RISCVMCExpr *create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx); + + VariantKind getKind() const { return Kind; } + + const MCExpr *getSubExpr() const { return Expr; } + + void printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const override; + bool evaluateAsRelocatableImpl(MCValue &Res, const MCAsmLayout *Layout, + const MCFixup *Fixup) const override; + void visitUsedExpr(MCStreamer &Streamer) const override; + MCFragment *findAssociatedFragment() const override { + return getSubExpr()->findAssociatedFragment(); + } + + // There are no TLS RISCVMCExprs at the moment. + void fixELFSymbolsInTLSFixups(MCAssembler &Asm) const override {} + + bool evaluateAsConstant(int64_t &Res) const; + + static bool classof(const MCExpr *E) { + return E->getKind() == MCExpr::Target; + } + + static bool classof(const RISCVMCExpr *) { return true; } + + static VariantKind getVariantKindForName(StringRef name); + static StringRef getVariantKindName(VariantKind Kind); +}; + +} // end namespace llvm. + +#endif Index: lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp =================================================================== --- /dev/null +++ lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp @@ -0,0 +1,96 @@ +//===-- RISCVMCExpr.cpp - RISCV specific MC expression classes ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the implementation of the assembly expression modifiers +// accepted by the RISCV architecture (e.g. ":lo12:", ":gottprel_g1:", ...). +// +//===----------------------------------------------------------------------===// + +#include "RISCVMCExpr.h" +#include "llvm/MC/MCAssembler.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSymbolELF.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Object/ELF.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace llvm; + +#define DEBUG_TYPE "riscvmcexpr" + +const RISCVMCExpr *RISCVMCExpr::create(const MCExpr *Expr, VariantKind Kind, + MCContext &Ctx) { + return new (Ctx) RISCVMCExpr(Expr, Kind); +} + +void RISCVMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { + bool HasVariant = getKind() != VK_RISCV_None; + if (HasVariant) + OS << '%' << getVariantKindName(getKind()) << '('; + Expr->print(OS, MAI); + if (HasVariant) + OS << ')'; +} + +bool RISCVMCExpr::evaluateAsRelocatableImpl(MCValue &Res, + const MCAsmLayout *Layout, + const MCFixup *Fixup) const { + return getSubExpr()->evaluateAsRelocatable(Res, Layout, Fixup); +} + +void RISCVMCExpr::visitUsedExpr(MCStreamer &Streamer) const { + Streamer.visitUsedExpr(*getSubExpr()); +} + +RISCVMCExpr::VariantKind RISCVMCExpr::getVariantKindForName(StringRef name) { + return StringSwitch(name) + .Case("lo", VK_RISCV_LO) + .Case("hi", VK_RISCV_HI) + .Case("pcrel_hi", VK_RISCV_PCREL_HI) + .Default(VK_RISCV_None); +} + +StringRef RISCVMCExpr::getVariantKindName(VariantKind Kind) { + switch (Kind) { + case VK_RISCV_LO: + return "lo"; + case VK_RISCV_HI: + return "hi"; + case VK_RISCV_PCREL_HI: + return "pcrel_hi"; + default: + llvm_unreachable("Invalid ELF symbol kind"); + } +} + +bool RISCVMCExpr::evaluateAsConstant(int64_t &Res) const { + MCValue Value; + + if (!getSubExpr()->evaluateAsRelocatable(Value, nullptr, nullptr)) + return false; + + if (!Value.isAbsolute()) + return false; + + Res = evaluateAsInt64(Value.getConstant()); + return true; +} + +int64_t RISCVMCExpr::evaluateAsInt64(int64_t Value) const { + switch (Kind) { + case VK_RISCV_LO: + return SignExtend64<12>(Value); + case VK_RISCV_HI: + // Add 1 if bit 11 is 1, to compensate for low 12 bits being negative. + return ((Value + 0x800) >> 12) & 0xfffff; + default: + llvm_unreachable("Invalid kind"); + } +} Index: lib/Target/RISCV/RISCVInstrFormats.td =================================================================== --- lib/Target/RISCV/RISCVInstrFormats.td +++ lib/Target/RISCV/RISCVInstrFormats.td @@ -25,7 +25,21 @@ // //===----------------------------------------------------------------------===// -class RISCVInst pattern> +// Format specifies the encoding used by the instruction. This is used by +// RISCVMCCodeEmitter to determine which form of fixup to use. These +// definitions must be kept in-sync with RISCVBaseInfo.h. +class Format val> { + bits<4> Value = val; +} +def FrmPseudo : Format<0>; +def FrmR : Format<1>; +def FrmI : Format<2>; +def FrmS : Format<3>; +def FrmSB : Format<4>; +def FrmU : Format<5>; +def FrmOther : Format<6>; + +class RISCVInst pattern, Format format> : Instruction { field bits<32> Inst; // SoftFail is a field the disassembler can use to provide a way for @@ -45,17 +59,19 @@ dag InOperandList = ins; let AsmString = asmstr; let Pattern = pattern; + + let TSFlags{3-0} = format.Value; } // Pseudo instructions -class Pseudo pattern> - : RISCVInst { +class Pseudo pattern> + : RISCVInst { let isPseudo = 1; let isCodeGenOnly = 1; } class FR funct7, bits<3> funct3, bits<7> opcode, dag outs, dag ins, - string asmstr, list pattern> : RISCVInst + string asmstr, list pattern> : RISCVInst { bits<5> rs2; bits<5> rs1; @@ -70,7 +86,7 @@ } class FI funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list pattern> - : RISCVInst + : RISCVInst { bits<12> imm12; bits<5> rs1; @@ -84,7 +100,7 @@ } class FI32Shift funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list pattern> - : RISCVInst + : RISCVInst { bits<5> shamt; bits<5> rs1; @@ -101,7 +117,7 @@ } class FS funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list pattern> - : RISCVInst + : RISCVInst { bits<12> imm12; bits<5> rs2; @@ -116,7 +132,7 @@ } class FSB funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list pattern> - : RISCVInst + : RISCVInst { bits<12> imm12; bits<5> rs2; @@ -133,7 +149,7 @@ } class FU opcode, dag outs, dag ins, string asmstr, list pattern> - : RISCVInst + : RISCVInst { bits<20> imm20; bits<5> rd; @@ -144,7 +160,7 @@ } class FUJ opcode, dag outs, dag ins, string asmstr, list pattern> - : RISCVInst + : RISCVInst { bits<20> imm20; bits<5> rd; Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -46,6 +46,7 @@ def simm12 : Operand { let ParserMatchClass = SImmAsmOperand<12>; + let EncoderMethod = "getImmOpValue"; let DecoderMethod = "decodeSImmOperand<12>"; } Index: test/MC/RISCV/fixups-diagnostics.s =================================================================== --- /dev/null +++ test/MC/RISCV/fixups-diagnostics.s @@ -0,0 +1,18 @@ +# RUN: not llvm-mc -triple riscv32 -filetype obj < %s -o /dev/null 2>&1 | FileCheck %s + + jal a0, far_distant # CHECK: :[[@LINE]]:3: error: fixup value out of range + jal a0, unaligned # CHECK: :[[@LINE]]:3: error: fixup value must be 2-byte aligned + + beq a0, a1, distant # CHECK: :[[@LINE]]:3: error: fixup value out of range + blt t0, t1, unaligned # CHECK: :[[@LINE]]:3: error: fixup value must be 2-byte aligned + + .byte 0 +unaligned: + .byte 0 + .byte 0 + .byte 0 + + .space 1<<12 +distant: + .space 1<<20 +far_distant: Index: test/MC/RISCV/fixups.s =================================================================== --- /dev/null +++ test/MC/RISCV/fixups.s @@ -0,0 +1,49 @@ +# RUN: llvm-mc -triple riscv32 < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=CHECK-FIXUP %s +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-objdump -d - | FileCheck -check-prefix=CHECK-INSTR %s +# RUN: llvm-mc -filetype=obj -triple=riscv32 %s \ +# RUN: | llvm-readobj -r | FileCheck %s -check-prefix=CHECK-REL + +# Checks that fixups that can be resolved within the same object file are +# applied correctly + +.LBB0: +lui t1, %hi(val) +# CHECK-FIXUP: fixup A - offset: 0, value: %hi(val), kind: fixup_riscv_hi20 +# CHECK-INSTR: lui t1, 74565 + +lw a0, %lo(val)(t1) +# CHECK-FIXUP: fixup A - offset: 0, value: %lo(val), kind: fixup_riscv_lo12_i +# CHECK-INSTR: lw a0, 1656(t1) +addi a1, t1, %lo(val) +# CHECK-FIXUP: fixup A - offset: 0, value: %lo(val), kind: fixup_riscv_lo12_i +# CHECK-INSTR: addi a1, t1, 1656 +sw a0, %lo(val)(t1) +# CHECK-FIXUP: fixup A - offset: 0, value: %lo(val), kind: fixup_riscv_lo12_s +# CHECK-INSTR: sw a0, 1656(t1) + +jal zero, .LBB0 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB0, kind: fixup_riscv_jal +# CHECK-INSTR: jal zero, -16 +jal zero, .LBB2 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB2, kind: fixup_riscv_jal +# CHECK-INSTR: jal zero, 330996 +beq a0, a1, .LBB0 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB0, kind: fixup_riscv_branch +# CHECK-INSTR: beq a0, a1, -24 +blt a0, a1, .LBB1 +# CHECK-FIXUP: fixup A - offset: 0, value: .LBB1, kind: fixup_riscv_branch +# CHECK-INSTR: blt a0, a1, 1108 + +.fill 1104 + +.LBB1: + +.fill 329876 +addi zero, zero, 0 +.LBB2: + +.set val, 0x12345678 + +# CHECK-REL-NOT: R_RISCV Index: test/MC/RISCV/hilo-constaddr.s =================================================================== --- /dev/null +++ test/MC/RISCV/hilo-constaddr.s @@ -0,0 +1,39 @@ +# RUN: llvm-mc -filetype=obj -triple=riscv32 %s \ +# RUN: | llvm-objdump -d - | FileCheck %s -check-prefix=CHECK-INSTR + +# RUN: llvm-mc -filetype=obj -triple=riscv32 %s \ +# RUN: | llvm-readobj -r | FileCheck %s -check-prefix=CHECK-REL + +# Check the assembler can handle hi and lo expressions with a constant +# address, and constant expressions involving labels. Test case derived from +# test/MC/Mips/hilo-addressing.s + +# Check that 1 is added to the high 20 bits if bit 11 of the low part is 1. +.equ addr, 0xdeadbeef + lui t0, %hi(addr) + lw ra, %lo(addr)(t0) +# CHECK-INSTR: lui t0, 912092 +# CHECK-INSTR: lw ra, -273(t0) + +# Check that assembler can handle %hi(label1 - label2) and %lo(label1 - label2) +# expressions. + +tmp1: + # Emit zeros so that difference between tmp1 and tmp3 is 0x30124 bytes. + .fill 0x30124-8 +tmp2: + lui t0, %hi(tmp3-tmp1) + lw ra, %lo(tmp3-tmp1)(t0) +# CHECK-INSTR: lui t0, 48 +# CHECK-INSTR: lw ra, 292(t0) + +tmp3: + lui t1, %hi(tmp2-tmp3) + lw sp, %lo(tmp2-tmp3)(t1) +# CHECK-INSTR: lui t1, 0 +# CHECK-INSTR: lw sp, -8(t1) + +# Check that a relocation isn't emitted for %hi(label1 - label2) and +# %lo(label1 - label2) expressions. + +# CHECK-REL-NOT: R_RISCV Index: test/MC/RISCV/relocations.s =================================================================== --- /dev/null +++ test/MC/RISCV/relocations.s @@ -0,0 +1,65 @@ +# RUN: llvm-mc -triple riscv32 < %s -show-encoding \ +# RUN: | FileCheck -check-prefix=INSTR -check-prefix=FIXUP %s +# RUN: llvm-mc -filetype=obj -triple riscv32 < %s \ +# RUN: | llvm-readobj -r | FileCheck -check-prefix=RELOC %s + +# Check prefixes: +# RELOC - Check the relocation in the object. +# FIXUP - Check the fixup on the instruction. +# INSTR - Check the instruction is handled properly by the ASMPrinter + +.long foo +# RELOC: R_RISCV_32 foo + +.quad foo +# RELOC: R_RISCV_64 foo + +lui t1, %hi(foo) +# RELOC: R_RISCV_HI20 foo 0x0 +# INSTR: lui t1, %hi(foo) +# FIXUP: fixup A - offset: 0, value: %hi(foo), kind: fixup_riscv_hi20 + +lui t1, %hi(foo+4) +# RELOC: R_RISCV_HI20 foo 0x4 +# INSTR: lui t1, %hi(foo+4) +# FIXUP: fixup A - offset: 0, value: %hi(foo+4), kind: fixup_riscv_hi20 + +addi t1, t1, %lo(foo) +# RELOC: R_RISCV_LO12_I foo 0x0 +# INSTR: addi t1, t1, %lo(foo) +# FIXUP: fixup A - offset: 0, value: %lo(foo), kind: fixup_riscv_lo12_i + +addi t1, t1, %lo(foo+4) +# RELOC: R_RISCV_LO12_I foo 0x4 +# INSTR: addi t1, t1, %lo(foo+4) +# FIXUP: fixup A - offset: 0, value: %lo(foo+4), kind: fixup_riscv_lo12_i + +sb t1, %lo(foo)(a2) +# RELOC: R_RISCV_LO12_S foo 0x0 +# INSTR: sb t1, %lo(foo)(a2) +# FIXUP: fixup A - offset: 0, value: %lo(foo), kind: fixup_riscv_lo12_s + +sb t1, %lo(foo+4)(a2) +# RELOC: R_RISCV_LO12_S foo 0x4 +# INSTR: sb t1, %lo(foo+4)(a2) +# FIXUP: fixup A - offset: 0, value: %lo(foo+4), kind: fixup_riscv_lo12_s + +auipc t1, %pcrel_hi(foo) +# RELOC: R_RISCV_PCREL_HI20 foo 0x0 +# INSTR: auipc t1, %pcrel_hi(foo) +# FIXUP: fixup A - offset: 0, value: %pcrel_hi(foo), kind: fixup_riscv_pcrel_hi20 + +auipc t1, %pcrel_hi(foo+4) +# RELOC: R_RISCV_PCREL_HI20 foo 0x4 +# INSTR: auipc t1, %pcrel_hi(foo+4) +# FIXUP: fixup A - offset: 0, value: %pcrel_hi(foo+4), kind: fixup_riscv_pcrel_hi20 + +jal zero, foo +# RELOC: R_RISCV_JAL +# INSTR: jal zero, foo +# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_jal + +bgeu a0, a1, foo +# RELOC: R_RISCV_BRANCH +# INSTR: bgeu a0, a1, foo +# FIXUP: fixup A - offset: 0, value: foo, kind: fixup_riscv_branch Index: test/MC/RISCV/rv32i-valid.s =================================================================== --- test/MC/RISCV/rv32i-valid.s +++ test/MC/RISCV/rv32i-valid.s @@ -13,6 +13,15 @@ # CHECK-INST: lui s11, 552960 # CHECK: encoding: [0xb7,0x0d,0x00,0x87] lui s11, (0x87000000>>12) +# CHECK-INST: lui a0, 0 +# CHECK: encoding: [0x37,0x05,0x00,0x00] +lui a0, %hi(2) +# CHECK-INST: lui s11, 552960 +# CHECK: encoding: [0xb7,0x0d,0x00,0x87] +lui s11, (0x87000000>>12) +# CHECK-INST: lui s11, 552960 +# CHECK: encoding: [0xb7,0x0d,0x00,0x87] +lui s11, %hi(0x87000000) # CHECK-INST: lui t0, 1048575 # CHECK: encoding: [0xb7,0xf2,0xff,0xff] lui t0, 1048575 @@ -43,6 +52,9 @@ # CHECK-INST: jalr a0, a1, -2048 # CHECK: encoding: [0x67,0x85,0x05,0x80] jalr a0, a1, -2048 +# CHECK-INST: jalr a0, a1, -2048 +# CHECK: encoding: [0x67,0x85,0x05,0x80] +jalr a0, a1, %lo(2048) # CHECK-INST: jalr t2, t1, 2047 # CHECK: encoding: [0xe7,0x03,0xf3,0x7f] jalr t2, t1, 2047 @@ -78,6 +90,9 @@ # CHECK-INST: lh t1, -2048(zero) # CHECK: encoding: [0x03,0x13,0x00,0x80] lh t1, -2048(zero) +# CHECK-INST: lh t1, -2048(zero) +# CHECK: encoding: [0x03,0x13,0x00,0x80] +lh t1, %lo(2048)(zero) # CHECK-INST: lh sp, 2047(a0) # CHECK: encoding: [0x03,0x11,0xf5,0x7f] lh sp, 2047(a0) @@ -97,6 +112,9 @@ # CHECK-INST: sh t3, -2048(t5) # CHECK: encoding: [0x23,0x10,0xcf,0x81] sh t3, -2048(t5) +# CHECK-INST: sh t3, -2048(t5) +# CHECK: encoding: [0x23,0x10,0xcf,0x81] +sh t3, %lo(2048)(t5) # CHECK-INST: sw ra, 999(zero) # CHECK: encoding: [0xa3,0x23,0x10,0x3e] sw ra, 999(zero) @@ -116,6 +134,9 @@ # CHECK-INST: ori a0, a1, -2048 # CHECK: encoding: [0x13,0xe5,0x05,0x80] ori a0, a1, -2048 +# CHECK-INST: ori a0, a1, -2048 +# CHECK: encoding: [0x13,0xe5,0x05,0x80] +ori a0, a1, %lo(2048) # CHECK-INST: andi ra, sp, 2047 # CHECK: encoding: [0x93,0x70,0xf1,0x7f] andi ra, sp, 2047