@@ -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+
48280template <typename ELFT>
49281class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder <ELFT> {
50282private:
@@ -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
@@ -100,22 +339,17 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
100339 using Base = ELFLinkGraphBuilder<ELFT>;
101340
102341 uint32_t Type = Rel.getType (false );
103- // We do not implement linker relaxation for jitlink now, except what is
104- // required for alignment (see below).
105- if (Type == ELF::R_LARCH_RELAX)
106- return Error::success ();
107-
108342 int64_t Addend = Rel.r_addend ;
109- if (Type == ELF::R_LARCH_ALIGN) {
110- uint64_t Alignment = PowerOf2Ceil (Addend);
111- // FIXME: Implement support for ensuring alignment together with linker
112- // relaxation. Addend is always 28 in the most common case when
113- // interpreting C++ code in clang-repl.
114- if (Alignment > 32 )
115- return make_error<JITLinkError>(
116- formatv ( " Unsupported relocation R_LARCH_ALIGN with alignment {0} "
117- " larger than 32 (addend: {1}) " ,
118- Alignment, 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 ));
119353 return Error::success ();
120354 }
121355
@@ -227,6 +461,9 @@ void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
227461
228462 // Add an in-place GOT/PLTStubs build pass.
229463 Config.PostPrunePasses .push_back (buildTables_ELF_loongarch);
464+
465+ // Add a linker relaxation pass.
466+ Config.PostAllocationPasses .push_back (relax);
230467 }
231468
232469 if (auto Err = Ctx->modifyPassConfig (*G, Config))
@@ -235,5 +472,7 @@ void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
235472 ELFJITLinker_loongarch::link (std::move (Ctx), std::move (G), std::move (Config));
236473}
237474
475+ LinkGraphPassFunction createRelaxationPass_ELF_loongarch () { return relax; }
476+
238477} // namespace jitlink
239478} // namespace llvm
0 commit comments