Skip to content

Commit 06a728f

Browse files
authored
[lld][ELF] Support relax R_LARCH_ALIGN (#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.
1 parent a71147d commit 06a728f

8 files changed

+363
-35
lines changed

lld/ELF/Arch/LoongArch.cpp

+154-2
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

@@ -465,8 +467,9 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
465467
case R_LARCH_TLS_GD_HI20:
466468
return R_TLSGD_GOT;
467469
case R_LARCH_RELAX:
468-
// LoongArch linker relaxation is not implemented yet.
469-
return R_NONE;
470+
return config->relax ? R_RELAX_HINT : R_NONE;
471+
case R_LARCH_ALIGN:
472+
return R_RELAX_HINT;
470473

471474
// Other known relocs that are explicitly unimplemented:
472475
//
@@ -659,6 +662,155 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
659662
}
660663
}
661664

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

lld/ELF/Arch/RISCV.cpp

+5-24
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class RISCV final : public TargetInfo {
4545
uint64_t val) const override;
4646
void relocateAlloc(InputSectionBase &sec, uint8_t *buf) const override;
4747
bool relaxOnce(int pass) const override;
48+
void finalizeRelax(int passes) const override;
4849
};
4950

5051
} // end anonymous namespace
@@ -104,26 +105,6 @@ static uint32_t setLO12_S(uint32_t insn, uint32_t imm) {
104105
(extractBits(imm, 4, 0) << 7);
105106
}
106107

107-
namespace {
108-
struct SymbolAnchor {
109-
uint64_t offset;
110-
Defined *d;
111-
bool end; // true for the anchor of st_value+st_size
112-
};
113-
} // namespace
114-
115-
struct elf::RISCVRelaxAux {
116-
// This records symbol start and end offsets which will be adjusted according
117-
// to the nearest relocDeltas element.
118-
SmallVector<SymbolAnchor, 0> anchors;
119-
// For relocations[i], the actual offset is
120-
// r_offset - (i ? relocDeltas[i-1] : 0).
121-
std::unique_ptr<uint32_t[]> relocDeltas;
122-
// For relocations[i], the actual type is relocTypes[i].
123-
std::unique_ptr<RelType[]> relocTypes;
124-
SmallVector<uint32_t, 0> writes;
125-
};
126-
127108
RISCV::RISCV() {
128109
copyRel = R_RISCV_COPY;
129110
pltRel = R_RISCV_JUMP_SLOT;
@@ -695,13 +676,13 @@ void RISCV::relocateAlloc(InputSectionBase &sec, uint8_t *buf) const {
695676
}
696677
}
697678

698-
static void initSymbolAnchors() {
679+
void elf::initSymbolAnchors() {
699680
SmallVector<InputSection *, 0> storage;
700681
for (OutputSection *osec : outputSections) {
701682
if (!(osec->flags & SHF_EXECINSTR))
702683
continue;
703684
for (InputSection *sec : getInputSections(*osec, storage)) {
704-
sec->relaxAux = make<RISCVRelaxAux>();
685+
sec->relaxAux = make<RelaxAux>();
705686
if (sec->relocs().size()) {
706687
sec->relaxAux->relocDeltas =
707688
std::make_unique<uint32_t[]>(sec->relocs().size());
@@ -948,15 +929,15 @@ bool RISCV::relaxOnce(int pass) const {
948929
return changed;
949930
}
950931

951-
void elf::riscvFinalizeRelax(int passes) {
932+
void RISCV::finalizeRelax(int passes) const {
952933
llvm::TimeTraceScope timeScope("Finalize RISC-V relaxation");
953934
log("relaxation passes: " + Twine(passes));
954935
SmallVector<InputSection *, 0> storage;
955936
for (OutputSection *osec : outputSections) {
956937
if (!(osec->flags & SHF_EXECINSTR))
957938
continue;
958939
for (InputSection *sec : getInputSections(*osec, storage)) {
959-
RISCVRelaxAux &aux = *sec->relaxAux;
940+
RelaxAux &aux = *sec->relaxAux;
960941
if (!aux.relocDeltas)
961942
continue;
962943

lld/ELF/InputSection.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -354,9 +354,10 @@ InputSectionBase *InputSection::getRelocatedSection() const {
354354

355355
template <class ELFT, class RelTy>
356356
void InputSection::copyRelocations(uint8_t *buf) {
357-
if (config->relax && !config->relocatable && config->emachine == EM_RISCV) {
358-
// On RISC-V, relaxation might change relocations: copy from internal ones
359-
// that are updated by relaxation.
357+
if (config->relax && !config->relocatable &&
358+
(config->emachine == EM_RISCV || config->emachine == EM_LOONGARCH)) {
359+
// On LoongArch and RISC-V, relaxation might change relocations: copy
360+
// from internal ones that are updated by relaxation.
360361
InputSectionBase *sec = getRelocatedSection();
361362
copyRelocations<ELFT, RelTy>(buf, llvm::make_range(sec->relocations.begin(),
362363
sec->relocations.end()));

lld/ELF/InputSection.h

+20-4
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,23 @@ class SectionBase {
102102
link(link), info(info) {}
103103
};
104104

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

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

230-
// Auxiliary information for RISC-V linker relaxation. RISC-V does not use
231-
// jumpInstrMod.
232-
RISCVRelaxAux *relaxAux;
246+
// Auxiliary information for RISC-V and LoongArch linker relaxation.
247+
// They do not use jumpInstrMod.
248+
RelaxAux *relaxAux;
233249

234250
// The compressed content size when `compressed` is true.
235251
size_t compressedSize;

lld/ELF/Target.h

+3
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ class TargetInfo {
9595

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

99101
virtual void applyJumpInstrMod(uint8_t *loc, JumpModType type,
100102
JumpModType val) const {}
@@ -236,6 +238,7 @@ void addArmSyntheticSectionMappingSymbol(Defined *);
236238
void sortArmMappingSymbols();
237239
void convertArmInstructionstoBE8(InputSection *sec, uint8_t *buf);
238240
void createTaggedSymbols(const SmallVector<ELFFileBase *, 0> &files);
241+
void initSymbolAnchors();
239242

240243
LLVM_LIBRARY_VISIBILITY extern const TargetInfo *target;
241244
TargetInfo *getTarget();

lld/ELF/Writer.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -1752,8 +1752,8 @@ template <class ELFT> void Writer<ELFT>::finalizeAddressDependentContent() {
17521752
}
17531753
}
17541754
}
1755-
if (!config->relocatable && config->emachine == EM_RISCV)
1756-
riscvFinalizeRelax(pass);
1755+
if (!config->relocatable)
1756+
target->finalizeRelax(pass);
17571757

17581758
if (config->relocatable)
17591759
for (OutputSection *sec : outputSections)

0 commit comments

Comments
 (0)