Skip to content

Commit 80c56e8

Browse files
MQ-mengqingAmi-zhang
authored andcommitted
[lld][ELF] Support relax R_LARCH_ALIGN (llvm#78692)
Refer to commit 6611d58 ("Relax R_RISCV_ALIGN"), we can relax R_LARCH_ALIGN by same way. Reuse `SymbolAnchor`, `RISCVRelaxAux` and `initSymbolAnchors` to simplify codes. As `riscvFinalizeRelax` is an arch-specific function, put it override on `TargetInfo::finalizeRelax`, so that LoongArch can override it, too. The flow of relax R_LARCH_ALIGN is almost consistent with RISCV. The difference is that LoongArch only has 4-bytes NOP and all executable insn is 4-bytes aligned. So LoongArch not need rewrite NOP sequence. Alignment maxBytesEmit parameter is supported in psABI v2.30. (cherry picked from commit 06a728f) (cherry picked from commit 60a8ec3) Change-Id: I680e9a44f05fb2cc820736eee63ddd999e689daf
1 parent d53182c commit 80c56e8

File tree

8 files changed

+362
-33
lines changed

8 files changed

+362
-33
lines changed

lld/ELF/Arch/LoongArch.cpp

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ class LoongArch final : public TargetInfo {
3636
bool usesOnlyLowPageBits(RelType type) const override;
3737
void relocate(uint8_t *loc, const Relocation &rel,
3838
uint64_t val) const override;
39+
bool relaxOnce(int pass) const override;
40+
void finalizeRelax(int passes) const override;
3941
};
4042
} // end anonymous namespace
4143

@@ -521,8 +523,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
521523
case R_LARCH_TLS_GD_HI20:
522524
return R_TLSGD_GOT;
523525
case R_LARCH_RELAX:
524-
// LoongArch linker relaxation is not implemented yet.
525-
return R_NONE;
526+
return config->relax ? R_RELAX_HINT : R_NONE;
527+
case R_LARCH_ALIGN:
528+
return R_RELAX_HINT;
526529

527530
// Other known relocs that are explicitly unimplemented:
528531
//
@@ -696,6 +699,155 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
696699
}
697700
}
698701

702+
static bool relax(InputSection &sec) {
703+
const uint64_t secAddr = sec.getVA();
704+
const MutableArrayRef<Relocation> relocs = sec.relocs();
705+
auto &aux = *sec.relaxAux;
706+
bool changed = false;
707+
ArrayRef<SymbolAnchor> sa = ArrayRef(aux.anchors);
708+
uint64_t delta = 0;
709+
710+
std::fill_n(aux.relocTypes.get(), relocs.size(), R_LARCH_NONE);
711+
aux.writes.clear();
712+
for (auto [i, r] : llvm::enumerate(relocs)) {
713+
const uint64_t loc = secAddr + r.offset - delta;
714+
uint32_t &cur = aux.relocDeltas[i], remove = 0;
715+
switch (r.type) {
716+
case R_LARCH_ALIGN: {
717+
const uint64_t addend =
718+
r.sym->isUndefined() ? Log2_64(r.addend) + 1 : r.addend;
719+
const uint64_t allBytes = (1 << (addend & 0xff)) - 4;
720+
const uint64_t align = 1 << (addend & 0xff);
721+
const uint64_t maxBytes = addend >> 8;
722+
const uint64_t off = loc & (align - 1);
723+
const uint64_t curBytes = off == 0 ? 0 : align - off;
724+
// All bytes beyond the alignment boundary should be removed.
725+
// If emit bytes more than max bytes to emit, remove all.
726+
if (maxBytes != 0 && curBytes > maxBytes)
727+
remove = allBytes;
728+
else
729+
remove = allBytes - curBytes;
730+
// If we can't satisfy this alignment, we've found a bad input.
731+
if (LLVM_UNLIKELY(static_cast<int32_t>(remove) < 0)) {
732+
errorOrWarn(getErrorLocation((const uint8_t *)loc) +
733+
"insufficient padding bytes for " + lld::toString(r.type) +
734+
": " + Twine(allBytes) + " bytes available for " +
735+
"requested alignment of " + Twine(align) + " bytes");
736+
remove = 0;
737+
}
738+
break;
739+
}
740+
}
741+
742+
// For all anchors whose offsets are <= r.offset, they are preceded by
743+
// the previous relocation whose `relocDeltas` value equals `delta`.
744+
// Decrease their st_value and update their st_size.
745+
for (; sa.size() && sa[0].offset <= r.offset; sa = sa.slice(1)) {
746+
if (sa[0].end)
747+
sa[0].d->size = sa[0].offset - delta - sa[0].d->value;
748+
else
749+
sa[0].d->value = sa[0].offset - delta;
750+
}
751+
delta += remove;
752+
if (delta != cur) {
753+
cur = delta;
754+
changed = true;
755+
}
756+
}
757+
758+
for (const SymbolAnchor &a : sa) {
759+
if (a.end)
760+
a.d->size = a.offset - delta - a.d->value;
761+
else
762+
a.d->value = a.offset - delta;
763+
}
764+
// Inform assignAddresses that the size has changed.
765+
if (!isUInt<32>(delta))
766+
fatal("section size decrease is too large: " + Twine(delta));
767+
sec.bytesDropped = delta;
768+
return changed;
769+
}
770+
771+
// When relaxing just R_LARCH_ALIGN, relocDeltas is usually changed only once in
772+
// the absence of a linker script. For call and load/store R_LARCH_RELAX, code
773+
// shrinkage may reduce displacement and make more relocations eligible for
774+
// relaxation. Code shrinkage may increase displacement to a call/load/store
775+
// target at a higher fixed address, invalidating an earlier relaxation. Any
776+
// change in section sizes can have cascading effect and require another
777+
// relaxation pass.
778+
bool LoongArch::relaxOnce(int pass) const {
779+
if (config->relocatable)
780+
return false;
781+
782+
if (pass == 0)
783+
initSymbolAnchors();
784+
785+
SmallVector<InputSection *, 0> storage;
786+
bool changed = false;
787+
for (OutputSection *osec : outputSections) {
788+
if (!(osec->flags & SHF_EXECINSTR))
789+
continue;
790+
for (InputSection *sec : getInputSections(*osec, storage))
791+
changed |= relax(*sec);
792+
}
793+
return changed;
794+
}
795+
796+
void LoongArch::finalizeRelax(int passes) const {
797+
log("relaxation passes: " + Twine(passes));
798+
SmallVector<InputSection *, 0> storage;
799+
for (OutputSection *osec : outputSections) {
800+
if (!(osec->flags & SHF_EXECINSTR))
801+
continue;
802+
for (InputSection *sec : getInputSections(*osec, storage)) {
803+
RelaxAux &aux = *sec->relaxAux;
804+
if (!aux.relocDeltas)
805+
continue;
806+
807+
MutableArrayRef<Relocation> rels = sec->relocs();
808+
ArrayRef<uint8_t> old = sec->content();
809+
size_t newSize = old.size() - aux.relocDeltas[rels.size() - 1];
810+
uint8_t *p = context().bAlloc.Allocate<uint8_t>(newSize);
811+
uint64_t offset = 0;
812+
int64_t delta = 0;
813+
sec->content_ = p;
814+
sec->size = newSize;
815+
sec->bytesDropped = 0;
816+
817+
// Update section content: remove NOPs for R_LARCH_ALIGN and rewrite
818+
// instructions for relaxed relocations.
819+
for (size_t i = 0, e = rels.size(); i != e; ++i) {
820+
uint32_t remove = aux.relocDeltas[i] - delta;
821+
delta = aux.relocDeltas[i];
822+
if (remove == 0 && aux.relocTypes[i] == R_LARCH_NONE)
823+
continue;
824+
825+
// Copy from last location to the current relocated location.
826+
const Relocation &r = rels[i];
827+
uint64_t size = r.offset - offset;
828+
memcpy(p, old.data() + offset, size);
829+
p += size;
830+
offset = r.offset + remove;
831+
}
832+
memcpy(p, old.data() + offset, old.size() - offset);
833+
834+
// Subtract the previous relocDeltas value from the relocation offset.
835+
// For a pair of R_LARCH_XXX/R_LARCH_RELAX with the same offset, decrease
836+
// their r_offset by the same delta.
837+
delta = 0;
838+
for (size_t i = 0, e = rels.size(); i != e;) {
839+
uint64_t cur = rels[i].offset;
840+
do {
841+
rels[i].offset -= delta;
842+
if (aux.relocTypes[i] != R_LARCH_NONE)
843+
rels[i].type = aux.relocTypes[i];
844+
} while (++i != e && rels[i].offset == cur);
845+
delta = aux.relocDeltas[i - 1];
846+
}
847+
}
848+
}
849+
}
850+
699851
TargetInfo *elf::getLoongArchTargetInfo() {
700852
static LoongArch target;
701853
return &target;

lld/ELF/Arch/RISCV.cpp

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class RISCV final : public TargetInfo {
4444
void relocate(uint8_t *loc, const Relocation &rel,
4545
uint64_t val) const override;
4646
bool relaxOnce(int pass) const override;
47+
void finalizeRelax(int passes) const override;
4748
};
4849

4950
} // end anonymous namespace
@@ -513,33 +514,14 @@ void RISCV::relocate(uint8_t *loc, const Relocation &rel, uint64_t val) const {
513514
}
514515
}
515516

516-
namespace {
517-
struct SymbolAnchor {
518-
uint64_t offset;
519-
Defined *d;
520-
bool end; // true for the anchor of st_value+st_size
521-
};
522-
} // namespace
523-
524-
struct elf::RISCVRelaxAux {
525-
// This records symbol start and end offsets which will be adjusted according
526-
// to the nearest relocDeltas element.
527-
SmallVector<SymbolAnchor, 0> anchors;
528-
// For relocations[i], the actual offset is r_offset - (i ? relocDeltas[i-1] :
529-
// 0).
530-
std::unique_ptr<uint32_t[]> relocDeltas;
531-
// For relocations[i], the actual type is relocTypes[i].
532-
std::unique_ptr<RelType[]> relocTypes;
533-
SmallVector<uint32_t, 0> writes;
534-
};
535517

536-
static void initSymbolAnchors() {
518+
void elf::initSymbolAnchors() {
537519
SmallVector<InputSection *, 0> storage;
538520
for (OutputSection *osec : outputSections) {
539521
if (!(osec->flags & SHF_EXECINSTR))
540522
continue;
541523
for (InputSection *sec : getInputSections(*osec, storage)) {
542-
sec->relaxAux = make<RISCVRelaxAux>();
524+
sec->relaxAux = make<RelaxAux>();
543525
if (sec->relocs().size()) {
544526
sec->relaxAux->relocDeltas =
545527
std::make_unique<uint32_t[]>(sec->relocs().size());
@@ -766,15 +748,15 @@ bool RISCV::relaxOnce(int pass) const {
766748
return changed;
767749
}
768750

769-
void elf::riscvFinalizeRelax(int passes) {
751+
void RISCV::finalizeRelax(int passes) const {
770752
llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation");
771753
log("relaxation passes: " + Twine(passes));
772754
SmallVector<InputSection *, 0> storage;
773755
for (OutputSection *osec : outputSections) {
774756
if (!(osec->flags & SHF_EXECINSTR))
775757
continue;
776758
for (InputSection *sec : getInputSections(*osec, storage)) {
777-
RISCVRelaxAux &aux = *sec->relaxAux;
759+
RelaxAux &aux = *sec->relaxAux;
778760
if (!aux.relocDeltas)
779761
continue;
780762

lld/ELF/InputSection.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,8 +351,9 @@ InputSectionBase *InputSection::getRelocatedSection() const {
351351

352352
template <class ELFT, class RelTy>
353353
void InputSection::copyRelocations(uint8_t *buf) {
354-
if (config->relax && !config->relocatable && config->emachine == EM_RISCV) {
355-
// On RISC-V, relaxation might change relocations: copy from
354+
if (config->relax && !config->relocatable &&
355+
(config->emachine == EM_RISCV || config->emachine == EM_LOONGARCH)) {
356+
// On LoongArch and RISC-V, relaxation might change relocations: copy from
356357
// internal ones that are updated by relaxation.
357358
InputSectionBase *sec = getRelocatedSection();
358359
copyRelocations<ELFT, RelTy>(buf, llvm::make_range(sec->relocations.begin(),

lld/ELF/InputSection.h

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,23 @@ class SectionBase {
101101
link(link), info(info) {}
102102
};
103103

104-
struct RISCVRelaxAux;
104+
struct SymbolAnchor {
105+
uint64_t offset;
106+
Defined *d;
107+
bool end; // true for the anchor of st_value+st_size
108+
};
109+
110+
struct RelaxAux {
111+
// This records symbol start and end offsets which will be adjusted according
112+
// to the nearest relocDeltas element.
113+
SmallVector<SymbolAnchor, 0> anchors;
114+
// For relocations[i], the actual offset is
115+
// r_offset - (i ? relocDeltas[i-1] : 0).
116+
std::unique_ptr<uint32_t[]> relocDeltas;
117+
// For relocations[i], the actual type is relocTypes[i].
118+
std::unique_ptr<RelType[]> relocTypes;
119+
SmallVector<uint32_t, 0> writes;
120+
};
105121

106122
// This corresponds to a section of an input file.
107123
class InputSectionBase : public SectionBase {
@@ -222,9 +238,9 @@ class InputSectionBase : public SectionBase {
222238
// basic blocks.
223239
JumpInstrMod *jumpInstrMod = nullptr;
224240

225-
// Auxiliary information for RISC-V linker relaxation. RISC-V does not use
226-
// jumpInstrMod.
227-
RISCVRelaxAux *relaxAux;
241+
// Auxiliary information for RISC-V and LoongArch linker relaxation.
242+
// They do not use jumpInstrMod.
243+
RelaxAux *relaxAux;
228244

229245
// The compressed content size when `compressed` is true.
230246
size_t compressedSize;

lld/ELF/Target.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class TargetInfo {
9494

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

98100
virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type,
99101
JumpModType val) const {}
@@ -234,6 +236,7 @@ void addArmInputSectionMappingSymbols();
234236
void addArmSyntheticSectionMappingSymbol(Defined *);
235237
void sortArmMappingSymbols();
236238
void convertArmInstructionstoBE8(InputSection *sec, uint8_t *buf);
239+
void initSymbolAnchors();
237240

238241
LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
239242
TargetInfo *getTarget();

lld/ELF/Writer.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,8 +1668,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
16681668
}
16691669
}
16701670
}
1671-
if (!config->relocatable && config->emachine == EM_RISCV)
1672-
riscvFinalizeRelax(pass);
1671+
if (!config->relocatable)
1672+
target->finalizeRelax(pass);
16731673

16741674
if (config->relocatable)
16751675
for (OutputSection *sec : outputSections)

0 commit comments

Comments
 (0)