diff --git a/lld/ELF/Arch/RISCV.cpp b/lld/ELF/Arch/RISCV.cpp --- 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 @@ 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 @@ 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,198 @@ } } +static void getSymbolAnchors() { + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec)) { + sec->relaxAux = make(); + sec->relaxAux->relocDeltas.resize(sec->relocations.size()); + } + } + // Store anchors (st_value and st_value+st_size) for symbols relative to text + // sections. + for (InputFile *file : objectFiles) + for (Symbol *sym : file->getSymbols()) { + auto *d = dyn_cast(sym); + if (!d || d->file != file) + continue; + if (auto *sec = dyn_cast_or_null(d->section)) + if (sec->flags & SHF_EXECINSTR) { + sec->relaxAux->anchors.push_back({d->value, d, false}); + sec->relaxAux->anchors.push_back({d->value + d->size, d, true}); + } + } + // Sort anchors by offset. For a zero size symbol, ensure that its start + // anchor precedes its end anchor. + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec)) { + llvm::sort(sec->relaxAux->anchors, [](auto &a, auto &b) { + return std::make_pair(a.offset, a.end) < + std::make_pair(b.offset, b.end); + }); + } + } +} + +// Do a relaxation pass and return true if we changed something. 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 { + if (config->relocatable) + return false; + + if (pass == 0) + getSymbolAnchors(); + + bool changed = false; + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec)) { + const uint64_t secAddr = sec->getVA(); + auto &aux = *sec->relaxAux; + + // Restore original st_value for symbols relative to this section. + ArrayRef sa = makeArrayRef(aux.anchors); + int32_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; + int32_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 = static_cast(nextLoc - ((loc + align - 1) & -align)); + assert(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; + } + // Tell assignAddresses that the size has changed. + sec->bytesDropped = delta; + } + } + return changed; +} + +void elf::riscvFinalizeRelax(int passes) { + llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation"); + log("relaxation passes: " + Twine(passes)); + for (OutputSection *osec : outputSections) { + if (!(osec->flags & SHF_EXECINSTR)) + continue; + for (InputSection *sec : getInputSections(*osec)) { + RISCVRelaxAux &aux = *sec->relaxAux; + if (aux.relocDeltas.empty()) + continue; + + auto &rels = sec->relocations; + ArrayRef old = sec->rawData; + size_t newSize = old.size() - aux.relocDeltas.back(); + uint8_t *p = context().bAlloc.Allocate(newSize); + uint64_t offset = 0; + int32_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) { + int32_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. + int32_t skip = 0; + if (r.type == R_RISCV_ALIGN) { + if (remove % 4 != 0) { + skip = r.addend - remove; + int32_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.h b/lld/ELF/InputSection.h --- 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,23 @@ link(link), info(info) {} }; +struct SymbolAnchor { + uint64_t offset; + Defined *d; + bool end; // true for the anchor of st_value+st_size +}; + +// Auxiliary information for RISC-V linker relaxation, attached to an +// InputSection. +struct RISCVRelaxAux { + // This records symbol start and end offsets which will be adjusted according + // to the nearest relocDeltas element. + SmallVector anchors; + // For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] : + // 0). + SmallVector relocDeltas; +}; + // This corresponds to a section of an input file. class InputSectionBase : public SectionBase { public: @@ -201,11 +220,15 @@ // This vector contains such "cooked" relocations. SmallVector 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; + + 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/InputSection.cpp b/lld/ELF/InputSection.cpp --- a/lld/ELF/InputSection.cpp +++ b/lld/ELF/InputSection.cpp @@ -622,6 +622,8 @@ 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 @@ *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/LinkerScript.h b/lld/ELF/LinkerScript.h --- a/lld/ELF/LinkerScript.h +++ b/lld/ELF/LinkerScript.h @@ -289,7 +289,7 @@ std::pair findMemoryRegion(OutputSection *sec, MemoryRegion *hint); - void assignOffsets(OutputSection *sec); + bool assignOffsets(OutputSection *sec); // Ctx captures the local AddressState and makes it accessible // deliberately. This is needed as there are some cases where we cannot just @@ -322,7 +322,7 @@ bool needsInterpSection(); bool shouldKeep(InputSectionBase *s); - const Defined *assignAddresses(); + std::pair assignAddresses(); void allocateHeaders(SmallVector &phdrs); void processSectionCommands(); void processSymbolAssignments(); diff --git a/lld/ELF/LinkerScript.cpp b/lld/ELF/LinkerScript.cpp --- a/lld/ELF/LinkerScript.cpp +++ b/lld/ELF/LinkerScript.cpp @@ -948,11 +948,12 @@ // This function assigns offsets to input sections and an output section // for a single sections command (e.g. ".text { *(.text); }"). -void LinkerScript::assignOffsets(OutputSection *sec) { +bool LinkerScript::assignOffsets(OutputSection *sec) { const bool isTbss = (sec->flags & SHF_TLS) && sec->type == SHT_NOBITS; const bool sameMemRegion = ctx->memRegion == sec->memRegion; const bool prevLMARegionIsDefault = ctx->lmaRegion == nullptr; const uint64_t savedDot = dot; + bool changed = false; ctx->memRegion = sec->memRegion; ctx->lmaRegion = sec->lmaRegion; @@ -1047,7 +1048,10 @@ assert(isec->getParent() == sec); const uint64_t pos = dot; dot = alignTo(dot, isec->alignment); - isec->outSecOff = dot - sec->addr; + if (dot - sec->addr != isec->outSecOff) { + isec->outSecOff = dot - sec->addr; + changed = true; + } dot += isec->getSize(); // Update output section size after adding each section. This is so that @@ -1066,6 +1070,7 @@ ctx->tbssAddr = dot; dot = savedDot; } + return changed; } static bool isDiscardable(const OutputSection &sec) { @@ -1287,11 +1292,11 @@ } // Here we assign addresses as instructed by linker script SECTIONS -// sub-commands. Doing that allows us to use final VA values, so here -// we also handle rest commands like symbol assignments and ASSERTs. -// Returns a symbol that has changed its section or value, or nullptr if no -// symbol has changed. -const Defined *LinkerScript::assignAddresses() { +// sub-commands. Doing that allows us to use final VA values, so here we also +// handle rest commands like symbol assignments and ASSERTs. Return a pair. +// First is whether any input section has changed outSecOff. Second is a symbol +// that has changed its section or value, or nullptr if no symbol has changed. +std::pair LinkerScript::assignAddresses() { if (script->hasSectionsCommand) { // With a linker script, assignment of addresses to headers is covered by // allocateHeaders(). @@ -1309,6 +1314,7 @@ errorOnMissingSection = true; ctx->outSec = aether; + bool addrChanged = false; SymbolAssignmentMap oldValues = getSymbolAssignmentValues(sectionCommands); for (SectionCommand *cmd : sectionCommands) { if (auto *assign = dyn_cast(cmd)) { @@ -1317,11 +1323,11 @@ assign->size = dot - assign->addr; continue; } - assignOffsets(&cast(cmd)->osec); + addrChanged |= assignOffsets(&cast(cmd)->osec); } ctx = nullptr; - return getChangedSymbolAssignment(oldValues); + return {addrChanged, getChangedSymbolAssignment(oldValues)}; } // Creates program headers as instructed by PHDRS linker script command. diff --git a/lld/ELF/Relocations.h b/lld/ELF/Relocations.h --- a/lld/ELF/Relocations.h +++ b/lld/ELF/Relocations.h @@ -46,6 +46,7 @@ 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, diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -956,8 +956,8 @@ const Symbol &sym, uint64_t relOff) const { // These expressions always compute a constant - if (oneof(e)) @@ -2178,7 +2178,6 @@ // Merge all created synthetic ThunkSections back into OutputSection mergeThunks(outputSections); - ++pass; return addressesChanged; } diff --git a/lld/ELF/Target.h b/lld/ELF/Target.h --- a/lld/ELF/Target.h +++ b/lld/ELF/Target.h @@ -89,6 +89,8 @@ relocate(loc, Relocation{R_NONE, type, 0, 0, nullptr}, val); } + virtual bool relaxOnce(int pass) const { return false; } + virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type, JumpModType val) const {} @@ -221,6 +223,7 @@ 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 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1632,12 +1632,15 @@ int assignPasses = 0; for (;;) { - bool changed = target->needsThunks && tc.createThunks(outputSections); + bool changed = target->needsThunks ? tc.createThunks(outputSections) + : target->relaxOnce(tc.pass); + ++tc.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"); + error(target->needsThunks ? "thunk creation not converged" + : "relaxation not converged"); break; } @@ -1661,11 +1664,13 @@ changed |= part.relrDyn->updateAllocSize(); } - const Defined *changedSym = script->assignAddresses(); + auto update = script->assignAddresses(); + changed |= update.first; if (!changed) { // Some symbols may be dependent on section addresses. When we break the // loop, the symbol values are finalized because a previous // assignAddresses() finalized section addresses. + const Defined *changedSym = update.second; if (!changedSym) break; if (++assignPasses == 5) { @@ -1675,6 +1680,8 @@ } } } + if (!config->relocatable && config->emachine == EM_RISCV) + riscvFinalizeRelax(tc.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 --- /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: : +# 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: : +# CHECK-NEXT: 10010: c.addi a0, 2 +# CHECK-EMPTY: +# CHECK-NEXT: : +# 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: : +# 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 --- /dev/null +++ b/lld/test/ELF/riscv-relax-align.s @@ -0,0 +1,151 @@ +# 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 + +## -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: : +# CHECK-NEXT: addi a0, a0, 2 +# CHECK-EMPTY: +# CHECK-NEXT: : +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: addi zero, zero, 0 +# CHECK-NEXT: 10010: addi a0, a0, 3 +# CHECK-EMPTY: +# CHECK-NEXT: : +# 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: : +# CHECK-NEXT: addi a0, a0, 1 +# CHECK-EMPTY: +# CHECK-NEXT: : +# 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: + +# CHECKR: <_start>: +# CHECKR-NEXT: addi a0, a0, 1 +# CHECKR-EMPTY: +# CHECKR-NEXT: : +# CHECKR-NEXT: addi a0, a0, 2 +# CHECKR-EMPTY: +# CHECKR-NEXT: : +# 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: : +# 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",@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",@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 --- 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