LLVM 20.0.0git
loongarch.h
Go to the documentation of this file.
1//= loongarch.h - Generic JITLink loongarch edge kinds, utilities -*- C++ -*-=//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// Generic utilities for graphs representing loongarch objects.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_EXECUTIONENGINE_JITLINK_LOONGARCH_H
14#define LLVM_EXECUTIONENGINE_JITLINK_LOONGARCH_H
15
16#include "TableManager.h"
19
20namespace llvm {
21namespace jitlink {
22namespace loongarch {
23
24/// Represents loongarch fixups.
26 /// A plain 64-bit pointer value relocation.
27 ///
28 /// Fixup expression:
29 /// Fixup <- Target + Addend : uint64
30 ///
32
33 /// A plain 32-bit pointer value relocation.
34 ///
35 /// Fixup expression:
36 /// Fixup <- Target + Addend : uint32
37 ///
38 /// Errors:
39 /// - The target must reside in the low 32-bits of the address space,
40 /// otherwise an out-of-range error will be returned.
41 ///
43
44 /// A 16-bit PC-relative branch.
45 ///
46 /// Represents a PC-relative branch to a target within +/-128Kb. The target
47 /// must be 4-byte aligned.
48 ///
49 /// Fixup expression:
50 /// Fixup <- (Target - Fixup + Addend) >> 2 : int16
51 ///
52 /// Notes:
53 /// The '16' in the name refers to the number operand bits and follows the
54 /// naming convention used by the corresponding ELF relocations. Since the low
55 /// two bits must be zero (because of the 4-byte alignment of the target) the
56 /// operand is effectively a signed 18-bit number.
57 ///
58 /// Errors:
59 /// - The result of the unshifted part of the fixup expression must be
60 /// 4-byte aligned otherwise an alignment error will be returned.
61 /// - The result of the fixup expression must fit into an int16 otherwise an
62 /// out-of-range error will be returned.
63 ///
65
66 /// A 21-bit PC-relative branch.
67 ///
68 /// Represents a PC-relative branch to a target within +/-4Mb. The Target must
69 /// be 4-byte aligned.
70 ///
71 /// Fixup expression:
72 /// Fixup <- (Target - Fixup + Addend) >> 2 : int21
73 ///
74 /// Notes:
75 /// The '21' in the name refers to the number operand bits and follows the
76 /// naming convention used by the corresponding ELF relocations. Since the low
77 /// two bits must be zero (because of the 4-byte alignment of the target) the
78 /// operand is effectively a signed 23-bit number.
79 ///
80 /// Errors:
81 /// - The result of the unshifted part of the fixup expression must be
82 /// 4-byte aligned otherwise an alignment error will be returned.
83 /// - The result of the fixup expression must fit into an int21 otherwise an
84 /// out-of-range error will be returned.
85 ///
87
88 /// A 26-bit PC-relative branch.
89 ///
90 /// Represents a PC-relative call or branch to a target within +/-128Mb. The
91 /// target must be 4-byte aligned.
92 ///
93 /// Fixup expression:
94 /// Fixup <- (Target - Fixup + Addend) >> 2 : int26
95 ///
96 /// Notes:
97 /// The '26' in the name refers to the number operand bits and follows the
98 /// naming convention used by the corresponding ELF relocations. Since the low
99 /// two bits must be zero (because of the 4-byte alignment of the target) the
100 /// operand is effectively a signed 28-bit number.
101 ///
102 /// Errors:
103 /// - The result of the unshifted part of the fixup expression must be
104 /// 4-byte aligned otherwise an alignment error will be returned.
105 /// - The result of the fixup expression must fit into an int26 otherwise an
106 /// out-of-range error will be returned.
107 ///
109
110 /// A 32-bit delta.
111 ///
112 /// Delta from the fixup to the target.
113 ///
114 /// Fixup expression:
115 /// Fixup <- Target - Fixup + Addend : int32
116 ///
117 /// Errors:
118 /// - The result of the fixup expression must fit into an int32, otherwise
119 /// an out-of-range error will be returned.
120 ///
122
123 /// A 32-bit negative delta.
124 ///
125 /// Delta from the target back to the fixup.
126 ///
127 /// Fixup expression:
128 /// Fixup <- Fixup - Target + Addend : int32
129 ///
130 /// Errors:
131 /// - The result of the fixup expression must fit into an int32, otherwise
132 /// an out-of-range error will be returned.
133 ///
135
136 /// A 64-bit delta.
137 ///
138 /// Delta from the fixup to the target.
139 ///
140 /// Fixup expression:
141 /// Fixup <- Target - Fixup + Addend : int64
142 ///
144
145 /// The signed 20-bit delta from the fixup page to the page containing the
146 /// target.
147 ///
148 /// Fixup expression:
149 /// Fixup <- (((Target + Addend + ((Target + Addend) & 0x800)) & ~0xfff)
150 // - (Fixup & ~0xfff)) >> 12 : int20
151 ///
152 /// Notes:
153 /// For PCALAU12I fixups.
154 ///
155 /// Errors:
156 /// - The result of the fixup expression must fit into an int20 otherwise an
157 /// out-of-range error will be returned.
158 ///
160
161 /// The 12-bit offset of the target within its page.
162 ///
163 /// Typically used to fix up ADDI/LD_W/LD_D immediates.
164 ///
165 /// Fixup expression:
166 /// Fixup <- ((Target + Addend) >> Shift) & 0xfff : int12
167 ///
169
170 /// A GOT entry getter/constructor, transformed to Page20 pointing at the GOT
171 /// entry for the original target.
172 ///
173 /// Indicates that this edge should be transformed into a Page20 targeting
174 /// the GOT entry for the edge's current target, maintaining the same addend.
175 /// A GOT entry for the target should be created if one does not already
176 /// exist.
177 ///
178 /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
179 /// by default.
180 ///
181 /// Fixup expression:
182 /// NONE
183 ///
184 /// Errors:
185 /// - *ASSERTION* Failure to handle edges of this kind prior to the fixup
186 /// phase will result in an assert/unreachable during the fixup phase.
187 ///
189
190 /// A GOT entry getter/constructor, transformed to Pageoffset12 pointing at
191 /// the GOT entry for the original target.
192 ///
193 /// Indicates that this edge should be transformed into a PageOffset12
194 /// targeting the GOT entry for the edge's current target, maintaining the
195 /// same addend. A GOT entry for the target should be created if one does not
196 /// already exist.
197 ///
198 /// Edges of this kind are usually handled by a GOT/PLT builder pass inserted
199 /// by default.
200 ///
201 /// Fixup expression:
202 /// NONE
203 ///
205
206 /// A 36-bit PC-relative call.
207 ///
208 /// Represents a PC-relative call to a target within [-128G - 0x20000, +128G
209 /// - 0x20000). The target must be 4-byte aligned. For adjacent pcaddu18i+jirl
210 /// instruction pairs.
211 ///
212 /// Fixup expression:
213 /// Fixup <- (Target - Fixup + Addend) >> 2 : int36
214 ///
215 /// Notes:
216 /// The '36' in the name refers to the number operand bits and follows the
217 /// naming convention used by the corresponding ELF relocations. Since the low
218 /// two bits must be zero (because of the 4-byte alignment of the target) the
219 /// operand is effectively a signed 38-bit number.
220 ///
221 /// Errors:
222 /// - The result of the unshifted part of the fixup expression must be
223 /// 4-byte aligned otherwise an alignment error will be returned.
224 /// - The result of the fixup expression must fit into an int36 otherwise an
225 /// out-of-range error will be returned.
226 ///
228};
229
230/// Returns a string name for the given loongarch edge. For debugging purposes
231/// only.
232const char *getEdgeKindName(Edge::Kind K);
233
234// Returns extract bits Val[Hi:Lo].
235inline uint32_t extractBits(uint64_t Val, unsigned Hi, unsigned Lo) {
236 return Hi == 63 ? Val >> Lo : (Val & (((1UL << (Hi + 1)) - 1))) >> Lo;
237}
238
239/// Apply fixup expression for edge to block content.
240inline Error applyFixup(LinkGraph &G, Block &B, const Edge &E) {
241 using namespace support;
242
243 char *BlockWorkingMem = B.getAlreadyMutableContent().data();
244 char *FixupPtr = BlockWorkingMem + E.getOffset();
245 uint64_t FixupAddress = (B.getAddress() + E.getOffset()).getValue();
246 uint64_t TargetAddress = E.getTarget().getAddress().getValue();
247 int64_t Addend = E.getAddend();
248
249 switch (E.getKind()) {
250 case Pointer64:
251 *(ulittle64_t *)FixupPtr = TargetAddress + Addend;
252 break;
253 case Pointer32: {
254 uint64_t Value = TargetAddress + Addend;
255 if (Value > std::numeric_limits<uint32_t>::max())
256 return makeTargetOutOfRangeError(G, B, E);
257 *(ulittle32_t *)FixupPtr = Value;
258 break;
259 }
260 case Branch16PCRel: {
261 int64_t Value = TargetAddress - FixupAddress + Addend;
262
263 if (!isInt<18>(Value))
264 return makeTargetOutOfRangeError(G, B, E);
265
266 if (!isShiftedInt<16, 2>(Value))
267 return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
268
269 uint32_t RawInstr = *(little32_t *)FixupPtr;
270 uint32_t Imm = static_cast<uint32_t>(Value >> 2);
271 uint32_t Imm15_0 = extractBits(Imm, /*Hi=*/15, /*Lo=*/0) << 10;
272 *(little32_t *)FixupPtr = RawInstr | Imm15_0;
273 break;
274 }
275 case Branch21PCRel: {
276 int64_t Value = TargetAddress - FixupAddress + Addend;
277
278 if (!isInt<23>(Value))
279 return makeTargetOutOfRangeError(G, B, E);
280
281 if (!isShiftedInt<21, 2>(Value))
282 return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
283
284 uint32_t RawInstr = *(little32_t *)FixupPtr;
285 uint32_t Imm = static_cast<uint32_t>(Value >> 2);
286 uint32_t Imm15_0 = extractBits(Imm, /*Hi=*/15, /*Lo=*/0) << 10;
287 uint32_t Imm20_16 = extractBits(Imm, /*Hi=*/20, /*Lo=*/16);
288 *(little32_t *)FixupPtr = RawInstr | Imm15_0 | Imm20_16;
289 break;
290 }
291 case Branch26PCRel: {
292 int64_t Value = TargetAddress - FixupAddress + Addend;
293
294 if (!isInt<28>(Value))
295 return makeTargetOutOfRangeError(G, B, E);
296
297 if (!isShiftedInt<26, 2>(Value))
298 return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
299
300 uint32_t RawInstr = *(little32_t *)FixupPtr;
301 uint32_t Imm = static_cast<uint32_t>(Value >> 2);
302 uint32_t Imm15_0 = extractBits(Imm, /*Hi=*/15, /*Lo=*/0) << 10;
303 uint32_t Imm25_16 = extractBits(Imm, /*Hi=*/25, /*Lo=*/16);
304 *(little32_t *)FixupPtr = RawInstr | Imm15_0 | Imm25_16;
305 break;
306 }
307 case Delta32: {
308 int64_t Value = TargetAddress - FixupAddress + Addend;
309
310 if (!isInt<32>(Value))
311 return makeTargetOutOfRangeError(G, B, E);
312 *(little32_t *)FixupPtr = Value;
313 break;
314 }
315 case NegDelta32: {
316 int64_t Value = FixupAddress - TargetAddress + Addend;
317 if (!isInt<32>(Value))
318 return makeTargetOutOfRangeError(G, B, E);
319 *(little32_t *)FixupPtr = Value;
320 break;
321 }
322 case Delta64:
323 *(little64_t *)FixupPtr = TargetAddress - FixupAddress + Addend;
324 break;
325 case Page20: {
326 uint64_t Target = TargetAddress + Addend;
327 uint64_t TargetPage =
328 (Target + (Target & 0x800)) & ~static_cast<uint64_t>(0xfff);
329 uint64_t PCPage = FixupAddress & ~static_cast<uint64_t>(0xfff);
330
331 int64_t PageDelta = TargetPage - PCPage;
332 if (!isInt<32>(PageDelta))
333 return makeTargetOutOfRangeError(G, B, E);
334
335 uint32_t RawInstr = *(little32_t *)FixupPtr;
336 uint32_t Imm31_12 = extractBits(PageDelta, /*Hi=*/31, /*Lo=*/12) << 5;
337 *(little32_t *)FixupPtr = RawInstr | Imm31_12;
338 break;
339 }
340 case PageOffset12: {
341 uint64_t TargetOffset = (TargetAddress + Addend) & 0xfff;
342
343 uint32_t RawInstr = *(ulittle32_t *)FixupPtr;
344 uint32_t Imm11_0 = TargetOffset << 10;
345 *(ulittle32_t *)FixupPtr = RawInstr | Imm11_0;
346 break;
347 }
348 case Call36PCRel: {
349 int64_t Value = TargetAddress - FixupAddress + Addend;
350
351 if ((Value + 0x20000) != llvm::SignExtend64(Value + 0x20000, 38))
352 return makeTargetOutOfRangeError(G, B, E);
353
354 if (!isShiftedInt<36, 2>(Value))
355 return makeAlignmentError(orc::ExecutorAddr(FixupAddress), Value, 4, E);
356
357 uint32_t Pcaddu18i = *(little32_t *)FixupPtr;
358 uint32_t Hi20 = extractBits(Value + (1 << 17), /*Hi=*/37, /*Lo=*/18) << 5;
359 *(little32_t *)FixupPtr = Pcaddu18i | Hi20;
360 uint32_t Jirl = *(little32_t *)(FixupPtr + 4);
361 uint32_t Lo16 = extractBits(Value, /*Hi=*/17, /*Lo=*/2) << 10;
362 *(little32_t *)(FixupPtr + 4) = Jirl | Lo16;
363 break;
364 }
365 default:
366 return make_error<JITLinkError>(
367 "In graph " + G.getName() + ", section " + B.getSection().getName() +
368 " unsupported edge kind " + getEdgeKindName(E.getKind()));
369 }
370
371 return Error::success();
372}
373
374/// loongarch null pointer content.
375extern const char NullPointerContent[8];
377 return {reinterpret_cast<const char *>(NullPointerContent),
378 G.getPointerSize()};
379}
380
381/// loongarch stub content.
382///
383/// Contains the instruction sequence for an indirect jump via an in-memory
384/// pointer:
385/// pcalau12i $t8, %page20(ptr)
386/// ld.[w/d] $t8, %pageoff12(ptr)
387/// jr $t8
388constexpr size_t StubEntrySize = 12;
392 auto StubContent =
393 G.getPointerSize() == 8 ? LA64StubContent : LA32StubContent;
394 return {reinterpret_cast<const char *>(StubContent), StubEntrySize};
395}
396
397/// Creates a new pointer block in the given section and returns an
398/// Anonymous symbol pointing to it.
399///
400/// If InitialTarget is given then an Pointer64 relocation will be added to the
401/// block pointing at InitialTarget.
402///
403/// The pointer block will have the following default values:
404/// alignment: PointerSize
405/// alignment-offset: 0
407 Symbol *InitialTarget = nullptr,
408 uint64_t InitialAddend = 0) {
409 auto &B = G.createContentBlock(PointerSection, getGOTEntryBlockContent(G),
410 orc::ExecutorAddr(), G.getPointerSize(), 0);
411 if (InitialTarget)
412 B.addEdge(G.getPointerSize() == 8 ? Pointer64 : Pointer32, 0,
413 *InitialTarget, InitialAddend);
414 return G.addAnonymousSymbol(B, 0, G.getPointerSize(), false, false);
415}
416
417/// Create a jump stub that jumps via the pointer at the given symbol and
418/// an anonymous symbol pointing to it. Return the anonymous symbol.
420 Section &StubSection,
421 Symbol &PointerSymbol) {
422 Block &StubContentBlock = G.createContentBlock(
423 StubSection, getStubBlockContent(G), orc::ExecutorAddr(), 4, 0);
424 StubContentBlock.addEdge(Page20, 0, PointerSymbol, 0);
425 StubContentBlock.addEdge(PageOffset12, 4, PointerSymbol, 0);
426 return G.addAnonymousSymbol(StubContentBlock, 0, StubEntrySize, true, false);
427}
428
429/// Global Offset Table Builder.
430class GOTTableManager : public TableManager<GOTTableManager> {
431public:
432 static StringRef getSectionName() { return "$__GOT"; }
433
435 Edge::Kind KindToSet = Edge::Invalid;
436 switch (E.getKind()) {
438 KindToSet = Page20;
439 break;
441 KindToSet = PageOffset12;
442 break;
443 default:
444 return false;
445 }
446 assert(KindToSet != Edge::Invalid &&
447 "Fell through switch, but no new kind to set");
448 DEBUG_WITH_TYPE("jitlink", {
449 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
450 << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
451 << formatv("{0:x}", E.getOffset()) << ")\n";
452 });
453 E.setKind(KindToSet);
454 E.setTarget(getEntryForTarget(G, E.getTarget()));
455 return true;
456 }
457
459 return createAnonymousPointer(G, getGOTSection(G), &Target);
460 }
461
462private:
463 Section &getGOTSection(LinkGraph &G) {
464 if (!GOTSection)
465 GOTSection = &G.createSection(getSectionName(),
467 return *GOTSection;
468 }
469
470 Section *GOTSection = nullptr;
471};
472
473/// Procedure Linkage Table Builder.
474class PLTTableManager : public TableManager<PLTTableManager> {
475public:
477
478 static StringRef getSectionName() { return "$__STUBS"; }
479
481 if ((E.getKind() == Branch26PCRel || E.getKind() == Call36PCRel) &&
482 !E.getTarget().isDefined()) {
483 DEBUG_WITH_TYPE("jitlink", {
484 dbgs() << " Fixing " << G.getEdgeKindName(E.getKind()) << " edge at "
485 << B->getFixupAddress(E) << " (" << B->getAddress() << " + "
486 << formatv("{0:x}", E.getOffset()) << ")\n";
487 });
488 E.setTarget(getEntryForTarget(G, E.getTarget()));
489 return true;
490 }
491 return false;
492 }
493
497 }
498
499public:
501 if (!StubsSection)
502 StubsSection = &G.createSection(getSectionName(),
504 return *StubsSection;
505 }
506
509};
510
511} // namespace loongarch
512} // namespace jitlink
513} // namespace llvm
514
515#endif
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define DEBUG_WITH_TYPE(TYPE,...)
DEBUG_WITH_TYPE macro - This macro should be used by passes to emit debug information.
Definition: Debug.h:64
#define G(x, y, z)
Definition: MD5.cpp:56
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
Lightweight error class with error context and mandatory checking.
Definition: Error.h:160
static ErrorSuccess success()
Create a success value.
Definition: Error.h:337
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:51
Target - Wrapper for Target specific information.
LLVM Value Representation.
Definition: Value.h:74
Represents an address in the executor process.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
raw_ostream & dbgs()
dbgs() - This returns a reference to a raw_ostream for debugging messages.
Definition: Debug.cpp:163
constexpr int64_t SignExtend64(uint64_t x)
Sign-extend the number in the bottom B bits of X to a 64-bit integer.
Definition: MathExtras.h:581