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/JITSymbol.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                                 JITTargetAddress ResolverTargetAddr,
65                                 JITTargetAddress ReentryFnAddr,
66                                 JITTargetAddress ReentryCtxAddr) {
67     llvm_unreachable("writeResolverCode is not supported by the generic host "
68                      "support class");
69   }
70 
71   static void writeTrampolines(char *TrampolineBlockWorkingMem,
72                                JITTargetAddress TrampolineBlockTargetAddr,
73                                JITTargetAddress ResolverAddr,
74                                unsigned NumTrampolines) {
75     llvm_unreachable("writeTrampolines is not supported by the generic host "
76                      "support class");
77   }
78 
79   static void writeIndirectStubsBlock(
80       char *StubsBlockWorkingMem, JITTargetAddress StubsBlockTargetAddress,
81       JITTargetAddress PointersBlockTargetAddress, unsigned NumStubs) {
82     llvm_unreachable(
83         "writeIndirectStubsBlock is not supported by the generic host "
84         "support class");
85   }
86 };
87 
88 class OrcAArch64 {
89 public:
90   static constexpr unsigned PointerSize = 8;
91   static constexpr unsigned TrampolineSize = 12;
92   static constexpr unsigned StubSize = 8;
93   static constexpr unsigned StubToPointerMaxDisplacement = 1U << 27;
94   static constexpr unsigned ResolverCodeSize = 0x120;
95 
96   /// Write the resolver code into the given memory. The user is
97   /// responsible for allocating the memory and setting permissions.
98   ///
99   /// ReentryFnAddr should be the address of a function whose signature matches
100   /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr
101   /// argument of writeResolverCode will be passed as the second argument to
102   /// the function at ReentryFnAddr.
103   static void writeResolverCode(char *ResolverWorkingMem,
104                                 JITTargetAddress ResolverTargetAddress,
105                                 JITTargetAddress ReentryFnAddr,
106                                 JITTargetAddress RentryCtxAddr);
107 
108   /// Write the requested number of trampolines into the given memory,
109   /// which must be big enough to hold 1 pointer, plus NumTrampolines
110   /// trampolines.
111   static void writeTrampolines(char *TrampolineBlockWorkingMem,
112                                JITTargetAddress TrampolineBlockTargetAddress,
113                                JITTargetAddress ResolverAddr,
114                                unsigned NumTrampolines);
115 
116   /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem.
117   /// Stubs will be written as if linked at StubsBlockTargetAddress, with the
118   /// Nth stub using the Nth pointer in memory starting at
119   /// PointersBlockTargetAddress.
120   static void writeIndirectStubsBlock(
121       char *StubsBlockWorkingMem, JITTargetAddress StubsBlockTargetAddress,
122       JITTargetAddress PointersBlockTargetAddress, unsigned MinStubs);
123 };
124 
125 /// X86_64 code that's common to all ABIs.
126 ///
127 /// X86_64 supports lazy JITing.
128 class OrcX86_64_Base {
129 public:
130   static constexpr unsigned PointerSize = 8;
131   static constexpr unsigned TrampolineSize = 8;
132   static constexpr unsigned StubSize = 8;
133   static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31;
134 
135   /// Write the requested number of trampolines into the given memory,
136   /// which must be big enough to hold 1 pointer, plus NumTrampolines
137   /// trampolines.
138   static void writeTrampolines(char *TrampolineBlockWorkingMem,
139                                JITTargetAddress TrampolineBlockTargetAddress,
140                                JITTargetAddress ResolverAddr,
141                                unsigned NumTrampolines);
142 
143   /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem.
144   /// Stubs will be written as if linked at StubsBlockTargetAddress, with the
145   /// Nth stub using the Nth pointer in memory starting at
146   /// PointersBlockTargetAddress.
147   static void writeIndirectStubsBlock(
148       char *StubsBlockWorkingMem, JITTargetAddress StubsBlockTargetAddress,
149       JITTargetAddress PointersBlockTargetAddress, unsigned NumStubs);
150 };
151 
152 /// X86_64 support for SysV ABI (Linux, MacOSX).
153 ///
154 /// X86_64_SysV supports lazy JITing.
155 class OrcX86_64_SysV : public OrcX86_64_Base {
156 public:
157   static constexpr unsigned ResolverCodeSize = 0x6C;
158 
159   /// Write the resolver code into the given memory. The user is
160   /// responsible for allocating the memory and setting permissions.
161   ///
162   /// ReentryFnAddr should be the address of a function whose signature matches
163   /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr
164   /// argument of writeResolverCode will be passed as the second argument to
165   /// the function at ReentryFnAddr.
166   static void writeResolverCode(char *ResolverWorkingMem,
167                                 JITTargetAddress ResolverTargetAddress,
168                                 JITTargetAddress ReentryFnAddr,
169                                 JITTargetAddress ReentryCtxAddr);
170 };
171 
172 /// X86_64 support for Win32.
173 ///
174 /// X86_64_Win32 supports lazy JITing.
175 class OrcX86_64_Win32 : public OrcX86_64_Base {
176 public:
177   static constexpr unsigned ResolverCodeSize = 0x74;
178 
179   /// Write the resolver code into the given memory. The user is
180   /// responsible for allocating the memory and setting permissions.
181   ///
182   /// ReentryFnAddr should be the address of a function whose signature matches
183   /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr
184   /// argument of writeResolverCode will be passed as the second argument to
185   /// the function at ReentryFnAddr.
186   static void writeResolverCode(char *ResolverWorkingMem,
187                                 JITTargetAddress ResolverTargetAddress,
188                                 JITTargetAddress ReentryFnAddr,
189                                 JITTargetAddress ReentryCtxAddr);
190 };
191 
192 /// I386 support.
193 ///
194 /// I386 supports lazy JITing.
195 class OrcI386 {
196 public:
197   static constexpr unsigned PointerSize = 4;
198   static constexpr unsigned TrampolineSize = 8;
199   static constexpr unsigned StubSize = 8;
200   static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31;
201   static constexpr unsigned ResolverCodeSize = 0x4a;
202 
203   /// Write the resolver code into the given memory. The user is
204   /// responsible for allocating the memory and setting permissions.
205   ///
206   /// ReentryFnAddr should be the address of a function whose signature matches
207   /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr
208   /// argument of writeResolverCode will be passed as the second argument to
209   /// the function at ReentryFnAddr.
210   static void writeResolverCode(char *ResolverWorkingMem,
211                                 JITTargetAddress ResolverTargetAddress,
212                                 JITTargetAddress ReentryFnAddr,
213                                 JITTargetAddress ReentryCtxAddr);
214 
215   /// Write the requested number of trampolines into the given memory,
216   /// which must be big enough to hold 1 pointer, plus NumTrampolines
217   /// trampolines.
218   static void writeTrampolines(char *TrampolineBlockWorkingMem,
219                                JITTargetAddress TrampolineBlockTargetAddress,
220                                JITTargetAddress ResolverAddr,
221                                unsigned NumTrampolines);
222 
223   /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem.
224   /// Stubs will be written as if linked at StubsBlockTargetAddress, with the
225   /// Nth stub using the Nth pointer in memory starting at
226   /// PointersBlockTargetAddress.
227   static void writeIndirectStubsBlock(
228       char *StubsBlockWorkingMem, JITTargetAddress StubsBlockTargetAddress,
229       JITTargetAddress PointersBlockTargetAddress, unsigned NumStubs);
230 };
231 
232 // @brief Mips32 support.
233 //
234 // Mips32 supports lazy JITing.
235 class OrcMips32_Base {
236 public:
237   static constexpr unsigned PointerSize = 4;
238   static constexpr unsigned TrampolineSize = 20;
239   static constexpr unsigned StubSize = 8;
240   static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31;
241   static constexpr unsigned ResolverCodeSize = 0xfc;
242 
243   /// Write the requested number of trampolines into the given memory,
244   /// which must be big enough to hold 1 pointer, plus NumTrampolines
245   /// trampolines.
246   static void writeTrampolines(char *TrampolineBlockWorkingMem,
247                                JITTargetAddress TrampolineBlockTargetAddress,
248                                JITTargetAddress ResolverAddr,
249                                unsigned NumTrampolines);
250 
251   /// Write the resolver code into the given memory. The user is
252   /// responsible for allocating the memory and setting permissions.
253   ///
254   /// ReentryFnAddr should be the address of a function whose signature matches
255   /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr
256   /// argument of writeResolverCode will be passed as the second argument to
257   /// the function at ReentryFnAddr.
258   static void writeResolverCode(char *ResolverBlockWorkingMem,
259                                 JITTargetAddress ResolverBlockTargetAddress,
260                                 JITTargetAddress ReentryFnAddr,
261                                 JITTargetAddress ReentryCtxAddr,
262                                 bool isBigEndian);
263   /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem.
264   /// Stubs will be written as if linked at StubsBlockTargetAddress, with the
265   /// Nth stub using the Nth pointer in memory starting at
266   /// PointersBlockTargetAddress.
267   static void writeIndirectStubsBlock(
268       char *StubsBlockWorkingMem, JITTargetAddress StubsBlockTargetAddress,
269       JITTargetAddress PointersBlockTargetAddress, unsigned NumStubs);
270 };
271 
272 class OrcMips32Le : public OrcMips32_Base {
273 public:
274   static void writeResolverCode(char *ResolverWorkingMem,
275                                 JITTargetAddress ResolverTargetAddress,
276                                 JITTargetAddress ReentryFnAddr,
277                                 JITTargetAddress ReentryCtxAddr) {
278     OrcMips32_Base::writeResolverCode(ResolverWorkingMem, ResolverTargetAddress,
279                                       ReentryFnAddr, ReentryCtxAddr, false);
280   }
281 };
282 
283 class OrcMips32Be : public OrcMips32_Base {
284 public:
285   static void writeResolverCode(char *ResolverWorkingMem,
286                                 JITTargetAddress ResolverTargetAddress,
287                                 JITTargetAddress ReentryFnAddr,
288                                 JITTargetAddress ReentryCtxAddr) {
289     OrcMips32_Base::writeResolverCode(ResolverWorkingMem, ResolverTargetAddress,
290                                       ReentryFnAddr, ReentryCtxAddr, true);
291   }
292 };
293 
294 // @brief Mips64 support.
295 //
296 // Mips64 supports lazy JITing.
297 class OrcMips64 {
298 public:
299   static constexpr unsigned PointerSize = 8;
300   static constexpr unsigned TrampolineSize = 40;
301   static constexpr unsigned StubSize = 32;
302   static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31;
303   static constexpr unsigned ResolverCodeSize = 0x120;
304 
305   /// Write the resolver code into the given memory. The user is
306   /// responsible for allocating the memory and setting permissions.
307   ///
308   /// ReentryFnAddr should be the address of a function whose signature matches
309   /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr
310   /// argument of writeResolverCode will be passed as the second argument to
311   /// the function at ReentryFnAddr.
312   static void writeResolverCode(char *ResolverWorkingMem,
313                                 JITTargetAddress ResolverTargetAddress,
314                                 JITTargetAddress ReentryFnAddr,
315                                 JITTargetAddress ReentryCtxAddr);
316 
317   /// Write the requested number of trampolines into the given memory,
318   /// which must be big enough to hold 1 pointer, plus NumTrampolines
319   /// trampolines.
320   static void writeTrampolines(char *TrampolineBlockWorkingMem,
321                                JITTargetAddress TrampolineBlockTargetAddress,
322                                JITTargetAddress ResolverFnAddr,
323                                unsigned NumTrampolines);
324   /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem.
325   /// Stubs will be written as if linked at StubsBlockTargetAddress, with the
326   /// Nth stub using the Nth pointer in memory starting at
327   /// PointersBlockTargetAddress.
328   static void writeIndirectStubsBlock(
329       char *StubsBlockWorkingMem, JITTargetAddress StubsBlockTargetAddress,
330       JITTargetAddress PointersBlockTargetAddress, unsigned NumStubs);
331 };
332 
333 // @brief riscv64 support.
334 //
335 // RISC-V 64 supports lazy JITing.
336 class OrcRiscv64 {
337 public:
338   static constexpr unsigned PointerSize = 8;
339   static constexpr unsigned TrampolineSize = 16;
340   static constexpr unsigned StubSize = 16;
341   static constexpr unsigned StubToPointerMaxDisplacement = 1 << 31;
342   static constexpr unsigned ResolverCodeSize = 0x148;
343 
344   /// Write the resolver code into the given memory. The user is
345   /// responsible for allocating the memory and setting permissions.
346   ///
347   /// ReentryFnAddr should be the address of a function whose signature matches
348   /// void* (*)(void *TrampolineAddr, void *ReentryCtxAddr). The ReentryCtxAddr
349   /// argument of writeResolverCode will be passed as the second argument to
350   /// the function at ReentryFnAddr.
351   static void writeResolverCode(char *ResolverWorkingMem,
352                                 JITTargetAddress ResolverTargetAddress,
353                                 JITTargetAddress ReentryFnAddr,
354                                 JITTargetAddress ReentryCtxAddr);
355 
356   /// Write the requested number of trampolines into the given memory,
357   /// which must be big enough to hold 1 pointer, plus NumTrampolines
358   /// trampolines.
359   static void writeTrampolines(char *TrampolineBlockWorkingMem,
360                                JITTargetAddress TrampolineBlockTargetAddress,
361                                JITTargetAddress ResolverFnAddr,
362                                unsigned NumTrampolines);
363   /// Write NumStubs indirect stubs to working memory at StubsBlockWorkingMem.
364   /// Stubs will be written as if linked at StubsBlockTargetAddress, with the
365   /// Nth stub using the Nth pointer in memory starting at
366   /// PointersBlockTargetAddress.
367   static void writeIndirectStubsBlock(
368       char *StubsBlockWorkingMem, JITTargetAddress StubsBlockTargetAddress,
369       JITTargetAddress PointersBlockTargetAddress, unsigned NumStubs);
370 };
371 
372 } // end namespace orc
373 } // end namespace llvm
374 
375 #endif // LLVM_EXECUTIONENGINE_ORC_ORCABISUPPORT_H
376