[RISCV] Add common fixups and relocations
authorAlex Bradbury <asb@lowrisc.org>
Thu, 28 Sep 2017 08:26:24 +0000 (08:26 +0000)
committerAlex Bradbury <asb@lowrisc.org>
Thu, 28 Sep 2017 08:26:24 +0000 (08:26 +0000)
%lo(), %hi(), and %pcrel_hi() are supported and test cases have been added to
ensure the appropriate fixups and relocations are generated. I've added an
instruction format field which is used in RISCVMCCodeEmitter to, for
instance, tell whether it should emit a lo12_i fixup or a lo12_s fixup
(RISC-V has two 12-bit immediate encodings depending on the instruction
type).

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

llvm-svn: 314389

17 files changed:
llvm/lib/Target/RISCV/AsmParser/RISCVAsmParser.cpp
llvm/lib/Target/RISCV/MCTargetDesc/CMakeLists.txt
llvm/lib/Target/RISCV/MCTargetDesc/RISCVAsmBackend.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVBaseInfo.h
llvm/lib/Target/RISCV/MCTargetDesc/RISCVELFObjectWriter.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h [new file with mode: 0644]
llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCCodeEmitter.cpp
llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp [new file with mode: 0644]
llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h [new file with mode: 0644]
llvm/lib/Target/RISCV/RISCVInstrFormats.td
llvm/lib/Target/RISCV/RISCVInstrInfo.td
llvm/test/MC/RISCV/fixups-diagnostics.s [new file with mode: 0644]
llvm/test/MC/RISCV/fixups.s [new file with mode: 0644]
llvm/test/MC/RISCV/hilo-constaddr.s [new file with mode: 0644]
llvm/test/MC/RISCV/relocations.s [new file with mode: 0644]
llvm/test/MC/RISCV/rv32i-invalid.s
llvm/test/MC/RISCV/rv32i-valid.s

index 4b1eec9..b0db5f4 100644 (file)
@@ -8,6 +8,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "MCTargetDesc/RISCVBaseInfo.h"
+#include "MCTargetDesc/RISCVMCExpr.h"
 #include "MCTargetDesc/RISCVMCTargetDesc.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringSwitch.h"
@@ -53,6 +54,7 @@ class RISCVAsmParser : public MCTargetAsmParser {
   OperandMatchResultTy parseImmediate(OperandVector &Operands);
   OperandMatchResultTy parseRegister(OperandVector &Operands);
   OperandMatchResultTy parseMemOpBaseReg(OperandVector &Operands);
+  OperandMatchResultTy parseOperandWithModifier(OperandVector &Operands);
 
   bool parseOperand(OperandVector &Operands);
 
@@ -64,6 +66,10 @@ public:
 #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) {
@@ -121,13 +127,32 @@ public:
   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
@@ -158,28 +183,49 @@ public:
   }
 
   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; }
@@ -234,7 +280,7 @@ public:
   }
 
   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;
@@ -244,8 +290,17 @@ public:
 
   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));
   }
@@ -411,9 +466,51 @@ OperandMatchResultTy RISCVAsmParser::parseImmediate(OperandVector &Operands) {
     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;
 }
 
@@ -498,6 +595,51 @@ bool RISCVAsmParser::ParseInstruction(ParseInstructionInfo &Info,
   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() {
index d79de74..6042964 100644 (file)
@@ -2,6 +2,7 @@ add_llvm_library(LLVMRISCVDesc
   RISCVAsmBackend.cpp
   RISCVELFObjectWriter.cpp
   RISCVMCAsmInfo.cpp
-  RISCVMCTargetDesc.cpp
   RISCVMCCodeEmitter.cpp
+  RISCVMCExpr.cpp
+  RISCVMCTargetDesc.cpp
 )
index 4896c38..692a179 100644 (file)
@@ -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,31 @@ public:
     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 +97,88 @@ bool RISCVAsmBackend::writeNopData(uint64_t Count, MCObjectWriter *OW) const {
   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;
 }
 
index 93c981e..95d4242 100644 (file)
@@ -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 @@ unsigned RISCVELFObjectWriter::getRelocType(MCContext &Ctx,
                                             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,
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVFixupKinds.h
new file mode 100644 (file)
index 0000000..1152294
--- /dev/null
@@ -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,
+
+  // 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
index f4eaf06..f821215 100644 (file)
@@ -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"
 #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 @@ using namespace llvm;
 #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 @@ public:
   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;
@@ -68,7 +77,7 @@ public:
 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 @@ RISCVMCCodeEmitter::getImmOpValueAsr1(const MCInst &MI, unsigned OpNo,
     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,50 @@ 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;
 }
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.cpp
new file mode 100644 (file)
index 0000000..b36236e
--- /dev/null
@@ -0,0 +1,99 @@
+//===-- 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;
+  }
+}
diff --git a/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h b/llvm/lib/Target/RISCV/MCTargetDesc/RISCVMCExpr.h
new file mode 100644 (file)
index 0000000..69b55ca
--- /dev/null
@@ -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 d77f387..383b73c 100644 (file)
 //
 //===----------------------------------------------------------------------===//
 
-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
@@ -45,17 +60,19 @@ class RISCVInst<dag outs, dag ins, string asmstr, list<dag> pattern>
   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;
@@ -70,7 +87,7 @@ class FR<bits<7> funct7, bits<3> funct3, bits<7> opcode, dag outs, dag ins,
 }
 
 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;
@@ -84,7 +101,7 @@ class FI<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<
 }
 
 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;
@@ -101,7 +118,7 @@ class FI32Shift<bit arithshift, bits<3> funct3, bits<7> opcode, dag outs, dag in
 }
 
 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;
@@ -116,7 +133,7 @@ class FS<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list<
 }
 
 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;
@@ -133,7 +150,7 @@ class FSB<bits<3> funct3, bits<7> opcode, dag outs, dag ins, string asmstr, list
 }
 
 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;
@@ -144,7 +161,7 @@ class FU<bits<7> opcode, dag outs, dag ins, string asmstr, list<dag> pattern>
 }
 
 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;
index e84e4da..1a5f32e 100644 (file)
@@ -46,6 +46,7 @@ def uimm5 : Operand<i32> {
 
 def simm12 : Operand<i32> {
   let ParserMatchClass = SImmAsmOperand<12>;
+  let EncoderMethod = "getImmOpValue";
   let DecoderMethod = "decodeSImmOperand<12>";
 }
 
diff --git a/llvm/test/MC/RISCV/fixups-diagnostics.s b/llvm/test/MC/RISCV/fixups-diagnostics.s
new file mode 100644 (file)
index 0000000..d346605
--- /dev/null
@@ -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:
diff --git a/llvm/test/MC/RISCV/fixups.s b/llvm/test/MC/RISCV/fixups.s
new file mode 100644 (file)
index 0000000..19cc692
--- /dev/null
@@ -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
diff --git a/llvm/test/MC/RISCV/hilo-constaddr.s b/llvm/test/MC/RISCV/hilo-constaddr.s
new file mode 100644 (file)
index 0000000..691401f
--- /dev/null
@@ -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
diff --git a/llvm/test/MC/RISCV/relocations.s b/llvm/test/MC/RISCV/relocations.s
new file mode 100644 (file)
index 0000000..c11e2f3
--- /dev/null
@@ -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 faafbfb..da49b93 100644 (file)
@@ -15,6 +15,10 @@ csrrwi a1, 0x1, -1 # CHECK: :[[@LINE]]:17: error: immediate must be an integer i
 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]
@@ -24,10 +28,6 @@ csrrwi a0, -50, 0 # CHECK: :[[@LINE]]:12: error: immediate must be an integer in
 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]
@@ -48,6 +48,59 @@ jal gp, 1048575 # CHECK: :[[@LINE]]:9: error: immediate must be a multiple of 2
 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
index 95d9da4..be65600 100644 (file)
@@ -13,6 +13,15 @@ lui a0, 2
 # 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 @@ jal a3, 256
 # 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 @@ lb s3, +4(ra)
 # 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 @@ sb a0, 2047(a2)
 # 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 @@ xori tp, t1, -99
 # 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