@@ -45,6 +45,238 @@ class ELFJITLinker_loongarch : public JITLinker<ELFJITLinker_loongarch> {
45
45
}
46
46
};
47
47
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
+
48
280
template <typename ELFT>
49
281
class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder <ELFT> {
50
282
private:
@@ -74,13 +306,20 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
74
306
return RequestGOTAndTransformToPageOffset12;
75
307
case ELF::R_LARCH_CALL36:
76
308
return Call36PCRel;
309
+ case ELF::R_LARCH_ALIGN:
310
+ return AlignRelaxable;
77
311
}
78
312
79
313
return make_error<JITLinkError>(
80
314
" Unsupported loongarch relocation:" + formatv (" {0:d}: " , Type) +
81
315
object::getELFRelocationTypeName (ELF::EM_LOONGARCH, Type));
82
316
}
83
317
318
+ EdgeKind_loongarch getRelaxableRelocationKind (EdgeKind_loongarch Kind) {
319
+ // TODO: Implement more. Just ignore all relaxations now.
320
+ return Kind;
321
+ }
322
+
84
323
Error addRelocations () override {
85
324
LLVM_DEBUG (dbgs () << " Processing relocations:\n " );
86
325
@@ -99,6 +338,25 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
99
338
Block &BlockToFix) {
100
339
using Base = ELFLinkGraphBuilder<ELFT>;
101
340
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
+
102
360
uint32_t SymbolIndex = Rel.getSymbol (false );
103
361
auto ObjSymbol = Base::Obj.getRelocationSymbol (Rel, Base::SymTabSec);
104
362
if (!ObjSymbol)
@@ -113,12 +371,6 @@ class ELFLinkGraphBuilder_loongarch : public ELFLinkGraphBuilder<ELFT> {
113
371
Base::GraphSymbols.size ()),
114
372
inconvertibleErrorCode ());
115
373
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 ;
122
374
auto FixupAddress = orc::ExecutorAddr (FixupSect.sh_addr ) + Rel.r_offset ;
123
375
Edge::OffsetT Offset = FixupAddress - BlockToFix.getAddress ();
124
376
Edge GE (*Kind, Offset, *GraphSymbol, Addend);
@@ -209,6 +461,9 @@ void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
209
461
210
462
// Add an in-place GOT/PLTStubs build pass.
211
463
Config.PostPrunePasses .push_back (buildTables_ELF_loongarch);
464
+
465
+ // Add a linker relaxation pass.
466
+ Config.PostAllocationPasses .push_back (relax);
212
467
}
213
468
214
469
if (auto Err = Ctx->modifyPassConfig (*G, Config))
@@ -217,5 +472,7 @@ void link_ELF_loongarch(std::unique_ptr<LinkGraph> G,
217
472
ELFJITLinker_loongarch::link (std::move (Ctx), std::move (G), std::move (Config));
218
473
}
219
474
475
+ LinkGraphPassFunction createRelaxationPass_ELF_loongarch () { return relax; }
476
+
220
477
} // namespace jitlink
221
478
} // namespace llvm
0 commit comments