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(StubBytes, RoundToMultipleOf);
44   unsigned NumStubs = StubBytes / ORCABI::StubSize;
45   uint64_t PointerBytes = NumStubs * ORCABI::PointerSize;
46   return {StubBytes, PointerBytes, 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(ResolverWorkingMem, ResolverTargetAddress,
283                                       ReentryFnAddr, ReentryCtxAddr, 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(ResolverWorkingMem, ResolverTargetAddress,
294                                       ReentryFnAddr, ReentryCtxAddr, 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