[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
This commit is contained in:
parent
ef7aed3e11
commit
6611d58f5b
|
@ -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 @@ public:
|
|||
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 ⌖
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 @@ protected:
|
|||
link(link), info(info) {}
|
||||
};
|
||||
|
||||
struct RISCVRelaxAux;
|
||||
|
||||
// This corresponds to a section of an input file.
|
||||
class InputSectionBase : public SectionBase {
|
||||
public:
|
||||
|
@ -129,11 +133,10 @@ public:
|
|||
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 @@ public:
|
|||
// 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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 @@ private:
|
|||
// 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
|
||||
|
|
|
@ -89,6 +89,9 @@ public:
|
|||
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;
|
||||
|
|
|
@ -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)
|
||||
|
|
75
lld/test/ELF/riscv-relax-align-rvc.s
Normal file
75
lld/test/ELF/riscv-relax-align-rvc.s
Normal file
|
@ -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
|
161
lld/test/ELF/riscv-relax-align.s
Normal file
161
lld/test/ELF/riscv-relax-align.s
Normal file
|
@ -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",@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)
|
|
@ -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
|
Loading…
Reference in a new issue