Skip to content

Commit 9dd5aed

Browse files
authored
[JITLink][LoongArch] Support R_LARCH_ALIGN relaxation (llvm#122259)
Linker relaxation is not implemented for jitlink now. But if relaxation is enabled by clang, R_LARCH_RELAX and R_LARCH_ALIGN relocations will be emitted. This commit adapts lld's algorithm to jitlink. Currently, only relaxing R_LARCH_ALIGN is implemented. Other relaxable relocs can be implemented in the future. Without this, interpreting C++ code using clang-repl or running ir using lli when relaxation is enabled will occur error: `JIT session error: Unsupported loongarch relocation:102: R_LARCH_ALIGN`. Similar to llvm@310473c but only implement align.
1 parent a9c61e0 commit 9dd5aed

File tree

5 files changed

+373
-6
lines changed

5 files changed

+373
-6
lines changed

llvm/include/llvm/ExecutionEngine/JITLink/ELF_loongarch.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ Expected<std::unique_ptr<LinkGraph>> createLinkGraphFromELFObject_loongarch(
3333
void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
3434
std::unique_ptr<JITLinkContext> Ctx);
3535

36+
/// Returns a pass that performs linker relaxation. Should be added to
37+
/// PostAllocationPasses.
38+
LinkGraphPassFunction createRelaxationPass_ELF_loongarch();
39+
3640
} // end namespace jitlink
3741
} // end namespace llvm
3842

llvm/include/llvm/ExecutionEngine/JITLink/loongarch.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,13 @@ enum EdgeKind_loongarch : Edge::Kind {
225225
/// out-of-range error will be returned.
226226
///
227227
Call36PCRel,
228+
229+
/// Alignment requirement used by linker relaxation.
230+
///
231+
/// Linker relaxation will use this to ensure all code sequences are properly
232+
/// aligned and then remove these edges from the graph.
233+
///
234+
AlignRelaxable,
228235
};
229236

230237
/// Returns a string name for the given loongarch edge. For debugging purposes
@@ -362,6 +369,9 @@ inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
362369
*(little32_t *)(FixupPtr + 4) = Jirl | Lo16;
363370
break;
364371
}
372+
case AlignRelaxable:
373+
// Ignore when the relaxation pass did not run
374+
break;
365375
default:
366376
return make_error<JITLinkError>(
367377
"In graph " + G.getName() + ", section " + B.getSection().getName() +

llvm/lib/ExecutionEngine/JITLink/ELF_loongarch.cpp

Lines changed: 263 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,238 @@ class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
4545
}
4646
};
4747

48+
namespace {
49+
50+
struct SymbolAnchor {
51+
uint64_t Offset;
52+
Symbol *Sym;
53+
bool End; // true for the anchor of getOffset() + getSize()
54+
};
55+
56+
struct BlockRelaxAux {
57+
// This records symbol start and end offsets which will be adjusted according
58+
// to the nearest RelocDeltas element.
59+
SmallVector<SymbolAnchor, 0> Anchors;
60+
// All edges that either 1) are R_LARCH_ALIGN or 2) have a R_LARCH_RELAX edge
61+
// at the same offset.
62+
SmallVector<Edge *, 0> RelaxEdges;
63+
// For RelaxEdges[I], the actual offset is RelaxEdges[I]->getOffset() - (I ?
64+
// RelocDeltas[I - 1] : 0).
65+
SmallVector<uint32_t, 0> RelocDeltas;
66+
// For RelaxEdges[I], the actual type is EdgeKinds[I].
67+
SmallVector<Edge::Kind, 0> EdgeKinds;
68+
// List of rewritten instructions. Contains one raw encoded instruction per
69+
// element in EdgeKinds that isn't Invalid or R_LARCH_ALIGN.
70+
SmallVector<uint32_t, 0> Writes;
71+
};
72+
73+
struct RelaxAux {
74+
DenseMap<Block *, BlockRelaxAux> Blocks;
75+
};
76+
77+
} // namespace
78+
79+
static bool shouldRelax(const Section &S) {
80+
return (S.getMemProt() & orc::MemProt::Exec) != orc::MemProt::None;
81+
}
82+
83+
static bool isRelaxable(const Edge &E) {
84+
switch (E.getKind()) {
85+
default:
86+
return false;
87+
case AlignRelaxable:
88+
return true;
89+
}
90+
}
91+
92+
static RelaxAux initRelaxAux(LinkGraph &G) {
93+
RelaxAux Aux;
94+
for (auto &S : G.sections()) {
95+
if (!shouldRelax(S))
96+
continue;
97+
for (auto *B : S.blocks()) {
98+
auto BlockEmplaceResult = Aux.Blocks.try_emplace(B);
99+
assert(BlockEmplaceResult.second && "Block encountered twice");
100+
auto &BlockAux = BlockEmplaceResult.first->second;
101+
102+
for (auto &E : B->edges())
103+
if (isRelaxable(E))
104+
BlockAux.RelaxEdges.push_back(&E);
105+
106+
if (BlockAux.RelaxEdges.empty()) {
107+
Aux.Blocks.erase(BlockEmplaceResult.first);
108+
continue;
109+
}
110+
111+
const auto NumEdges = BlockAux.RelaxEdges.size();
112+
BlockAux.RelocDeltas.resize(NumEdges, 0);
113+
BlockAux.EdgeKinds.resize_for_overwrite(NumEdges);
114+
115+
// Store anchors (offset and offset+size) for symbols.
116+
for (auto *Sym : S.symbols()) {
117+
if (!Sym->isDefined() || &Sym->getBlock() != B)
118+
continue;
119+
120+
BlockAux.Anchors.push_back({Sym->getOffset(), Sym, false});
121+
BlockAux.Anchors.push_back(
122+
{Sym->getOffset() + Sym->getSize(), Sym, true});
123+
}
124+
}
125+
}
126+
127+
// Sort anchors by offset so that we can find the closest relocation
128+
// efficiently. For a zero size symbol, ensure that its start anchor precedes
129+
// its end anchor. For two symbols with anchors at the same offset, their
130+
// order does not matter.
131+
for (auto &BlockAuxIter : Aux.Blocks) {
132+
llvm::sort(BlockAuxIter.second.Anchors, [](auto &A, auto &B) {
133+
return std::make_pair(A.Offset, A.End) < std::make_pair(B.Offset, B.End);
134+
});
135+
}
136+
137+
return Aux;
138+
}
139+
140+
static void relaxAlign(orc::ExecutorAddr Loc, const Edge &E, uint32_t &Remove,
141+
Edge::Kind &NewEdgeKind) {
142+
const uint64_t Addend =
143+
!E.getTarget().isDefined() ? Log2_64(E.getAddend()) + 1 : E.getAddend();
144+
const uint64_t AllBytes = (1ULL << (Addend & 0xff)) - 4;
145+
const uint64_t Align = 1ULL << (Addend & 0xff);
146+
const uint64_t MaxBytes = Addend >> 8;
147+
const uint64_t Off = Loc.getValue() & (Align - 1);
148+
const uint64_t CurBytes = Off == 0 ? 0 : Align - Off;
149+
// All bytes beyond the alignment boundary should be removed.
150+
// If emit bytes more than max bytes to emit, remove all.
151+
if (MaxBytes != 0 && CurBytes > MaxBytes)
152+
Remove = AllBytes;
153+
else
154+
Remove = AllBytes - CurBytes;
155+
156+
assert(static_cast<int32_t>(Remove) >= 0 &&
157+
"R_LARCH_ALIGN needs expanding the content");
158+
NewEdgeKind = AlignRelaxable;
159+
}
160+
161+
static bool relaxBlock(LinkGraph &G, Block &Block, BlockRelaxAux &Aux) {
162+
const auto BlockAddr = Block.getAddress();
163+
bool Changed = false;
164+
ArrayRef<SymbolAnchor> SA = ArrayRef(Aux.Anchors);
165+
uint32_t Delta = 0;
166+
167+
Aux.EdgeKinds.assign(Aux.EdgeKinds.size(), Edge::Invalid);
168+
Aux.Writes.clear();
169+
170+
for (auto [I, E] : llvm::enumerate(Aux.RelaxEdges)) {
171+
const auto Loc = BlockAddr + E->getOffset() - Delta;
172+
auto &Cur = Aux.RelocDeltas[I];
173+
uint32_t Remove = 0;
174+
switch (E->getKind()) {
175+
case AlignRelaxable:
176+
relaxAlign(Loc, *E, Remove, Aux.EdgeKinds[I]);
177+
break;
178+
default:
179+
llvm_unreachable("Unexpected relaxable edge kind");
180+
}
181+
182+
// For all anchors whose offsets are <= E->getOffset(), they are preceded by
183+
// the previous relocation whose RelocDeltas value equals Delta.
184+
// Decrease their offset and update their size.
185+
for (; SA.size() && SA[0].Offset <= E->getOffset(); SA = SA.slice(1)) {
186+
if (SA[0].End)
187+
SA[0].Sym->setSize(SA[0].Offset - Delta - SA[0].Sym->getOffset());
188+
else
189+
SA[0].Sym->setOffset(SA[0].Offset - Delta);
190+
}
191+
192+
Delta += Remove;
193+
if (Delta != Cur) {
194+
Cur = Delta;
195+
Changed = true;
196+
}
197+
}
198+
199+
for (const SymbolAnchor &A : SA) {
200+
if (A.End)
201+
A.Sym->setSize(A.Offset - Delta - A.Sym->getOffset());
202+
else
203+
A.Sym->setOffset(A.Offset - Delta);
204+
}
205+
206+
return Changed;
207+
}
208+
209+
static bool relaxOnce(LinkGraph &G, RelaxAux &Aux) {
210+
bool Changed = false;
211+
212+
for (auto &[B, BlockAux] : Aux.Blocks)
213+
Changed |= relaxBlock(G, *B, BlockAux);
214+
215+
return Changed;
216+
}
217+
218+
static void finalizeBlockRelax(LinkGraph &G, Block &Block, BlockRelaxAux &Aux) {
219+
auto Contents = Block.getAlreadyMutableContent();
220+
auto *Dest = Contents.data();
221+
uint32_t Offset = 0;
222+
uint32_t Delta = 0;
223+
224+
// Update section content: remove NOPs for R_LARCH_ALIGN and rewrite
225+
// instructions for relaxed relocations.
226+
for (auto [I, E] : llvm::enumerate(Aux.RelaxEdges)) {
227+
uint32_t Remove = Aux.RelocDeltas[I] - Delta;
228+
Delta = Aux.RelocDeltas[I];
229+
if (Remove == 0 && Aux.EdgeKinds[I] == Edge::Invalid)
230+
continue;
231+
232+
// Copy from last location to the current relocated location.
233+
const auto Size = E->getOffset() - Offset;
234+
std::memmove(Dest, Contents.data() + Offset, Size);
235+
Dest += Size;
236+
Offset = E->getOffset() + Remove;
237+
}
238+
239+
std::memmove(Dest, Contents.data() + Offset, Contents.size() - Offset);
240+
241+
// Fixup edge offsets and kinds.
242+
Delta = 0;
243+
size_t I = 0;
244+
for (auto &E : Block.edges()) {
245+
E.setOffset(E.getOffset() - Delta);
246+
247+
if (I < Aux.RelaxEdges.size() && Aux.RelaxEdges[I] == &E) {
248+
if (Aux.EdgeKinds[I] != Edge::Invalid)
249+
E.setKind(Aux.EdgeKinds[I]);
250+
251+
Delta = Aux.RelocDeltas[I];
252+
++I;
253+
}
254+
}
255+
256+
// Remove AlignRelaxable edges: all other relaxable edges got modified and
257+
// will be used later while linking. Alignment is entirely handled here so we
258+
// don't need these edges anymore.
259+
for (auto IE = Block.edges().begin(); IE != Block.edges().end();) {
260+
if (IE->getKind() == AlignRelaxable)
261+
IE = Block.removeEdge(IE);
262+
else
263+
++IE;
264+
}
265+
}
266+
267+
static void finalizeRelax(LinkGraph &G, RelaxAux &Aux) {
268+
for (auto &[B, BlockAux] : Aux.Blocks)
269+
finalizeBlockRelax(G, *B, BlockAux);
270+
}
271+
272+
static Error relax(LinkGraph &G) {
273+
auto Aux = initRelaxAux(G);
274+
while (relaxOnce(G, Aux)) {
275+
}
276+
finalizeRelax(G, Aux);
277+
return Error::success();
278+
}
279+
48280
template <typename ELFT>
49281
class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
50282
private:
@@ -74,13 +306,20 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
74306
return RequestGOTAndTransformToPageOffset12;
75307
case ELF::R_LARCH_CALL36:
76308
return Call36PCRel;
309+
case ELF::R_LARCH_ALIGN:
310+
return AlignRelaxable;
77311
}
78312

79313
return make_error<JITLinkError>(
80314
"Unsupported loongarch relocation:" + formatv("{0:d}: ", Type) +
81315
object::getELFRelocationTypeName(ELF::EM_LOONGARCH, Type));
82316
}
83317

318+
EdgeKind_loongarch getRelaxableRelocationKind(EdgeKind_loongarch Kind) {
319+
// TODO: Implement more. Just ignore all relaxations now.
320+
return Kind;
321+
}
322+
84323
Error addRelocations() override {
85324
LLVM_DEBUG(dbgs() << "Processing relocations:\n");
86325

@@ -99,6 +338,25 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
99338
Block &BlockToFix) {
100339
using Base = ELFLinkGraphBuilder<ELFT>;
101340

341+
uint32_t Type = Rel.getType(false);
342+
int64_t Addend = Rel.r_addend;
343+
344+
if (Type == ELF::R_LARCH_RELAX) {
345+
if (BlockToFix.edges_empty())
346+
return make_error<StringError>(
347+
"R_LARCH_RELAX without preceding relocation",
348+
inconvertibleErrorCode());
349+
350+
auto &PrevEdge = *std::prev(BlockToFix.edges().end());
351+
auto Kind = static_cast<EdgeKind_loongarch>(PrevEdge.getKind());
352+
PrevEdge.setKind(getRelaxableRelocationKind(Kind));
353+
return Error::success();
354+
}
355+
356+
Expected<loongarch::EdgeKind_loongarch> Kind = getRelocationKind(Type);
357+
if (!Kind)
358+
return Kind.takeError();
359+
102360
uint32_t SymbolIndex = Rel.getSymbol(false);
103361
auto ObjSymbol = Base::Obj.getRelocationSymbol(Rel, Base::SymTabSec);
104362
if (!ObjSymbol)
@@ -113,12 +371,6 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
113371
Base::GraphSymbols.size()),
114372
inconvertibleErrorCode());
115373

116-
uint32_t Type = Rel.getType(false);
117-
Expected<loongarch::EdgeKind_loongarch> Kind = getRelocationKind(Type);
118-
if (!Kind)
119-
return Kind.takeError();
120-
121-
int64_t Addend = Rel.r_addend;
122374
auto FixupAddress = orc::ExecutorAddr(FixupSect.sh_addr) + Rel.r_offset;
123375
Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress();
124376
Edge GE(*Kind, Offset, *GraphSymbol, Addend);
@@ -209,6 +461,9 @@ void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
209461

210462
// Add an in-place GOT/PLTStubs build pass.
211463
Config.PostPrunePasses.push_back(buildTables_ELF_loongarch);
464+
465+
// Add a linker relaxation pass.
466+
Config.PostAllocationPasses.push_back(relax);
212467
}
213468

214469
if (auto Err = Ctx->modifyPassConfig(*G, Config))
@@ -217,5 +472,7 @@ void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
217472
ELFJITLinker_loongarch::link(std::move(Ctx), std::move(G), std::move(Config));
218473
}
219474

475+
LinkGraphPassFunction createRelaxationPass_ELF_loongarch() { return relax; }
476+
220477
} // namespace jitlink
221478
} // namespace llvm

llvm/lib/ExecutionEngine/JITLink/loongarch.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const char *getEdgeKindName(Edge::Kind K) {
5252
KIND_NAME_CASE(RequestGOTAndTransformToPage20)
5353
KIND_NAME_CASE(RequestGOTAndTransformToPageOffset12)
5454
KIND_NAME_CASE(Call36PCRel)
55+
KIND_NAME_CASE(AlignRelaxable)
5556
default:
5657
return getGenericEdgeKindName(K);
5758
}

0 commit comments

Comments
 (0)