Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lld][ELF] Support relax R_LARCH_ALIGN #78692

Merged
merged 2 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
156 changes: 154 additions & 2 deletions lld/ELF/Arch/LoongArch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class LoongArch final : public TargetInfo {
bool usesOnlyLowPageBits(RelType type) const override;
void relocate(uint8_t *loc, const Relocation &rel,
uint64_t val) const override;
bool relaxOnce(int pass) const override;
void finalizeRelax(int passes) const override;
};
} // end anonymous namespace

Expand Down Expand Up @@ -465,8 +467,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
case R_LARCH_TLS_GD_HI20:
return R_TLSGD_GOT;
case R_LARCH_RELAX:
// LoongArch linker relaxation is not implemented yet.
return R_NONE;
return config->relax ? R_RELAX_HINT : R_NONE;
case R_LARCH_ALIGN:
return R_RELAX_HINT;

// Other known relocs that are explicitly unimplemented:
//
Expand Down Expand Up @@ -659,6 +662,155 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
}
}

static bool relax(InputSection &sec) {
const uint64_t secAddr = sec.getVA();
const MutableArrayRef<Relocation> relocs = sec.relocs();
auto &aux = *sec.relaxAux;
bool changed = false;
ArrayRef<SymbolAnchor> sa = ArrayRef(aux.anchors);
uint64_t delta = 0;

std::fill_n(aux.relocTypes.get(), relocs.size(), R_LARCH_NONE);
aux.writes.clear();
for (auto [i, r] : llvm::enumerate(relocs)) {
const uint64_t loc = secAddr + r.offset - delta;
uint32_t &cur = aux.relocDeltas[i], remove = 0;
switch (r.type) {
case R_LARCH_ALIGN: {
const uint64_t addend =
r.sym->isUndefined() ? Log2_64(r.addend) + 1 : r.addend;
const uint64_t allBytes = (1 << (addend & 0xff)) - 4;
const uint64_t align = 1 << (addend & 0xff);
const uint64_t maxBytes = addend >> 8;
const uint64_t off = loc & (align - 1);
const uint64_t curBytes = off == 0 ? 0 : align - off;
// All bytes beyond the alignment boundary should be removed.
// If emit bytes more than max bytes to emit, remove all.
if (maxBytes != 0 && curBytes > maxBytes)
remove = allBytes;
else
remove = allBytes - curBytes;
// If we can't satisfy this alignment, we've found a bad input.
if (LLVM_UNLIKELY(static_cast<int32_t>(remove) < 0)) {
errorOrWarn(getErrorLocation((const uint8_t *)loc) +
"insufficient padding bytes for " + lld::toString(r.type) +
": " + Twine(allBytes) + " bytes available for " +
"requested alignment of " + Twine(align) + " bytes");
remove = 0;
}
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.
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 = sa[0].offset - 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 = a.offset - delta;
}
// Inform assignAddresses that the size has changed.
if (!isUInt<32>(delta))
fatal("section size decrease is too large: " + Twine(delta));
sec.bytesDropped = delta;
return changed;
}

// When relaxing just R_LARCH_ALIGN, relocDeltas is usually changed only once in
// the absence of a linker script. For call and load/store R_LARCH_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 LoongArch::relaxOnce(int pass) const {
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 LoongArch::finalizeRelax(int passes) const {
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)) {
RelaxAux &aux = *sec->relaxAux;
if (!aux.relocDeltas)
continue;

MutableArrayRef<Relocation> rels = sec->relocs();
ArrayRef<uint8_t> old = sec->content();
size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1];
uint8_t *p = context().bAlloc.Allocate<uint8_t>(newSize);
uint64_t offset = 0;
int64_t delta = 0;
sec->content_ = p;
sec->size = newSize;
sec->bytesDropped = 0;

// Update section content: remove NOPs for R_LARCH_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 && aux.relocTypes[i] == R_LARCH_NONE)
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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RISCV.cpp has just one memcpy and it is immediately after uint64_t size = r.offset - offset;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first memcpy is copy from the last offset to current reloc offset. The second memcpy is copy from final offset the to the section end.

p += size;
offset = r.offset + remove;
}
memcpy(p, old.data() + offset, old.size() - offset);

// Subtract the previous relocDeltas value from the relocation offset.
// For a pair of R_LARCH_XXX/R_LARCH_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;
if (aux.relocTypes[i] != R_LARCH_NONE)
rels[i].type = aux.relocTypes[i];
} while (++i != e && rels[i].offset == cur);
delta = aux.relocDeltas[i - 1];
}
}
}
}

TargetInfo *elf::getLoongArchTargetInfo() {
static LoongArch target;
return &target;
Expand Down
29 changes: 5 additions & 24 deletions lld/ELF/Arch/RISCV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class RISCV final : public TargetInfo {
uint64_t val) const override;
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
bool relaxOnce(int pass) const override;
void finalizeRelax(int passes) const override;
};

} // end anonymous namespace
Expand Down Expand Up @@ -104,26 +105,6 @@ static uint32_t setLO12_S(uint32_t insn, uint32_t imm) {
(extractBits(imm, 4, 0) << 7);
}

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;
// For relocations[i], the actual type is relocTypes[i].
std::unique_ptr<RelType[]> relocTypes;
SmallVector<uint32_t, 0> writes;
};

RISCV::RISCV() {
copyRel = R_RISCV_COPY;
pltRel = R_RISCV_JUMP_SLOT;
Expand Down Expand Up @@ -695,13 +676,13 @@ void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
}
}

static void initSymbolAnchors() {
void elf::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>();
sec->relaxAux = make<RelaxAux>();
if (sec->relocs().size()) {
sec->relaxAux->relocDeltas =
std::make_unique<uint32_t[]>(sec->relocs().size());
Expand Down Expand Up @@ -948,15 +929,15 @@ bool RISCV::relaxOnce(int pass) const {
return changed;
}

void elf::riscvFinalizeRelax(int passes) {
void RISCV::finalizeRelax(int passes) const {
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;
RelaxAux &aux = *sec->relaxAux;
if (!aux.relocDeltas)
continue;

Expand Down
7 changes: 4 additions & 3 deletions lld/ELF/InputSection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -354,9 +354,10 @@ InputSectionBase *InputSection::getRelocatedSection() const {

template <class ELFT, class RelTy>
void InputSection::copyRelocations(uint8_t *buf) {
if (config->relax && !config->relocatable && config->emachine == EM_RISCV) {
// On RISC-V, relaxation might change relocations: copy from internal ones
// that are updated by relaxation.
if (config->relax && !config->relocatable &&
(config->emachine == EM_RISCV || config->emachine == EM_LOONGARCH)) {
// On LoongArch and RISC-V, relaxation might change relocations: copy
// from internal ones that are updated by relaxation.
InputSectionBase *sec = getRelocatedSection();
copyRelocations<ELFT, RelTy>(buf, llvm::make_range(sec->relocations.begin(),
sec->relocations.end()));
Expand Down
24 changes: 20 additions & 4 deletions lld/ELF/InputSection.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,23 @@ class SectionBase {
link(link), info(info) {}
};

struct RISCVRelaxAux;
struct SymbolAnchor {
uint64_t offset;
Defined *d;
bool end; // true for the anchor of st_value+st_size
};

struct RelaxAux {
// 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;
// For relocations[i], the actual type is relocTypes[i].
std::unique_ptr<RelType[]> relocTypes;
SmallVector<uint32_t, 0> writes;
};

// This corresponds to a section of an input file.
class InputSectionBase : public SectionBase {
Expand Down Expand Up @@ -227,9 +243,9 @@ class InputSectionBase : public SectionBase {
// basic blocks.
JumpInstrMod *jumpInstrMod = nullptr;

// Auxiliary information for RISC-V linker relaxation. RISC-V does not use
// jumpInstrMod.
RISCVRelaxAux *relaxAux;
// Auxiliary information for RISC-V and LoongArch linker relaxation.
// They do not use jumpInstrMod.
RelaxAux *relaxAux;

// The compressed content size when `compressed` is true.
size_t compressedSize;
Expand Down
3 changes: 3 additions & 0 deletions lld/ELF/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ class TargetInfo {

// Do a linker relaxation pass and return true if we changed something.
virtual bool relaxOnce(int pass) const { return false; }
// Do finalize relaxation after collecting relaxation infos.
virtual void finalizeRelax(int passes) const {}

virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type,
JumpModType val) const {}
Expand Down Expand Up @@ -236,6 +238,7 @@ void addArmSyntheticSectionMappingSymbol(Defined *);
void sortArmMappingSymbols();
void convertArmInstructionstoBE8(InputSection *sec, uint8_t *buf);
void createTaggedSymbols(const SmallVector<ELFFileBase *, 0> &files);
void initSymbolAnchors();

LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
TargetInfo *getTarget();
Expand Down
4 changes: 2 additions & 2 deletions lld/ELF/Writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1752,8 +1752,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
}
}
}
if (!config->relocatable && config->emachine == EM_RISCV)
riscvFinalizeRelax(pass);
if (!config->relocatable)
target->finalizeRelax(pass);

if (config->relocatable)
for (OutputSection *sec : outputSections)
Expand Down
Loading