Skip to content

Commit 6460625

Browse files
committed
[lld][ELF] Support relax R_LARCH_ALIGN
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 2abcbbd commit 6460625

File tree

7 files changed

+314
-33
lines changed

7 files changed

+314
-33
lines changed

lld/ELF/Arch/LoongArch.cpp

Lines changed: 154 additions & 0 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

@@ -467,6 +469,8 @@ RelExpr LoongArch::getRelExpr(const RelType type, const Symbol &s,
467469
case R_LARCH_RELAX:
468470
// LoongArch linker relaxation is not implemented yet.
469471
return R_NONE;
472+
case R_LARCH_ALIGN:
473+
return R_RELAX_HINT;
470474

471475
// Other known relocs that are explicitly unimplemented:
472476
//
@@ -659,6 +663,156 @@ void LoongArch::relocate(uint8_t *loc, const Relocation &rel,
659663
}
660664
}
661665

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

lld/ELF/Arch/RISCV.cpp

Lines changed: 5 additions & 24 deletions
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

Lines changed: 4 additions & 3 deletions
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

Lines changed: 20 additions & 4 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 2 additions & 2 deletions
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)