[lld] 6611d58 - [ELF] Relax R_RISCV_ALIGN

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 7 10:16:13 PDT 2022


Author: Fangrui Song
Date: 2022-07-07T10:16:09-07:00
New Revision: 6611d58f5bbcbec77262d392e2923e1d680f6985

URL: https://github.com/llvm/llvm-project/commit/6611d58f5bbcbec77262d392e2923e1d680f6985
DIFF: https://github.com/llvm/llvm-project/commit/6611d58f5bbcbec77262d392e2923e1d680f6985.diff

LOG: [ELF] Relax R_RISCV_ALIGN

Alternative to D125036. Implement R_RISCV_ALIGN relaxation so that we can handle
-mrelax object files (i.e. -mno-relax is no longer needed) and creates a
framework for future relaxation.

`relaxAux` is placed in a union with InputSectionBase::jumpInstrMod, storing
auxiliary information for relaxation. In the first pass, `relaxAux` is allocated.
The main data structure is `relocDeltas`: when referencing `relocations[i]`, the
actual offset is `r_offset - (i ? relocDeltas[i-1] : 0)`.

`relaxOnce` performs one relaxation pass. It computes `relocDeltas` for all text
section. Then, adjust st_value/st_size for symbols relative to this section
based on `SymbolAnchor`. `bytesDropped` is set so that `assignAddresses` knows
that the size has changed.

Run `relaxOnce` in the `finalizeAddressDependentContent` loop to wait for
convergence of text sections and other address dependent sections (e.g.
SHT_RELR). Note: extrating `relaxOnce` into a separate loop works for many cases
but has issues in some linker script edge cases.

After convergence, compute section contents: shrink the NOP sequence of each
R_RISCV_ALIGN as appropriate. Instead of deleting bytes, we run a sequence of
memcpy on the content delimitered by relocation locations. For R_RISCV_ALIGN let
the next memcpy skip the desired number of bytes. Section content computation is
parallelizable, but let's ensure the implementation is mature before
optimizations. Technically we can save a copy if we interleave some code with
`OutputSection::writeTo`, but let's not pollute the generic code (we don't have
templated relocation resolving, so using conditions can impose overhead to
non-RISCV.)

Tested:
`make ARCH=riscv CROSS_COMPILE=riscv64-linux-gnu- LLVM=1 defconfig all` built Linux kernel using -mrelax is bootable.
FreeBSD RISCV64 system using -mrelax is bootable.
bash/curl/firefox/libevent/vim/tmux using -mrelax works.

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

Added: 
    lld/test/ELF/riscv-relax-align-rvc.s
    lld/test/ELF/riscv-relax-align.s

Modified: 
    lld/ELF/Arch/RISCV.cpp
    lld/ELF/InputSection.cpp
    lld/ELF/InputSection.h
    lld/ELF/Relocations.cpp
    lld/ELF/Relocations.h
    lld/ELF/Target.h
    lld/ELF/Writer.cpp

Removed: 
    lld/test/ELF/riscv-reloc-align.s


################################################################################
diff  --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp
index 7ba0214eb2a7f..7d2255f44060d 100644
--- a/lld/ELF/Arch/RISCV.cpp
+++ b/lld/ELF/Arch/RISCV.cpp
@@ -7,9 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "InputFiles.h"
+#include "OutputSections.h"
 #include "Symbols.h"
 #include "SyntheticSections.h"
 #include "Target.h"
+#include "llvm/Support/TimeProfiler.h"
 
 using namespace llvm;
 using namespace llvm::object;
@@ -36,6 +38,7 @@ class RISCV final : public TargetInfo {
                      const uint8_t *loc) const override;
   void relocate(uint8_t *loc, const Relocation &rel,
                 uint64_t val) const override;
+  bool relaxOnce(int pass) const override;
 };
 
 } // end anonymous namespace
@@ -271,12 +274,7 @@ RelExpr RISCV::getRelExpr(const RelType type, const Symbol &s,
   case R_RISCV_TPREL_ADD:
     return R_NONE;
   case R_RISCV_ALIGN:
-    // Not just a hint; always padded to the worst-case number of NOPs, so may
-    // not currently be aligned, and without linker relaxation support we can't
-    // delete NOPs to realign.
-    errorOrWarn(getErrorLocation(loc) + "relocation R_RISCV_ALIGN requires "
-                "unimplemented linker relaxation; recompile with -mno-relax");
-    return R_NONE;
+    return R_RELAX_HINT;
   default:
     error(getErrorLocation(loc) + "unknown relocation (" + Twine(type) +
           ") against symbol " + toString(s));
@@ -476,6 +474,233 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
   }
 }
 
+namespace {
+struct SymbolAnchor {
+  uint64_t offset;
+  Defined *d;
+  bool end; // true for the anchor of st_value+st_size
+};
+} // namespace
+
+struct elf::RISCVRelaxAux {
+  // This records symbol start and end offsets which will be adjusted according
+  // to the nearest relocDeltas element.
+  SmallVector<SymbolAnchor, 0> anchors;
+  // For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] :
+  // 0).
+  std::unique_ptr<uint32_t[]> relocDeltas;
+};
+
+static void initSymbolAnchors() {
+  SmallVector<InputSection *, 0> storage;
+  for (OutputSection *osec : outputSections) {
+    if (!(osec->flags & SHF_EXECINSTR))
+      continue;
+    for (InputSection *sec : getInputSections(*osec, storage)) {
+      sec->relaxAux = make<RISCVRelaxAux>();
+      if (sec->relocations.size())
+        sec->relaxAux->relocDeltas =
+            std::make_unique<uint32_t[]>(sec->relocations.size());
+    }
+  }
+  // Store anchors (st_value and st_value+st_size) for symbols relative to text
+  // sections.
+  for (InputFile *file : ctx->objectFiles)
+    for (Symbol *sym : file->getSymbols()) {
+      auto *d = dyn_cast<Defined>(sym);
+      if (!d || d->file != file)
+        continue;
+      if (auto *sec = dyn_cast_or_null<InputSection>(d->section))
+        if (sec->flags & SHF_EXECINSTR && sec->relaxAux) {
+          // If sec is discarded, relaxAux will be nullptr.
+          sec->relaxAux->anchors.push_back({d->value, d, false});
+          sec->relaxAux->anchors.push_back({d->value + d->size, d, true});
+        }
+    }
+  // Sort anchors by offset so that we can find the closest relocation
+  // efficiently. For a zero size symbol, ensure that its start anchor precedes
+  // its end anchor. For two symbols with anchors at the same offset, their
+  // order does not matter.
+  for (OutputSection *osec : outputSections) {
+    if (!(osec->flags & SHF_EXECINSTR))
+      continue;
+    for (InputSection *sec : getInputSections(*osec, storage)) {
+      llvm::sort(sec->relaxAux->anchors, [](auto &a, auto &b) {
+        return std::make_pair(a.offset, a.end) <
+               std::make_pair(b.offset, b.end);
+      });
+    }
+  }
+}
+
+static bool relax(InputSection &sec) {
+  const uint64_t secAddr = sec.getVA();
+  auto &aux = *sec.relaxAux;
+  bool changed = false;
+
+  // Restore original st_value for symbols relative to this section.
+  ArrayRef<SymbolAnchor> sa = makeArrayRef(aux.anchors);
+  uint32_t delta = 0;
+  for (auto it : llvm::enumerate(sec.relocations)) {
+    for (; sa.size() && sa[0].offset <= it.value().offset; sa = sa.slice(1))
+      if (!sa[0].end)
+        sa[0].d->value += delta;
+    delta = aux.relocDeltas[it.index()];
+  }
+  for (const SymbolAnchor &sa : sa)
+    if (!sa.end)
+      sa.d->value += delta;
+  sa = makeArrayRef(aux.anchors);
+  delta = 0;
+
+  for (auto it : llvm::enumerate(sec.relocations)) {
+    Relocation &r = it.value();
+    const size_t i = it.index();
+    const uint64_t loc = secAddr + r.offset - delta;
+    uint32_t &cur = aux.relocDeltas[i], remove = 0;
+    switch (r.type) {
+    case R_RISCV_ALIGN: {
+      const uint64_t nextLoc = loc + r.addend;
+      const uint64_t align = PowerOf2Ceil(r.addend + 2);
+      // All bytes beyond the alignment boundary should be removed.
+      remove = nextLoc - ((loc + align - 1) & -align);
+      assert(static_cast<int32_t>(remove) >= 0 &&
+             "R_RISCV_ALIGN needs expanding the content");
+      break;
+    }
+    }
+
+    // For all anchors whose offsets are <= r.offset, they are preceded by
+    // the previous relocation whose `relocDeltas` value equals `delta`.
+    // Decrease their st_value and update their st_size.
+    if (remove) {
+      for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) {
+        if (sa[0].end)
+          sa[0].d->size = sa[0].offset - delta - sa[0].d->value;
+        else
+          sa[0].d->value -= delta;
+      }
+    }
+    delta += remove;
+    if (delta != cur) {
+      cur = delta;
+      changed = true;
+    }
+  }
+
+  for (const SymbolAnchor &a : sa) {
+    if (a.end)
+      a.d->size = a.offset - delta - a.d->value;
+    else
+      a.d->value -= delta;
+  }
+  // Inform assignAddresses that the size has changed.
+  if (!isUInt<16>(delta))
+    fatal("section size decrease is too large");
+  sec.bytesDropped = delta;
+  return changed;
+}
+
+// When relaxing just R_RISCV_ALIGN, relocDeltas is usually changed only once in
+// the absence of a linker script. For call and load/store R_RISCV_RELAX, code
+// shrinkage may reduce displacement and make more relocations eligible for
+// relaxation. Code shrinkage may increase displacement to a call/load/store
+// target at a higher fixed address, invalidating an earlier relaxation. Any
+// change in section sizes can have cascading effect and require another
+// relaxation pass.
+bool RISCV::relaxOnce(int pass) const {
+  llvm::TimeTraceScope timeScope("RISC-V relaxOnce");
+  if (config->relocatable)
+    return false;
+
+  if (pass == 0)
+    initSymbolAnchors();
+
+  SmallVector<InputSection *, 0> storage;
+  bool changed = false;
+  for (OutputSection *osec : outputSections) {
+    if (!(osec->flags & SHF_EXECINSTR))
+      continue;
+    for (InputSection *sec : getInputSections(*osec, storage))
+      changed |= relax(*sec);
+  }
+  return changed;
+}
+
+void elf::riscvFinalizeRelax(int passes) {
+  llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation");
+  log("relaxation passes: " + Twine(passes));
+  SmallVector<InputSection *, 0> storage;
+  for (OutputSection *osec : outputSections) {
+    if (!(osec->flags & SHF_EXECINSTR))
+      continue;
+    for (InputSection *sec : getInputSections(*osec, storage)) {
+      RISCVRelaxAux &aux = *sec->relaxAux;
+      if (!aux.relocDeltas)
+        continue;
+
+      auto &rels = sec->relocations;
+      ArrayRef<uint8_t> old = sec->rawData;
+      size_t newSize =
+          old.size() - aux.relocDeltas[sec->relocations.size() - 1];
+      uint8_t *p = context().bAlloc.Allocate<uint8_t>(newSize);
+      uint64_t offset = 0;
+      int64_t delta = 0;
+      sec->rawData = makeArrayRef(p, newSize);
+      sec->bytesDropped = 0;
+
+      // Update section content: remove NOPs for R_RISCV_ALIGN and rewrite
+      // instructions for relaxed relocations.
+      for (size_t i = 0, e = rels.size(); i != e; ++i) {
+        uint32_t remove = aux.relocDeltas[i] - delta;
+        delta = aux.relocDeltas[i];
+        if (remove == 0)
+          continue;
+
+        // Copy from last location to the current relocated location.
+        const Relocation &r = rels[i];
+        uint64_t size = r.offset - offset;
+        memcpy(p, old.data() + offset, size);
+        p += size;
+
+        // For R_RISCV_ALIGN, we will place `offset` in a location (among NOPs)
+        // to satisfy the alignment requirement. If `remove` is a multiple of 4,
+        // it is as if we have skipped some NOPs. Otherwise we are in the middle
+        // of a 4-byte NOP, and we need to rewrite the NOP sequence.
+        int64_t skip = 0;
+        if (r.type == R_RISCV_ALIGN) {
+          if (remove % 4 != 0) {
+            skip = r.addend - remove;
+            int64_t j = 0;
+            for (; j + 4 <= skip; j += 4)
+              write32le(p + j, 0x00000013); // nop
+            if (j != skip) {
+              assert(j + 2 == skip);
+              write16le(p + j, 0x0001); // c.nop
+            }
+          }
+        }
+
+        p += skip;
+        offset = r.offset + skip + remove;
+      }
+      memcpy(p, old.data() + offset, old.size() - offset);
+
+      // Substract the previous relocDeltas value from the relocation offset.
+      // For a pair of R_RISCV_CALL/R_RISCV_RELAX with the same offset, decrease
+      // their r_offset by the same delta.
+      delta = 0;
+      for (size_t i = 0, e = rels.size(); i != e;) {
+        uint64_t cur = rels[i].offset;
+        do {
+          rels[i].offset -= delta;
+        } while (++i != e && rels[i].offset == cur);
+        delta = aux.relocDeltas[i - 1];
+      }
+    }
+  }
+}
+
 TargetInfo *elf::getRISCVTargetInfo() {
   static RISCV target;
   return ⌖

diff  --git a/lld/ELF/InputSection.cpp b/lld/ELF/InputSection.cpp
index 97fc18b58244d..0dd17ae4bbcb3 100644
--- a/lld/ELF/InputSection.cpp
+++ b/lld/ELF/InputSection.cpp
@@ -622,6 +622,8 @@ uint64_t InputSectionBase::getRelocTargetVA(const InputFile *file, RelType type,
     return sym.getVA(a);
   case R_ADDEND:
     return a;
+  case R_RELAX_HINT:
+    return 0;
   case R_ARM_SBREL:
     return sym.getVA(a) - getARMStaticBase(sym);
   case R_GOT:
@@ -987,6 +989,8 @@ void InputSectionBase::relocateAlloc(uint8_t *buf, uint8_t *bufEnd) {
                                       *rel.sym, rel.expr),
                      bits);
     switch (rel.expr) {
+    case R_RELAX_HINT:
+      continue;
     case R_RELAX_GOT_PC:
     case R_RELAX_GOT_PC_NOPIC:
       target.relaxGot(bufLoc, rel, targetVA);

diff  --git a/lld/ELF/InputSection.h b/lld/ELF/InputSection.h
index c7c8f45f432db..d1b889750bbd4 100644
--- a/lld/ELF/InputSection.h
+++ b/lld/ELF/InputSection.h
@@ -10,7 +10,9 @@
 #define LLD_ELF_INPUT_SECTION_H
 
 #include "Relocations.h"
+#include "lld/Common/CommonLinkerContext.h"
 #include "lld/Common/LLVM.h"
+#include "lld/Common/Memory.h"
 #include "llvm/ADT/CachedHashString.h"
 #include "llvm/ADT/DenseSet.h"
 #include "llvm/ADT/TinyPtrVector.h"
@@ -97,6 +99,8 @@ class SectionBase {
         link(link), info(info) {}
 };
 
+struct RISCVRelaxAux;
+
 // This corresponds to a section of an input file.
 class InputSectionBase : public SectionBase {
 public:
@@ -129,11 +133,10 @@ class InputSectionBase : public SectionBase {
     return cast_or_null<ObjFile<ELFT>>(file);
   }
 
-  // If basic block sections are enabled, many code sections could end up with
-  // one or two jump instructions at the end that could be relaxed to a smaller
-  // instruction. The members below help trimming the trailing jump instruction
-  // and shrinking a section.
-  uint8_t bytesDropped = 0;
+  // Used by --optimize-bb-jumps and RISC-V linker relaxation temporarily to
+  // indicate the number of bytes which is not counted in the size. This should
+  // be reset to zero after uses.
+  uint16_t bytesDropped = 0;
 
   // Whether the section needs to be padded with a NOP filler due to
   // deleteFallThruJmpInsn.
@@ -201,11 +204,17 @@ class InputSectionBase : public SectionBase {
   // This vector contains such "cooked" relocations.
   SmallVector<Relocation, 0> relocations;
 
-  // These are modifiers to jump instructions that are necessary when basic
-  // block sections are enabled.  Basic block sections creates opportunities to
-  // relax jump instructions at basic block boundaries after reordering the
-  // basic blocks.
-  JumpInstrMod *jumpInstrMod = nullptr;
+  union {
+    // These are modifiers to jump instructions that are necessary when basic
+    // block sections are enabled.  Basic block sections creates opportunities
+    // to relax jump instructions at basic block boundaries after reordering the
+    // basic blocks.
+    JumpInstrMod *jumpInstrMod = nullptr;
+
+    // Auxiliary information for RISC-V linker relaxation. RISC-V does not use
+    // jumpInstrMod.
+    RISCVRelaxAux *relaxAux;
+  };
 
   // A function compiled with -fsplit-stack calling a function
   // compiled without -fsplit-stack needs its prologue adjusted. Find

diff  --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp
index 64381ae754146..e54e1ebd41bb8 100644
--- a/lld/ELF/Relocations.cpp
+++ b/lld/ELF/Relocations.cpp
@@ -958,8 +958,8 @@ bool RelocationScanner::isStaticLinkTimeConstant(RelExpr e, RelType type,
                                                  const Symbol &sym,
                                                  uint64_t relOff) const {
   // These expressions always compute a constant
-  if (oneof<R_GOTPLT, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE, R_MIPS_GOTREL,
-            R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
+  if (oneof<R_GOTPLT, R_GOT_OFF, R_RELAX_HINT, R_MIPS_GOT_LOCAL_PAGE,
+            R_MIPS_GOTREL, R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
             R_AARCH64_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC, R_GOTPLTONLY_PC,
             R_PLT_PC, R_PLT_GOTPLT, R_PPC32_PLTREL, R_PPC64_CALL_PLT,
             R_PPC64_RELAX_TOC, R_RISCV_ADD, R_AARCH64_GOT_PAGE>(e))
@@ -2118,7 +2118,9 @@ bool ThunkCreator::normalizeExistingThunk(Relocation &rel, uint64_t src) {
 // made no changes. If the target requires range extension thunks, currently
 // ARM, then any future change in offset between caller and callee risks a
 // relocation out of range error.
-bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
+bool ThunkCreator::createThunks(uint32_t pass,
+                                ArrayRef<OutputSection *> outputSections) {
+  this->pass = pass;
   bool addressesChanged = false;
 
   if (pass == 0 && target->getThunkSectionSpacing())
@@ -2180,7 +2182,6 @@ bool ThunkCreator::createThunks(ArrayRef<OutputSection *> outputSections) {
 
   // Merge all created synthetic ThunkSections back into OutputSection
   mergeThunks(outputSections);
-  ++pass;
   return addressesChanged;
 }
 

diff  --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h
index da0f2289bc907..f70d255ba229a 100644
--- a/lld/ELF/Relocations.h
+++ b/lld/ELF/Relocations.h
@@ -46,6 +46,7 @@ enum RelExpr {
   R_PLT,
   R_PLT_PC,
   R_PLT_GOTPLT,
+  R_RELAX_HINT,
   R_RELAX_GOT_PC,
   R_RELAX_GOT_PC_NOPIC,
   R_RELAX_TLS_GD_TO_IE,
@@ -139,12 +140,7 @@ class InputSectionDescription;
 class ThunkCreator {
 public:
   // Return true if Thunks have been added to OutputSections
-  bool createThunks(ArrayRef<OutputSection *> outputSections);
-
-  // The number of completed passes of createThunks this permits us
-  // to do one time initialization on Pass 0 and put a limit on the
-  // number of times it can be called to prevent infinite loops.
-  uint32_t pass = 0;
+  bool createThunks(uint32_t pass, ArrayRef<OutputSection *> outputSections);
 
 private:
   void mergeThunks(ArrayRef<OutputSection *> outputSections);
@@ -186,6 +182,11 @@ class ThunkCreator {
   // so we need to make sure that there is only one of them.
   // The Mips LA25 Thunk is an example of an inline ThunkSection.
   llvm::DenseMap<InputSection *, ThunkSection *> thunkedSections;
+
+  // The number of completed passes of createThunks this permits us
+  // to do one time initialization on Pass 0 and put a limit on the
+  // number of times it can be called to prevent infinite loops.
+  uint32_t pass = 0;
 };
 
 // Return a int64_t to make sure we get the sign extension out of the way as

diff  --git a/lld/ELF/Target.h b/lld/ELF/Target.h
index 5e9bbd62572dd..14b1f53c6a819 100644
--- a/lld/ELF/Target.h
+++ b/lld/ELF/Target.h
@@ -89,6 +89,9 @@ class TargetInfo {
     relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val);
   }
 
+  // Do a linker relaxation pass and return true if we changed something.
+  virtual bool relaxOnce(int pass) const { return false; }
+
   virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type,
                                  JumpModType val) const {}
 
@@ -221,6 +224,7 @@ void writePrefixedInstruction(uint8_t *loc, uint64_t insn);
 void addPPC64SaveRestore();
 uint64_t getPPC64TocBase();
 uint64_t getAArch64Page(uint64_t expr);
+void riscvFinalizeRelax(int passes);
 
 class AArch64Relaxer {
   bool safeToRelaxAdrpLdr = true;

diff  --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp
index 8ac6fc7cb8317..a398a7f952e91 100644
--- a/lld/ELF/Writer.cpp
+++ b/lld/ELF/Writer.cpp
@@ -1630,14 +1630,17 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
   if (config->emachine == EM_HEXAGON)
     hexagonTLSSymbolUpdate(outputSections);
 
-  int assignPasses = 0;
+  uint32_t pass = 0, assignPasses = 0;
   for (;;) {
-    bool changed = target->needsThunks && tc.createThunks(outputSections);
+    bool changed = target->needsThunks ? tc.createThunks(pass, outputSections)
+                                       : target->relaxOnce(pass);
+    ++pass;
 
     // With Thunk Size much smaller than branch range we expect to
     // converge quickly; if we get to 15 something has gone wrong.
-    if (changed && tc.pass >= 15) {
-      error("thunk creation not converged");
+    if (changed && pass >= 15) {
+      error(target->needsThunks ? "thunk creation not converged"
+                                : "relaxation not converged");
       break;
     }
 
@@ -1675,6 +1678,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
       }
     }
   }
+  if (!config->relocatable && config->emachine == EM_RISCV)
+    riscvFinalizeRelax(pass);
 
   if (config->relocatable)
     for (OutputSection *sec : outputSections)

diff  --git a/lld/test/ELF/riscv-relax-align-rvc.s b/lld/test/ELF/riscv-relax-align-rvc.s
new file mode 100644
index 0000000000000..37758ed813219
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-align-rvc.s
@@ -0,0 +1,75 @@
+# REQUIRES: riscv
+
+# RUN: rm -rf %t && mkdir %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+c,+relax %s -o 32.o
+# RUN: ld.lld -Ttext=0x10000 32.o -o 32
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32 | FileCheck %s
+## R_RISCV_ALIGN is handled regarldess of --no-relax.
+# RUN: ld.lld -Ttext=0x10000 --no-relax 32.o -o 32.norelax
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32.norelax | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+c,+relax %s -o 64.o
+# RUN: ld.lld -Ttext=0x10000 64.o -o 64
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64 | FileCheck %s
+# RUN: ld.lld -Ttext=0x10000 --no-relax 64.o -o 64.norelax
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s
+
+# CHECK-DAG: 00010002 l       .text  {{0*}}1e a
+# CHECK-DAG: 00010010 l       .text  {{0*}}22 b
+# CHECK-DAG: 00010012 l       .text  {{0*}}1e c
+# CHECK-DAG: 00010020 l       .text  {{0*}}16 d
+# CHECK-DAG: 00010000 g       .text  {{0*}}36 _start
+
+# CHECK:      <_start>:
+# CHECK-NEXT:           c.addi    a0, 1
+# CHECK-EMPTY:
+# CHECK-NEXT: <a>:
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           c.nop
+# CHECK-EMPTY:
+# CHECK-NEXT: <b>:
+# CHECK-NEXT:   10010:  c.addi  a0, 2
+# CHECK-EMPTY:
+# CHECK-NEXT: <c>:
+# CHECK-NEXT:           c.addi  a0, 3
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-EMPTY:
+# CHECK-NEXT: <d>:
+# CHECK-NEXT:   10020:  c.addi  a0, 4
+# CHECK-NEXT:           c.addi  a0, 5
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:   10030:  c.addi  a0, 6
+# CHECK-NEXT:           c.addi  a0, 7
+# CHECK-NEXT:           c.addi  a0, 8
+# CHECK-EMPTY:
+
+.global _start
+_start:
+  c.addi a0, 1
+a:
+.balign 16
+b:
+  c.addi a0, 2
+c:
+  c.addi a0, 3
+.balign 32
+.size a, . - a
+d:
+  c.addi a0, 4
+  c.addi a0, 5
+.balign 16
+.size c, . - c
+  c.addi a0, 6
+.size b, . - b
+  c.addi a0, 7
+.balign 4
+  c.addi a0, 8
+.size d, . - d
+.size _start, . - _start

diff  --git a/lld/test/ELF/riscv-relax-align.s b/lld/test/ELF/riscv-relax-align.s
new file mode 100644
index 0000000000000..8a46d601d4ef0
--- /dev/null
+++ b/lld/test/ELF/riscv-relax-align.s
@@ -0,0 +1,161 @@
+# REQUIRES: riscv
+## Test that we can handle R_RISCV_ALIGN.
+
+# RUN: rm -rf %t && mkdir %t && cd %t
+
+# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o 32.o
+# RUN: ld.lld -Ttext=0x10000 32.o -o 32
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32 | FileCheck %s
+## R_RISCV_ALIGN is handled regarldess of --no-relax.
+# RUN: ld.lld -Ttext=0x10000 --no-relax 32.o -o 32.norelax
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 32.norelax | FileCheck %s
+
+# RUN: llvm-mc -filetype=obj -triple=riscv64 -mattr=+relax %s -o 64.o
+# RUN: ld.lld -Ttext=0x10000 64.o -o 64
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64 | FileCheck %s
+# RUN: ld.lld -Ttext=0x10000 --no-relax 64.o -o 64.norelax
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64.norelax | FileCheck %s
+
+# RUN: ld.lld -Ttext=0x10000 --gc-sections 64.o -o 64.gc
+# RUN: llvm-objdump -td --no-show-raw-insn -M no-aliases 64.gc | FileCheck %s --check-prefix=GC
+
+## -r keeps section contents unchanged.
+# RUN: ld.lld -r 64.o -o 64.r
+# RUN: llvm-objdump -dr --no-show-raw-insn -M no-aliases 64.r | FileCheck %s --check-prefix=CHECKR
+
+# CHECK-DAG: 00010004 l       .text  {{0*}}1c a
+# CHECK-DAG: 00010008 l       .text  {{0*}}28 b
+# CHECK-DAG: 00010014 l       .text  {{0*}}20 c
+# CHECK-DAG: 00010000 g       .text  {{0*}}38 _start
+
+# CHECK:       <_start>:
+# CHECK-NEXT:            addi    a0, a0, 1
+# CHECK-EMPTY:
+# CHECK-NEXT:  <a>:
+# CHECK-NEXT:            addi    a0, a0, 2
+# CHECK-EMPTY:
+# CHECK-NEXT:  <b>:
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:    10010:  addi    a0, a0, 3
+# CHECK-EMPTY:
+# CHECK-NEXT:  <c>:
+# CHECK-NEXT:            addi    a0, a0, 4
+# CHECK-NEXT:            addi    a0, a0, 5
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:    10020:  addi    a0, a0, 6
+# CHECK-NEXT:            addi    a0, a0, 7
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:    10030:  addi    a0, a0, 8
+# CHECK-NEXT:            addi    a0, a0, 9
+# CHECK-EMPTY:
+# CHECK:       <e>:
+# CHECK-NEXT:            addi    a0, a0, 1
+# CHECK-EMPTY:
+# CHECK-NEXT:  <f>:
+# CHECK-NEXT:    10044:  addi    a0, a0, 2
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:            addi    zero, zero, 0
+# CHECK-NEXT:    10060:  addi    a0, a0, 3
+# CHECK-EMPTY:
+
+## _start-0x10070 = 0x10000-0x10070 = -112
+# CHECK:      <.L1>:
+# CHECK-NEXT:   10070:  auipc   a0, 0
+# CHECK-NEXT:           addi    a0, a0, -112
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           addi    zero, zero, 0
+# CHECK-NEXT:           auipc   a0, 0
+# CHECK-NEXT:           addi    a0, a0, -112
+# CHECK-EMPTY:
+
+# GC-DAG:       00010004 l       .text  {{0*}}1c a
+# GC-DAG:       00010008 l       .text  {{0*}}28 b
+# GC-DAG:       00010014 l       .text  {{0*}}20 c
+# GC-DAG:       00010000 g       .text  {{0*}}38 _start
+# GC:           <_start>:
+# GC-NOT:       <d>:
+
+# CHECKR:       <_start>:
+# CHECKR-NEXT:          addi    a0, a0, 1
+# CHECKR-EMPTY:
+# CHECKR-NEXT:  <a>:
+# CHECKR-NEXT:          addi    a0, a0, 2
+# CHECKR-EMPTY:
+# CHECKR-NEXT:  <b>:
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          0000000000000008:  R_RISCV_ALIGN        *ABS*+0xc
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    a0, a0, 3
+# CHECKR-EMPTY:
+# CHECKR-NEXT:  <c>:
+# CHECKR-NEXT:          addi    a0, a0, 4
+# CHECKR-NEXT:          addi    a0, a0, 5
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          0000000000000020:  R_RISCV_ALIGN        *ABS*+0x1c
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    a0, a0, 6
+# CHECKR-NEXT:          addi    a0, a0, 7
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          0000000000000044:  R_RISCV_ALIGN        *ABS*+0xc
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    zero, zero, 0
+# CHECKR-NEXT:          addi    a0, a0, 8
+# CHECKR-NEXT:          addi    a0, a0, 9
+
+.global _start
+_start:
+  addi a0, a0, 1
+a:
+  addi a0, a0, 2
+b:
+.balign 16
+  addi a0, a0, 3
+c:
+  addi a0, a0, 4
+  addi a0, a0, 5
+.balign 32
+.size a, . - a
+  addi a0, a0, 6
+  addi a0, a0, 7
+.balign 16
+.size b, . - b
+  addi a0, a0, 8
+.size c, . - c
+  addi a0, a0, 9
+.size _start, . - _start
+
+## Test another text section.
+.section .text2,"ax", at progbits
+d:
+e:
+.balign 8
+  addi a0, a0, 1
+f:
+  addi a0, a0, 2
+.balign 32
+.size d, . - d
+  addi a0, a0, 3
+.size e, . - e
+.size f, . - f
+
+## Test that matching HI20 can be found despite deleted bytes.
+.section .pcrel,"ax", at progbits
+.L1:
+  auipc a0, %pcrel_hi(_start)
+  addi a0, a0, %pcrel_lo(.L1)
+.balign 16
+.L2:
+  auipc a0, %pcrel_hi(_start)
+  addi a0, a0, %pcrel_lo(.L1)

diff  --git a/lld/test/ELF/riscv-reloc-align.s b/lld/test/ELF/riscv-reloc-align.s
deleted file mode 100644
index 5103066caa374..0000000000000
--- a/lld/test/ELF/riscv-reloc-align.s
+++ /dev/null
@@ -1,12 +0,0 @@
-# REQUIRES: riscv
-
-# RUN: llvm-mc -filetype=obj -triple=riscv32 -mattr=+relax %s -o %t.o
-# RUN: not ld.lld %t.o -o /dev/null 2>&1 | FileCheck %s
-
-# CHECK: relocation R_RISCV_ALIGN requires unimplemented linker relaxation
-
-.global _start
-_start:
-    nop
-    .balign 8
-    nop


        


More information about the llvm-commits mailing list