//===----------------------------------------------------------------------===//
#include "MCTargetDesc/RISCVBaseInfo.h"
+#include "MCTargetDesc/RISCVMCExpr.h"
#include "MCTargetDesc/RISCVMCTargetDesc.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
OperandMatchResultTy parseImmediate(OperandVector &Operands);
OperandMatchResultTy parseRegister(OperandVector &Operands);
OperandMatchResultTy parseMemOpBaseReg(OperandVector &Operands);
+ OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands);
bool parseOperand(OperandVector &Operands);
#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) {
bool isImm() const override { return Kind == Immediate; }
bool isMem() const override { return false; }
- bool isConstantImm() const {
- return isImm() && dyn_cast<MCConstantExpr>(getImm());
- }
-
- int64_t getConstantImm() const {
+ bool evaluateConstantImm(int64_t &Imm, RISCVMCExpr::VariantKind &VK) const {
const MCExpr *Val = getImm();
- return static_cast<const MCConstantExpr *>(Val)->getValue();
+ bool Ret = false;
+ if (auto *RE = dyn_cast<RISCVMCExpr>(Val)) {
+ Ret = RE->evaluateAsConstant(Imm);
+ VK = RE->getKind();
+ } else if (auto CE = dyn_cast<MCConstantExpr>(Val)) {
+ Ret = true;
+ VK = RISCVMCExpr::VK_RISCV_None;
+ Imm = CE->getValue();
+ }
+ return Ret;
+ }
+
+ // True if operand is a symbol with no modifiers, or a constant with no
+ // modifiers and isShiftedInt<N-1, 1>(Op).
+ template <int N> bool isBareSimmNLsb0() const {
+ int64_t Imm;
+ RISCVMCExpr::VariantKind VK;
+ bool IsConstantImm = evaluateConstantImm(Imm, VK);
+ bool IsValid;
+ if (!IsConstantImm)
+ IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm);
+ else
+ IsValid = isShiftedInt<N - 1, 1>(Imm);
+ return IsValid && VK == RISCVMCExpr::VK_RISCV_None;
}
// Predicate methods for AsmOperands defined in RISCVInstrInfo.td
}
bool isUImm5() const {
- return (isConstantImm() && isUInt<5>(getConstantImm()));
+ int64_t Imm;
+ RISCVMCExpr::VariantKind VK;
+ bool IsConstantImm = evaluateConstantImm(Imm, VK);
+ return IsConstantImm && isUInt<5>(Imm) && VK == RISCVMCExpr::VK_RISCV_None;
}
bool isSImm12() const {
- return (isConstantImm() && isInt<12>(getConstantImm()));
+ RISCVMCExpr::VariantKind VK;
+ int64_t Imm;
+ bool IsValid;
+ bool IsConstantImm = evaluateConstantImm(Imm, VK);
+ if (!IsConstantImm)
+ IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm);
+ else
+ IsValid = isInt<12>(Imm);
+ return IsValid &&
+ (VK == RISCVMCExpr::VK_RISCV_None || VK == RISCVMCExpr::VK_RISCV_LO);
}
bool isUImm12() const {
- return (isConstantImm() && isUInt<12>(getConstantImm()));
+ int64_t Imm;
+ RISCVMCExpr::VariantKind VK;
+ bool IsConstantImm = evaluateConstantImm(Imm, VK);
+ return IsConstantImm && isUInt<12>(Imm) && VK == RISCVMCExpr::VK_RISCV_None;
}
- bool isSImm13Lsb0() const {
- return (isConstantImm() && isShiftedInt<12, 1>(getConstantImm()));
- }
+ bool isSImm13Lsb0() const { return isBareSimmNLsb0<13>(); }
bool isUImm20() const {
- return (isConstantImm() && isUInt<20>(getConstantImm()));
+ RISCVMCExpr::VariantKind VK;
+ int64_t Imm;
+ bool IsValid;
+ bool IsConstantImm = evaluateConstantImm(Imm, VK);
+ if (!IsConstantImm)
+ IsValid = RISCVAsmParser::classifySymbolRef(getImm(), VK, Imm);
+ else
+ IsValid = isUInt<20>(Imm);
+ return IsValid && (VK == RISCVMCExpr::VK_RISCV_None ||
+ VK == RISCVMCExpr::VK_RISCV_HI ||
+ VK == RISCVMCExpr::VK_RISCV_PCREL_HI);
}
- bool isSImm21Lsb0() const {
- return (isConstantImm() && isShiftedInt<20, 1>(getConstantImm()));
- }
+ bool isSImm21Lsb0() const { return isBareSimmNLsb0<21>(); }
/// getStartLoc - Gets location of the first token of this operand
SMLoc getStartLoc() const override { return StartLoc; }
}
static std::unique_ptr<RISCVOperand> createImm(const MCExpr *Val, SMLoc S,
- SMLoc E) {
+ SMLoc E, MCContext &Ctx) {
auto Op = make_unique<RISCVOperand>(Immediate);
Op->Imm.Val = Val;
Op->StartLoc = S;
void addExpr(MCInst &Inst, const MCExpr *Expr) const {
assert(Expr && "Expr shouldn't be null!");
- if (auto *CE = dyn_cast<MCConstantExpr>(Expr))
- Inst.addOperand(MCOperand::createImm(CE->getValue()));
+ int64_t Imm = 0;
+ bool IsConstant = false;
+ if (auto *RE = dyn_cast<RISCVMCExpr>(Expr)) {
+ IsConstant = RE->evaluateAsConstant(Imm);
+ } else if (auto *CE = dyn_cast<MCConstantExpr>(Expr)) {
+ IsConstant = true;
+ Imm = CE->getValue();
+ }
+
+ if (IsConstant)
+ Inst.addOperand(MCOperand::createImm(Imm));
else
Inst.addOperand(MCOperand::createExpr(Expr));
}
Res = MCSymbolRefExpr::create(Sym, MCSymbolRefExpr::VK_None, getContext());
break;
}
+ case AsmToken::Percent:
+ return parseOperandWithModifier(Operands);
+ }
+
+ 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().getIdentifier();
+ RISCVMCExpr::VariantKind VK = RISCVMCExpr::getVariantKindForName(Identifier);
+ if (VK == RISCVMCExpr::VK_RISCV_Invalid) {
+ 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;
}
- Operands.push_back(RISCVOperand::createImm(Res, S, E));
+ const MCExpr *ModExpr = RISCVMCExpr::create(SubExpr, VK, getContext());
+ Operands.push_back(RISCVOperand::createImm(ModExpr, S, E, getContext()));
return MatchOperand_Success;
}
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<RISCVMCExpr>(Expr)) {
+ Kind = RE->getKind();
+ Expr = RE->getSubExpr();
+ }
+
+ // It's a simple symbol reference or constant with no addend.
+ if (isa<MCConstantExpr>(Expr) || isa<MCSymbolRefExpr>(Expr))
+ return true;
+
+ const MCBinaryExpr *BE = dyn_cast<MCBinaryExpr>(Expr);
+ if (!BE)
+ return false;
+
+ if (!isa<MCSymbolRefExpr>(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<MCSymbolRefExpr>(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<MCConstantExpr>(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() {
RISCVAsmBackend.cpp
RISCVELFObjectWriter.cpp
RISCVMCAsmInfo.cpp
- RISCVMCTargetDesc.cpp
RISCVMCCodeEmitter.cpp
+ RISCVMCExpr.cpp
+ RISCVMCTargetDesc.cpp
)
//
//===----------------------------------------------------------------------===//
+#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"
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; }
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<char> 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) {
+ Data[Offset + i] |= uint8_t((Value >> (i * 8)) & 0xff);
+ }
return;
}
InstFormatMask = 15
};
+
enum {
MO_None,
MO_LO,
//
//===----------------------------------------------------------------------===//
+#include "MCTargetDesc/RISCVFixupKinds.h"
#include "MCTargetDesc/RISCVMCTargetDesc.h"
#include "llvm/MC/MCELFObjectWriter.h"
#include "llvm/MC/MCFixup.h"
const MCValue &Target,
const MCFixup &Fixup,
bool IsPCRel) const {
- report_fatal_error("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,
--- /dev/null
+//===-- 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,
+
+ // fixup_riscv_invalid - used as a sentinel and a marker, must be last fixup
+ fixup_riscv_invalid,
+ NumTargetFixupKinds = fixup_riscv_invalid - FirstTargetFixupKind
+};
+} // end namespace RISCV
+} // end namespace llvm
+
+#endif
//
//===----------------------------------------------------------------------===//
+#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"
#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"
#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 {}
unsigned getImmOpValueAsr1(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
+
unsigned getImmOpValue(const MCInst &MI, unsigned OpNo,
SmallVectorImpl<MCFixup> &Fixups,
const MCSubtargetInfo &STI) const;
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,
return Res >> 1;
}
- llvm_unreachable("Unhandled expression!");
-
- return 0;
+ return getImmOpValue(MI, OpNo, Fixups, STI);
}
unsigned RISCVMCCodeEmitter::getImmOpValue(const MCInst &MI, unsigned OpNo,
const MCOperand &MO = MI.getOperand(OpNo);
+ MCInstrDesc const &Desc = MCII.get(MI.getOpcode());
+ unsigned MIFrm = Desc.TSFlags & RISCVII::InstFormatMask;
+
// 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 = RISCV::fixup_riscv_invalid;
+ if (Kind == MCExpr::Target) {
+ const RISCVMCExpr *RVExpr = cast<RISCVMCExpr>(Expr);
+
+ switch (RVExpr->getKind()) {
+ case RISCVMCExpr::VK_RISCV_None:
+ case RISCVMCExpr::VK_RISCV_Invalid:
+ llvm_unreachable("Unhandled fixup kind!");
+ case RISCVMCExpr::VK_RISCV_LO:
+ FixupKind = MIFrm == RISCVII::InstFormatI ? 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<MCSymbolRefExpr>(Expr)->getKind() == MCSymbolRefExpr::VK_None) {
+ if (Desc.getOpcode() == RISCV::JAL) {
+ FixupKind = RISCV::fixup_riscv_jal;
+ } else if (MIFrm == RISCVII::InstFormatSB) {
+ FixupKind = RISCV::fixup_riscv_branch;
+ }
+ }
+
+ assert(FixupKind != RISCV::fixup_riscv_invalid && "Unhandled expression!");
+
+ Fixups.push_back(
+ MCFixup::create(0, Expr, MCFixupKind(FixupKind), MI.getLoc()));
+ ++MCNumFixups;
return 0;
}
--- /dev/null
+//===-- 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<RISCVMCExpr::VariantKind>(name)
+ .Case("lo", VK_RISCV_LO)
+ .Case("hi", VK_RISCV_HI)
+ .Case("pcrel_hi", VK_RISCV_PCREL_HI)
+ .Default(VK_RISCV_Invalid);
+}
+
+StringRef RISCVMCExpr::getVariantKindName(VariantKind Kind) {
+ switch (Kind) {
+ default:
+ llvm_unreachable("Invalid ELF symbol kind");
+ case VK_RISCV_LO:
+ return "lo";
+ case VK_RISCV_HI:
+ return "hi";
+ case VK_RISCV_PCREL_HI:
+ return "pcrel_hi";
+ }
+}
+
+bool RISCVMCExpr::evaluateAsConstant(int64_t &Res) const {
+ MCValue Value;
+
+ if (Kind == VK_RISCV_PCREL_HI)
+ return false;
+
+ 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) {
+ default:
+ llvm_unreachable("Invalid 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;
+ }
+}
--- /dev/null
+//===-- 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
//
//===----------------------------------------------------------------------===//
-class RISCVInst<dag outs, dag ins, string asmstr, list<dag> 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 InstFormat<bits<4> val> {
+ bits<4> Value = val;
+}
+def InstFormatPseudo : InstFormat<0>;
+def InstFormatR : InstFormat<1>;
+def InstFormatI : InstFormat<2>;
+def InstFormatS : InstFormat<3>;
+def InstFormatSB : InstFormat<4>;
+def InstFormatU : InstFormat<5>;
+def InstFormatOther : InstFormat<6>;
+
+class RISCVInst<dag outs, dag ins, string asmstr, list<dag> pattern,
+ InstFormat format>
: Instruction {
field bits<32> Inst;
// SoftFail is a field the disassembler can use to provide a way for
dag InOperandList = ins;
let AsmString = asmstr;
let Pattern = pattern;
+
+ let TSFlags{3-0} = format.Value;
}
// Pseudo instructions
-class Pseudo<dag outs, dag ins, string asmstr, list<dag> pattern>
- : RISCVInst<outs, ins, "", pattern> {
+class Pseudo<dag outs, dag ins, list<dag> pattern>
+ : RISCVInst<outs, ins, "", pattern, InstFormatPseudo> {
let isPseudo = 1;
let isCodeGenOnly = 1;
}
class FR<bits<7> funct7, bits<3> funct3, bits<7> opcode, dag outs, dag ins,
- string asmstr, list<dag> pattern> : RISCVInst<outs, ins, asmstr, pattern>
+ string asmstr, list<dag> pattern> : RISCVInst<outs, ins, asmstr, pattern, InstFormatR>
{
bits<5> rs2;
bits<5> rs1;
}
class FI<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern>
- : RISCVInst<outs, ins, asmstr, pattern>
+ : RISCVInst<outs, ins, asmstr, pattern, InstFormatI>
{
bits<12> imm12;
bits<5> rs1;
}
class FI32Shift<bit arithshift, bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern>
- : RISCVInst<outs, ins, asmstr, pattern>
+ : RISCVInst<outs, ins, asmstr, pattern, InstFormatI>
{
bits<5> shamt;
bits<5> rs1;
}
class FS<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern>
- : RISCVInst<outs, ins, asmstr, pattern>
+ : RISCVInst<outs, ins, asmstr, pattern, InstFormatS>
{
bits<12> imm12;
bits<5> rs2;
}
class FSB<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern>
- : RISCVInst<outs, ins, asmstr, pattern>
+ : RISCVInst<outs, ins, asmstr, pattern, InstFormatSB>
{
bits<12> imm12;
bits<5> rs2;
}
class FU<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern>
- : RISCVInst<outs, ins, asmstr, pattern>
+ : RISCVInst<outs, ins, asmstr, pattern, InstFormatU>
{
bits<20> imm20;
bits<5> rd;
}
class FUJ<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern>
- : RISCVInst<outs, ins, asmstr, pattern>
+ : RISCVInst<outs, ins, asmstr, pattern, InstFormatU>
{
bits<20> imm20;
bits<5> rd;
def simm12 : Operand<i32> {
let ParserMatchClass = SImmAsmOperand<12>;
+ let EncoderMethod = "getImmOpValue";
let DecoderMethod = "decodeSImmOperand<12>";
}
--- /dev/null
+# 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:
--- /dev/null
+# 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
--- /dev/null
+# 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
--- /dev/null
+# 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
csrrsi t1, 999, 32 # CHECK: :[[@LINE]]:17: error: immediate must be an integer in the range [0, 31]
csrrci x0, 43, -90 # CHECK: :[[@LINE]]:16: error: immediate must be an integer in the range [0, 31]
+## simm12
+ori a0, a1, -2049 # CHECK: :[[@LINE]]:13: error: immediate must be an integer in the range [-2048, 2047]
+andi ra, sp, 2048 # CHECK: :[[@LINE]]:14: error: immediate must be an integer in the range [-2048, 2047]
+
## uimm12
csrrw a0, -1, a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095]
csrrs a0, 4096, a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095]
csrrsi a0, 4097, a0 # CHECK: :[[@LINE]]:12: error: immediate must be an integer in the range [0, 4095]
csrrci a0, 0xffff, a0 # CHECK: :[[@LINE]]:12: error: immediate must be an integer in the range [0, 4095]
-## simm12
-ori a0, a1, -2049 # CHECK: :[[@LINE]]:13: error: immediate must be an integer in the range [-2048, 2047]
-andi ra, sp, 2048 # CHECK: :[[@LINE]]:14: error: immediate must be an integer in the range [-2048, 2047]
-
## simm13_lsb0
beq t0, t1, -4098 # CHECK: :[[@LINE]]:13: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
bne t0, t1, -4097 # CHECK: :[[@LINE]]:13: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
jal gp, 1048576 # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
jal gp, 1 # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
+# Illegal operand modifier
+## fencearg
+fence %hi(iorw), iorw # CHECK: :[[@LINE]]:7: error: operand must be formed of letters selected in-order from 'iorw'
+fence %lo(iorw), iorw # CHECK: :[[@LINE]]:7: error: operand must be formed of letters selected in-order from 'iorw'
+fence %pcrel_hi(iorw), iorw # CHECK: :[[@LINE]]:7: error: operand must be formed of letters selected in-order from 'iorw'
+
+## uimm5
+slli a0, a0, %lo(1) # CHECK: :[[@LINE]]:14: error: immediate must be an integer in the range [0, 31]
+srli a0, a0, %lo(a) # CHECK: :[[@LINE]]:14: error: immediate must be an integer in the range [0, 31]
+srai a0, a0, %hi(2) # CHECK: :[[@LINE]]:14: error: immediate must be an integer in the range [0, 31]
+csrrwi a1, 0x1, %hi(b) # CHECK: :[[@LINE]]:17: error: immediate must be an integer in the range [0, 31]
+csrrsi t1, 999, %pcrel_hi(3) # CHECK: :[[@LINE]]:17: error: immediate must be an integer in the range [0, 31]
+csrrci x0, 43, %pcrel_hi(c) # CHECK: :[[@LINE]]:16: error: immediate must be an integer in the range [0, 31]
+
+## simm12
+ori a0, a1, %hi(foo) # CHECK: :[[@LINE]]:13: error: immediate must be an integer in the range [-2048, 2047]
+andi ra, sp, %pcrel_hi(123) # CHECK: :[[@LINE]]:14: error: immediate must be an integer in the range [-2048, 2047]
+xori a2, a3, %hi(345) # CHECK: :[[@LINE]]:14: error: immediate must be an integer in the range [-2048, 2047]
+
+## uimm12
+csrrw a0, %lo(1), a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095]
+csrrs a0, %lo(a), a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095]
+csrrs a0, %hi(2), a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095]
+csrrc a0, %hi(b), a0 # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 4095]
+csrrwi a0, %pcrel_hi(3), 0 # CHECK: :[[@LINE]]:12: error: immediate must be an integer in the range [0, 4095]
+csrrsi a0, %pcrel_hi(c), a0 # CHECK: :[[@LINE]]:12: error: immediate must be an integer in the range [0, 4095]
+
+## simm13_lsb0
+beq t0, t1, %lo(1) # CHECK: :[[@LINE]]:13: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
+bne t0, t1, %lo(a) # CHECK: :[[@LINE]]:13: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
+blt t0, t1, %hi(2) # CHECK: :[[@LINE]]:13: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
+bge t0, t1, %hi(b) # CHECK: :[[@LINE]]:13: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
+bltu t0, t1, %pcrel_hi(3) # CHECK: :[[@LINE]]:14: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
+bgeu t0, t1, %pcrel_hi(c) # CHECK: :[[@LINE]]:14: error: immediate must be a multiple of 2 bytes in the range [-4096, 4094]
+
+## uimm20
+lui a0, %lo(1) # CHECK: :[[@LINE]]:9: error: immediate must be an integer in the range [0, 1048575]
+auipc a1, %lo(foo) # CHECK: :[[@LINE]]:11: error: immediate must be an integer in the range [0, 1048575]
+
+## simm21_lsb0
+jal gp, %lo(1) # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
+jal gp, %lo(a) # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
+jal gp, %hi(2) # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
+jal gp, %hi(b) # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
+jal gp, %pcrel_hi(3) # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
+jal gp, %pcrel_hi(c) # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2 bytes in the range [-1048576, 1048574]
+
+# Unrecognized operand modifier
+addi t0, sp, %modifer(255) # CHECK: :[[@LINE]]:15: error: unrecognized operand modifier
+
+# Use of operand modifier on register name
+addi t1, %lo(t2), 1 # CHECK: :[[@LINE]]:10: error: invalid operand for instruction
+
# Invalid mnemonics
subs t0, t2, t1 # CHECK: :[[@LINE]]:1: error: unrecognized instruction mnemonic
nandi t0, zero, 0 # CHECK: :[[@LINE]]:1: error: unrecognized instruction mnemonic
# 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
# 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
# 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)
# 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)
# 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