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/Memory.h"
24#include <algorithm>
25#include <cstdint>
26
27namespace llvm {
28namespace orc {
29
30/// Generic ORC ABI support.
31///
32/// This class can be substituted as the target architecture support class for
33/// ORC templates that require one (e.g. IndirectStubsManagers). It does not
34/// support lazy JITing however, and any attempt to use that functionality
35/// will result in execution of an llvm_unreachable.
36class OrcGenericABI {
37public:
38  static const unsigned PointerSize = sizeof(uintptr_t);
39  static const unsigned TrampolineSize = 1;
40  static const unsigned ResolverCodeSize = 1;
41
42  using JITReentryFn = JITTargetAddress (*)(void *CallbackMgr,
43                                            void *TrampolineId);
44
45  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,
46                                void *CallbackMgr) {
47    llvm_unreachable("writeResolverCode is not supported by the generic host "
48                     "support class");
49  }
50
51  static void writeTrampolines(uint8_t *TrampolineMem, void *ResolverAddr,
52                               unsigned NumTrampolines) {
53    llvm_unreachable("writeTrampolines is not supported by the generic host "
54                     "support class");
55  }
56
57  class IndirectStubsInfo {
58  public:
59    const static unsigned StubSize = 1;
60
61    unsigned getNumStubs() const { llvm_unreachable("Not supported"); }
62    void *getStub(unsigned Idx) const { llvm_unreachable("Not supported"); }
63    void **getPtr(unsigned Idx) const { llvm_unreachable("Not supported"); }
64  };
65
66  static Error emitIndirectStubsBlock(IndirectStubsInfo &StubsInfo,
67                                      unsigned MinStubs, void *InitialPtrVal) {
68    llvm_unreachable("emitIndirectStubsBlock is not supported by the generic "
69                     "host support class");
70  }
71};
72
73/// Provide information about stub blocks generated by the
74/// makeIndirectStubsBlock function.
75template <unsigned StubSizeVal> class GenericIndirectStubsInfo {
76public:
77  const static unsigned StubSize = StubSizeVal;
78
79  GenericIndirectStubsInfo() = default;
80  GenericIndirectStubsInfo(unsigned NumStubs, sys::OwningMemoryBlock StubsMem)
81      : NumStubs(NumStubs), StubsMem(std::move(StubsMem)) {}
82  GenericIndirectStubsInfo(GenericIndirectStubsInfo &&Other)
83      : NumStubs(Other.NumStubs), StubsMem(std::move(Other.StubsMem)) {
84    Other.NumStubs = 0;
85  }
86
87  GenericIndirectStubsInfo &operator=(GenericIndirectStubsInfo &&Other) {
88    NumStubs = Other.NumStubs;
89    Other.NumStubs = 0;
90    StubsMem = std::move(Other.StubsMem);
91    return *this;
92  }
93
94  /// Number of stubs in this block.
95  unsigned getNumStubs() const { return NumStubs; }
96
97  /// Get a pointer to the stub at the given index, which must be in
98  /// the range 0 .. getNumStubs() - 1.
99  void *getStub(unsigned Idx) const {
100    return static_cast<char *>(StubsMem.base()) + Idx * StubSize;
101  }
102
103  /// Get a pointer to the implementation-pointer at the given index,
104  /// which must be in the range 0 .. getNumStubs() - 1.
105  void **getPtr(unsigned Idx) const {
106    char *PtrsBase = static_cast<char *>(StubsMem.base()) + NumStubs * StubSize;
107    return reinterpret_cast<void **>(PtrsBase) + Idx;
108  }
109
110private:
111  unsigned NumStubs = 0;
112  sys::OwningMemoryBlock StubsMem;
113};
114
115class OrcAArch64 {
116public:
117  static const unsigned PointerSize = 8;
118  static const unsigned TrampolineSize = 12;
119  static const unsigned ResolverCodeSize = 0x120;
120
121  using IndirectStubsInfo = GenericIndirectStubsInfo<8>;
122
123  using JITReentryFn = JITTargetAddress (*)(void *CallbackMgr,
124                                            void *TrampolineId);
125
126  /// Write the resolver code into the given memory. The user is
127  /// responsible for allocating the memory and setting permissions.
128  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,
129                                void *CallbackMgr);
130
131  /// Write the requested number of trampolines into the given memory,
132  /// which must be big enough to hold 1 pointer, plus NumTrampolines
133  /// trampolines.
134  static void writeTrampolines(uint8_t *TrampolineMem, void *ResolverAddr,
135                               unsigned NumTrampolines);
136
137  /// Emit at least MinStubs worth of indirect call stubs, rounded out to
138  /// the nearest page size.
139  ///
140  ///   E.g. Asking for 4 stubs on x86-64, where stubs are 8-bytes, with 4k
141  /// pages will return a block of 512 stubs (4096 / 8 = 512). Asking for 513
142  /// will return a block of 1024 (2-pages worth).
143  static Error emitIndirectStubsBlock(IndirectStubsInfo &StubsInfo,
144                                      unsigned MinStubs, void *InitialPtrVal);
145};
146
147/// X86_64 code that's common to all ABIs.
148///
149/// X86_64 supports lazy JITing.
150class OrcX86_64_Base {
151public:
152  static const unsigned PointerSize = 8;
153  static const unsigned TrampolineSize = 8;
154
155  using IndirectStubsInfo = GenericIndirectStubsInfo<8>;
156
157  /// Write the requested number of trampolines into the given memory,
158  /// which must be big enough to hold 1 pointer, plus NumTrampolines
159  /// trampolines.
160  static void writeTrampolines(uint8_t *TrampolineMem, void *ResolverAddr,
161                               unsigned NumTrampolines);
162
163  /// Emit at least MinStubs worth of indirect call stubs, rounded out to
164  /// the nearest page size.
165  ///
166  ///   E.g. Asking for 4 stubs on x86-64, where stubs are 8-bytes, with 4k
167  /// pages will return a block of 512 stubs (4096 / 8 = 512). Asking for 513
168  /// will return a block of 1024 (2-pages worth).
169  static Error emitIndirectStubsBlock(IndirectStubsInfo &StubsInfo,
170                                      unsigned MinStubs, void *InitialPtrVal);
171};
172
173/// X86_64 support for SysV ABI (Linux, MacOSX).
174///
175/// X86_64_SysV supports lazy JITing.
176class OrcX86_64_SysV : public OrcX86_64_Base {
177public:
178  static const unsigned ResolverCodeSize = 0x6C;
179
180  using JITReentryFn = JITTargetAddress (*)(void *CallbackMgr,
181                                            void *TrampolineId);
182
183  /// Write the resolver code into the given memory. The user is
184  /// responsible for allocating the memory and setting permissions.
185  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,
186                                void *CallbackMgr);
187};
188
189/// X86_64 support for Win32.
190///
191/// X86_64_Win32 supports lazy JITing.
192class OrcX86_64_Win32 : public OrcX86_64_Base {
193public:
194  static const unsigned ResolverCodeSize = 0x74;
195
196  using JITReentryFn = JITTargetAddress (*)(void *CallbackMgr,
197                                            void *TrampolineId);
198
199  /// Write the resolver code into the given memory. The user is
200  /// responsible for allocating the memory and setting permissions.
201  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,
202                                void *CallbackMgr);
203};
204
205/// I386 support.
206///
207/// I386 supports lazy JITing.
208class OrcI386 {
209public:
210  static const unsigned PointerSize = 4;
211  static const unsigned TrampolineSize = 8;
212  static const unsigned ResolverCodeSize = 0x4a;
213
214  using IndirectStubsInfo = GenericIndirectStubsInfo<8>;
215
216  using JITReentryFn = JITTargetAddress (*)(void *CallbackMgr,
217                                            void *TrampolineId);
218
219  /// Write the resolver code into the given memory. The user is
220  /// responsible for allocating the memory and setting permissions.
221  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,
222                                void *CallbackMgr);
223
224  /// Write the requested number of trampolines into the given memory,
225  /// which must be big enough to hold 1 pointer, plus NumTrampolines
226  /// trampolines.
227  static void writeTrampolines(uint8_t *TrampolineMem, void *ResolverAddr,
228                               unsigned NumTrampolines);
229
230  /// Emit at least MinStubs worth of indirect call stubs, rounded out to
231  /// the nearest page size.
232  ///
233  ///   E.g. Asking for 4 stubs on i386, where stubs are 8-bytes, with 4k
234  /// pages will return a block of 512 stubs (4096 / 8 = 512). Asking for 513
235  /// will return a block of 1024 (2-pages worth).
236  static Error emitIndirectStubsBlock(IndirectStubsInfo &StubsInfo,
237                                      unsigned MinStubs, void *InitialPtrVal);
238};
239
240// @brief Mips32 support.
241//
242// Mips32 supports lazy JITing.
243class OrcMips32_Base {
244public:
245  static const unsigned PointerSize = 4;
246  static const unsigned TrampolineSize = 20;
247  static const unsigned ResolverCodeSize = 0xfc;
248  using IndirectStubsInfo = GenericIndirectStubsInfo<16>;
249
250  using JITReentryFn = JITTargetAddress (*)(void *CallbackMgr,
251                                            void *TrampolineId);
252  /// Write the requested number of trampolines into the given memory,
253  /// which must be big enough to hold 1 pointer, plus NumTrampolines
254  /// trampolines.
255  static void writeTrampolines(uint8_t *TrampolineMem, void *ResolverAddr,unsigned NumTrampolines);
256
257  /// Write the resolver code into the given memory. The user is
258  /// responsible for allocating the memory and setting permissions.
259  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,void *CallbackMgr, bool isBigEndian);
260  /// Emit at least MinStubs worth of indirect call stubs, rounded out to
261  /// the nearest page size.
262  ///
263  ///   E.g. Asking for 4 stubs on Mips32, where stubs are 8-bytes, with 4k
264  /// pages will return a block of 512 stubs (4096 / 8 = 512). Asking for 513
265  /// will return a block of 1024 (2-pages worth).
266  static Error emitIndirectStubsBlock(IndirectStubsInfo &StubsInfo,unsigned MinStubs, void *InitialPtrVal);
267};
268
269
270class OrcMips32Le : public OrcMips32_Base {
271public:
272  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,void *CallbackMgr)
273  { OrcMips32_Base::writeResolverCode(ResolveMem, Reentry, CallbackMgr, false); }
274};
275
276class OrcMips32Be : public OrcMips32_Base {
277public:
278  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,void *CallbackMgr)
279  { OrcMips32_Base::writeResolverCode(ResolveMem, Reentry, CallbackMgr, true); }
280};
281
282// @brief Mips64 support.
283//
284// Mips64 supports lazy JITing.
285class OrcMips64 {
286public:
287  static const unsigned PointerSize = 8;
288  static const unsigned TrampolineSize = 40;
289  static const unsigned ResolverCodeSize = 0x120;
290
291  using IndirectStubsInfo = GenericIndirectStubsInfo<32>;
292  using JITReentryFn = JITTargetAddress (*)(void *CallbackMgr,
293                                            void *TrampolineId);
294  /// Write the resolver code into the given memory. The user is
295  /// responsible for allocating the memory and setting permissions.
296  static void writeResolverCode(uint8_t *ResolveMem, JITReentryFn Reentry,void *CallbackMgr);
297
298  /// Write the requested number of trampolines into the given memory,
299  /// which must be big enough to hold 1 pointer, plus NumTrampolines
300  /// trampolines.
301  static void writeTrampolines(uint8_t *TrampolineMem, void *ResolverAddr,unsigned NumTrampolines);
302
303  /// Emit at least MinStubs worth of indirect call stubs, rounded out to
304  /// the nearest page size.
305  ///
306  ///   E.g. Asking for 4 stubs on Mips64, where stubs are 8-bytes, with 4k
307  /// pages will return a block of 512 stubs (4096 / 8 = 512). Asking for 513
308  /// will return a block of 1024 (2-pages worth).
309  static Error emitIndirectStubsBlock(IndirectStubsInfo &StubsInfo,unsigned MinStubs, void *InitialPtrVal);
310};
311
312 } // end namespace orc
313 } // end namespace llvm
314#endif // LLVM_EXECUTIONENGINE_ORC_ORCABISUPPORT_H
315