1 | //===- OrcABISupport.h - ABI support code -----------------------*- 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 | // ABI specific code for Orc, e.g. callback assembly. |
10 | // |
11 | // ABI classes should be part of the JIT *target* process, not the host |
12 | // process (except where you're doing hosted JITing and the two are one and the |
13 | // same). |
14 | // |
15 | //===----------------------------------------------------------------------===// |
16 | |
17 | #ifndef LLVM_EXECUTIONENGINE_ORC_ORCABISUPPORT_H |
18 | #define LLVM_EXECUTIONENGINE_ORC_ORCABISUPPORT_H |
19 | |
20 | #include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" |
21 | #include "llvm/Support/Error.h" |
22 | #include "llvm/Support/ErrorHandling.h" |
23 | #include "llvm/Support/MathExtras.h" |
24 | #include <cstdint> |
25 | |
26 | namespace llvm { |
27 | namespace orc { |
28 | |
29 | struct IndirectStubsAllocationSizes { |
30 | uint64_t StubBytes = 0; |
31 | uint64_t PointerBytes = 0; |
32 | unsigned NumStubs = 0; |
33 | }; |
34 | |
35 | template <typename ORCABI> |
36 | IndirectStubsAllocationSizes |
37 | getIndirectStubsBlockSizes(unsigned MinStubs, unsigned RoundToMultipleOf = 0) { |
38 | assert( |
39 | (RoundToMultipleOf == 0 || (RoundToMultipleOf % ORCABI::StubSize == 0)) && |
40 | "RoundToMultipleOf is not a multiple of stub size" ); |
41 | uint64_t StubBytes = MinStubs * ORCABI::StubSize; |
42 | if (RoundToMultipleOf) |
43 | StubBytes = alignTo(Value: StubBytes, Align: RoundToMultipleOf); |
44 | unsigned NumStubs = StubBytes / ORCABI::StubSize; |
45 | uint64_t PointerBytes = NumStubs * ORCABI::PointerSize; |
46 | return {.StubBytes: StubBytes, .PointerBytes: PointerBytes, .NumStubs: NumStubs}; |
47 | } |
48 | |
49 | /// Generic ORC ABI support. |
50 | /// |
51 | /// This class can be substituted as the target architecture support class for |
52 | /// ORC templates that require one (e.g. IndirectStubsManagers). It does not |
53 | /// support lazy JITing however, and any attempt to use that functionality |
54 | /// will result in execution of an llvm_unreachable. |
55 | class OrcGenericABI { |
56 | public: |
57 | static constexpr unsigned PointerSize = sizeof(uintptr_t); |
58 | static constexpr unsigned TrampolineSize = 1; |
59 | static constexpr unsigned StubSize = 1; |
60 | static constexpr unsigned StubToPointerMaxDisplacement = 1; |
61 | static constexpr unsigned ResolverCodeSize = 1; |
62 | |
63 | static void writeResolverCode(char *ResolveWorkingMem, |
64 | ExecutorAddr ResolverTargetAddr, |
65 | ExecutorAddr ReentryFnAddr, |
66 | ExecutorAddr ReentryCtxAddr) { |
67 | llvm_unreachable("writeResolverCode is not supported by the generic host " |
68 | "support class" ); |
69 | } |
70 | |
71 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
72 | ExecutorAddr TrampolineBlockTargetAddr, |
73 | ExecutorAddr ResolverAddr, |
74 | unsigned NumTrampolines) { |
75 | llvm_unreachable("writeTrampolines is not supported by the generic host " |
76 | "support class" ); |
77 | } |
78 | |
79 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
80 | ExecutorAddr StubsBlockTargetAddress, |
81 | ExecutorAddr PointersBlockTargetAddress, |
82 | unsigned NumStubs) { |
83 | llvm_unreachable( |
84 | "writeIndirectStubsBlock is not supported by the generic host " |
85 | "support class" ); |
86 | } |
87 | }; |
88 | |
89 | class OrcAArch64 { |
90 | public: |
91 | static constexpr unsigned PointerSize = 8; |
92 | static constexpr unsigned TrampolineSize = 12; |
93 | static constexpr unsigned StubSize = 8; |
94 | static constexpr unsigned StubToPointerMaxDisplacement = 1U << 27; |
95 | static constexpr unsigned ResolverCodeSize = 0x120; |
96 | |
97 | /// Write the resolver code into the given memory. The user is |
98 | /// responsible for allocating the memory and setting permissions. |
99 | /// |
100 | /// ReentryFnAddr should be the address of a function whose signature matches |
101 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
102 | /// argument of writeResolverCode will be passed as the second argument to |
103 | /// the function at ReentryFnAddr. |
104 | static void writeResolverCode(char *ResolverWorkingMem, |
105 | ExecutorAddr ResolverTargetAddress, |
106 | ExecutorAddr ReentryFnAddr, |
107 | ExecutorAddr RentryCtxAddr); |
108 | |
109 | /// Write the requested number of trampolines into the given memory, |
110 | /// which must be big enough to hold 1 pointer, plus NumTrampolines |
111 | /// trampolines. |
112 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
113 | ExecutorAddr TrampolineBlockTargetAddress, |
114 | ExecutorAddr ResolverAddr, |
115 | unsigned NumTrampolines); |
116 | |
117 | /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem. |
118 | /// Stubs will be written as if linked at StubsBlockTargetAddress, with the |
119 | /// Nth stub using the Nth pointer in memory starting at |
120 | /// PointersBlockTargetAddress. |
121 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
122 | ExecutorAddr StubsBlockTargetAddress, |
123 | ExecutorAddr PointersBlockTargetAddress, |
124 | unsigned MinStubs); |
125 | }; |
126 | |
127 | /// X86_64 code that's common to all ABIs. |
128 | /// |
129 | /// X86_64 supports lazy JITing. |
130 | class OrcX86_64_Base { |
131 | public: |
132 | static constexpr unsigned PointerSize = 8; |
133 | static constexpr unsigned TrampolineSize = 8; |
134 | static constexpr unsigned StubSize = 8; |
135 | static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31; |
136 | |
137 | /// Write the requested number of trampolines into the given memory, |
138 | /// which must be big enough to hold 1 pointer, plus NumTrampolines |
139 | /// trampolines. |
140 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
141 | ExecutorAddr TrampolineBlockTargetAddress, |
142 | ExecutorAddr ResolverAddr, |
143 | unsigned NumTrampolines); |
144 | |
145 | /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem. |
146 | /// Stubs will be written as if linked at StubsBlockTargetAddress, with the |
147 | /// Nth stub using the Nth pointer in memory starting at |
148 | /// PointersBlockTargetAddress. |
149 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
150 | ExecutorAddr StubsBlockTargetAddress, |
151 | ExecutorAddr PointersBlockTargetAddress, |
152 | unsigned NumStubs); |
153 | }; |
154 | |
155 | /// X86_64 support for SysV ABI (Linux, MacOSX). |
156 | /// |
157 | /// X86_64_SysV supports lazy JITing. |
158 | class OrcX86_64_SysV : public OrcX86_64_Base { |
159 | public: |
160 | static constexpr unsigned ResolverCodeSize = 0x6C; |
161 | |
162 | /// Write the resolver code into the given memory. The user is |
163 | /// responsible for allocating the memory and setting permissions. |
164 | /// |
165 | /// ReentryFnAddr should be the address of a function whose signature matches |
166 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
167 | /// argument of writeResolverCode will be passed as the second argument to |
168 | /// the function at ReentryFnAddr. |
169 | static void writeResolverCode(char *ResolverWorkingMem, |
170 | ExecutorAddr ResolverTargetAddress, |
171 | ExecutorAddr ReentryFnAddr, |
172 | ExecutorAddr ReentryCtxAddr); |
173 | }; |
174 | |
175 | /// X86_64 support for Win32. |
176 | /// |
177 | /// X86_64_Win32 supports lazy JITing. |
178 | class OrcX86_64_Win32 : public OrcX86_64_Base { |
179 | public: |
180 | static constexpr unsigned ResolverCodeSize = 0x74; |
181 | |
182 | /// Write the resolver code into the given memory. The user is |
183 | /// responsible for allocating the memory and setting permissions. |
184 | /// |
185 | /// ReentryFnAddr should be the address of a function whose signature matches |
186 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
187 | /// argument of writeResolverCode will be passed as the second argument to |
188 | /// the function at ReentryFnAddr. |
189 | static void writeResolverCode(char *ResolverWorkingMem, |
190 | ExecutorAddr ResolverTargetAddress, |
191 | ExecutorAddr ReentryFnAddr, |
192 | ExecutorAddr ReentryCtxAddr); |
193 | }; |
194 | |
195 | /// I386 support. |
196 | /// |
197 | /// I386 supports lazy JITing. |
198 | class OrcI386 { |
199 | public: |
200 | static constexpr unsigned PointerSize = 4; |
201 | static constexpr unsigned TrampolineSize = 8; |
202 | static constexpr unsigned StubSize = 8; |
203 | static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31; |
204 | static constexpr unsigned ResolverCodeSize = 0x4a; |
205 | |
206 | /// Write the resolver code into the given memory. The user is |
207 | /// responsible for allocating the memory and setting permissions. |
208 | /// |
209 | /// ReentryFnAddr should be the address of a function whose signature matches |
210 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
211 | /// argument of writeResolverCode will be passed as the second argument to |
212 | /// the function at ReentryFnAddr. |
213 | static void writeResolverCode(char *ResolverWorkingMem, |
214 | ExecutorAddr ResolverTargetAddress, |
215 | ExecutorAddr ReentryFnAddr, |
216 | ExecutorAddr ReentryCtxAddr); |
217 | |
218 | /// Write the requested number of trampolines into the given memory, |
219 | /// which must be big enough to hold 1 pointer, plus NumTrampolines |
220 | /// trampolines. |
221 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
222 | ExecutorAddr TrampolineBlockTargetAddress, |
223 | ExecutorAddr ResolverAddr, |
224 | unsigned NumTrampolines); |
225 | |
226 | /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem. |
227 | /// Stubs will be written as if linked at StubsBlockTargetAddress, with the |
228 | /// Nth stub using the Nth pointer in memory starting at |
229 | /// PointersBlockTargetAddress. |
230 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
231 | ExecutorAddr StubsBlockTargetAddress, |
232 | ExecutorAddr PointersBlockTargetAddress, |
233 | unsigned NumStubs); |
234 | }; |
235 | |
236 | // @brief Mips32 support. |
237 | // |
238 | // Mips32 supports lazy JITing. |
239 | class OrcMips32_Base { |
240 | public: |
241 | static constexpr unsigned PointerSize = 4; |
242 | static constexpr unsigned TrampolineSize = 20; |
243 | static constexpr unsigned StubSize = 8; |
244 | static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31; |
245 | static constexpr unsigned ResolverCodeSize = 0xfc; |
246 | |
247 | /// Write the requested number of trampolines into the given memory, |
248 | /// which must be big enough to hold 1 pointer, plus NumTrampolines |
249 | /// trampolines. |
250 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
251 | ExecutorAddr TrampolineBlockTargetAddress, |
252 | ExecutorAddr ResolverAddr, |
253 | unsigned NumTrampolines); |
254 | |
255 | /// Write the resolver code into the given memory. The user is |
256 | /// responsible for allocating the memory and setting permissions. |
257 | /// |
258 | /// ReentryFnAddr should be the address of a function whose signature matches |
259 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
260 | /// argument of writeResolverCode will be passed as the second argument to |
261 | /// the function at ReentryFnAddr. |
262 | static void writeResolverCode(char *ResolverBlockWorkingMem, |
263 | ExecutorAddr ResolverBlockTargetAddress, |
264 | ExecutorAddr ReentryFnAddr, |
265 | ExecutorAddr ReentryCtxAddr, bool isBigEndian); |
266 | /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem. |
267 | /// Stubs will be written as if linked at StubsBlockTargetAddress, with the |
268 | /// Nth stub using the Nth pointer in memory starting at |
269 | /// PointersBlockTargetAddress. |
270 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
271 | ExecutorAddr StubsBlockTargetAddress, |
272 | ExecutorAddr PointersBlockTargetAddress, |
273 | unsigned NumStubs); |
274 | }; |
275 | |
276 | class OrcMips32Le : public OrcMips32_Base { |
277 | public: |
278 | static void writeResolverCode(char *ResolverWorkingMem, |
279 | ExecutorAddr ResolverTargetAddress, |
280 | ExecutorAddr ReentryFnAddr, |
281 | ExecutorAddr ReentryCtxAddr) { |
282 | OrcMips32_Base::writeResolverCode(ResolverBlockWorkingMem: ResolverWorkingMem, ResolverBlockTargetAddress: ResolverTargetAddress, |
283 | ReentryFnAddr, ReentryCtxAddr, isBigEndian: false); |
284 | } |
285 | }; |
286 | |
287 | class OrcMips32Be : public OrcMips32_Base { |
288 | public: |
289 | static void writeResolverCode(char *ResolverWorkingMem, |
290 | ExecutorAddr ResolverTargetAddress, |
291 | ExecutorAddr ReentryFnAddr, |
292 | ExecutorAddr ReentryCtxAddr) { |
293 | OrcMips32_Base::writeResolverCode(ResolverBlockWorkingMem: ResolverWorkingMem, ResolverBlockTargetAddress: ResolverTargetAddress, |
294 | ReentryFnAddr, ReentryCtxAddr, isBigEndian: true); |
295 | } |
296 | }; |
297 | |
298 | // @brief Mips64 support. |
299 | // |
300 | // Mips64 supports lazy JITing. |
301 | class OrcMips64 { |
302 | public: |
303 | static constexpr unsigned PointerSize = 8; |
304 | static constexpr unsigned TrampolineSize = 40; |
305 | static constexpr unsigned StubSize = 32; |
306 | static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31; |
307 | static constexpr unsigned ResolverCodeSize = 0x120; |
308 | |
309 | /// Write the resolver code into the given memory. The user is |
310 | /// responsible for allocating the memory and setting permissions. |
311 | /// |
312 | /// ReentryFnAddr should be the address of a function whose signature matches |
313 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
314 | /// argument of writeResolverCode will be passed as the second argument to |
315 | /// the function at ReentryFnAddr. |
316 | static void writeResolverCode(char *ResolverWorkingMem, |
317 | ExecutorAddr ResolverTargetAddress, |
318 | ExecutorAddr ReentryFnAddr, |
319 | ExecutorAddr ReentryCtxAddr); |
320 | |
321 | /// Write the requested number of trampolines into the given memory, |
322 | /// which must be big enough to hold 1 pointer, plus NumTrampolines |
323 | /// trampolines. |
324 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
325 | ExecutorAddr TrampolineBlockTargetAddress, |
326 | ExecutorAddr ResolverFnAddr, |
327 | unsigned NumTrampolines); |
328 | /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem. |
329 | /// Stubs will be written as if linked at StubsBlockTargetAddress, with the |
330 | /// Nth stub using the Nth pointer in memory starting at |
331 | /// PointersBlockTargetAddress. |
332 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
333 | ExecutorAddr StubsBlockTargetAddress, |
334 | ExecutorAddr PointersBlockTargetAddress, |
335 | unsigned NumStubs); |
336 | }; |
337 | |
338 | // @brief riscv64 support. |
339 | // |
340 | // RISC-V 64 supports lazy JITing. |
341 | class OrcRiscv64 { |
342 | public: |
343 | static constexpr unsigned PointerSize = 8; |
344 | static constexpr unsigned TrampolineSize = 16; |
345 | static constexpr unsigned StubSize = 16; |
346 | static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31; |
347 | static constexpr unsigned ResolverCodeSize = 0x148; |
348 | |
349 | /// Write the resolver code into the given memory. The user is |
350 | /// responsible for allocating the memory and setting permissions. |
351 | /// |
352 | /// ReentryFnAddr should be the address of a function whose signature matches |
353 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
354 | /// argument of writeResolverCode will be passed as the second argument to |
355 | /// the function at ReentryFnAddr. |
356 | static void writeResolverCode(char *ResolverWorkingMem, |
357 | ExecutorAddr ResolverTargetAddress, |
358 | ExecutorAddr ReentryFnAddr, |
359 | ExecutorAddr ReentryCtxAddr); |
360 | |
361 | /// Write the requested number of trampolines into the given memory, |
362 | /// which must be big enough to hold 1 pointer, plus NumTrampolines |
363 | /// trampolines. |
364 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
365 | ExecutorAddr TrampolineBlockTargetAddress, |
366 | ExecutorAddr ResolverFnAddr, |
367 | unsigned NumTrampolines); |
368 | /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem. |
369 | /// Stubs will be written as if linked at StubsBlockTargetAddress, with the |
370 | /// Nth stub using the Nth pointer in memory starting at |
371 | /// PointersBlockTargetAddress. |
372 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
373 | ExecutorAddr StubsBlockTargetAddress, |
374 | ExecutorAddr PointersBlockTargetAddress, |
375 | unsigned NumStubs); |
376 | }; |
377 | |
378 | // @brief loongarch64 support. |
379 | // |
380 | // LoongArch 64 supports lazy JITing. |
381 | class OrcLoongArch64 { |
382 | public: |
383 | static constexpr unsigned PointerSize = 8; |
384 | static constexpr unsigned TrampolineSize = 16; |
385 | static constexpr unsigned StubSize = 16; |
386 | static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31; |
387 | static constexpr unsigned ResolverCodeSize = 0xc8; |
388 | |
389 | /// Write the resolver code into the given memory. The user is |
390 | /// responsible for allocating the memory and setting permissions. |
391 | /// |
392 | /// ReentryFnAddr should be the address of a function whose signature matches |
393 | /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr |
394 | /// argument of writeResolverCode will be passed as the second argument to |
395 | /// the function at ReentryFnAddr. |
396 | static void writeResolverCode(char *ResolverWorkingMem, |
397 | ExecutorAddr ResolverTargetAddress, |
398 | ExecutorAddr ReentryFnAddr, |
399 | ExecutorAddr ReentryCtxAddr); |
400 | |
401 | /// Write the requested number of trampolines into the given memory, |
402 | /// which must be big enough to hold 1 pointer, plus NumTrampolines |
403 | /// trampolines. |
404 | static void writeTrampolines(char *TrampolineBlockWorkingMem, |
405 | ExecutorAddr TrampolineBlockTargetAddress, |
406 | ExecutorAddr ResolverFnAddr, |
407 | unsigned NumTrampolines); |
408 | |
409 | /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem. |
410 | /// Stubs will be written as if linked at StubsBlockTargetAddress, with the |
411 | /// Nth stub using the Nth pointer in memory starting at |
412 | /// PointersBlockTargetAddress. |
413 | static void writeIndirectStubsBlock(char *StubsBlockWorkingMem, |
414 | ExecutorAddr StubsBlockTargetAddress, |
415 | ExecutorAddr PointersBlockTargetAddress, |
416 | unsigned NumStubs); |
417 | }; |
418 | |
419 | } // end namespace orc |
420 | } // end namespace llvm |
421 | |
422 | #endif // LLVM_EXECUTIONENGINE_ORC_ORCABISUPPORT_H |
423 | |