Line data Source code
1 : #ifdef __PLUMED_HAS_ASMJIT
2 : // This file is part of AsmJit project <https://asmjit.com>
3 : //
4 : // See asmjit.h or LICENSE.md for license and copyright information
5 : // SPDX-License-Identifier: Zlib
6 :
7 : #ifndef ASMJIT_X86_X86COMPILER_H_INCLUDED
8 : #define ASMJIT_X86_X86COMPILER_H_INCLUDED
9 :
10 : #include "api-config.h"
11 : #ifndef ASMJIT_NO_COMPILER
12 :
13 : #include "compiler.h"
14 : #include "type.h"
15 : #include "x86emitter.h"
16 :
17 : ASMJIT_BEGIN_SUB_NAMESPACE(x86)
18 :
19 : //! \addtogroup asmjit_x86
20 : //! \{
21 :
22 : //! X86/X64 compiler implementation.
23 : //!
24 : //! ### Compiler Basics
25 : //!
26 : //! The first \ref x86::Compiler example shows how to generate a function that simply returns an integer value. It's
27 : //! an analogy to the first Assembler example:
28 : //!
29 : //! ```
30 : //! #include <asmjit/x86.h>
31 : //! #include <stdio.h>
32 : //!
33 : //! using namespace asmjit;
34 : //!
35 : //! // Signature of the generated function.
36 : //! typedef int (*Func)(void);
37 : //!
38 : //! int main() {
39 : //! JitRuntime rt; // Runtime specialized for JIT code execution.
40 : //! CodeHolder code; // Holds code and relocation information.
41 : //!
42 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
43 : //! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
44 : //!
45 : //! cc.addFunc(FuncSignatureT<int>());// Begin a function of `int fn(void)` signature.
46 : //!
47 : //! x86::Gp vReg = cc.newGpd(); // Create a 32-bit general purpose register.
48 : //! cc.mov(vReg, 1); // Move one to our virtual register `vReg`.
49 : //! cc.ret(vReg); // Return `vReg` from the function.
50 : //!
51 : //! cc.endFunc(); // End of the function body.
52 : //! cc.finalize(); // Translate and assemble the whole 'cc' content.
53 : //! // ----> x86::Compiler is no longer needed from here and can be destroyed <----
54 : //!
55 : //! Func fn;
56 : //! Error err = rt.add(&fn, &code); // Add the generated code to the runtime.
57 : //! if (err) return 1; // Handle a possible error returned by AsmJit.
58 : //! // ----> CodeHolder is no longer needed from here and can be destroyed <----
59 : //!
60 : //! int result = fn(); // Execute the generated code.
61 : //! printf("%d\n", result); // Print the resulting "1".
62 : //!
63 : //! rt.release(fn); // Explicitly remove the function from the runtime.
64 : //! return 0;
65 : //! }
66 : //! ```
67 : //!
68 : //! The \ref BaseCompiler::addFunc() and \ref BaseCompiler::endFunc() functions are used to define the function and
69 : //! its end. Both must be called per function, but the body doesn't have to be generated in sequence. An example of
70 : //! generating two functions will be shown later. The next example shows more complicated code that contain a loop
71 : //! and generates a simple memory copy function that uses `uint32_t` items:
72 : //!
73 : //! ```
74 : //! #include <asmjit/x86.h>
75 : //! #include <stdio.h>
76 : //!
77 : //! using namespace asmjit;
78 : //!
79 : //! // Signature of the generated function.
80 : //! typedef void (*MemCpy32)(uint32_t* dst, const uint32_t* src, size_t count);
81 : //!
82 : //! int main() {
83 : //! JitRuntime rt; // Runtime specialized for JIT code execution.
84 : //! CodeHolder code; // Holds code and relocation information.
85 : //!
86 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
87 : //! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
88 : //!
89 : //! FuncNode* funcNode = cc.addFunc( // Begin the function of the following signature:
90 : //! FuncSignatureT<void, // Return value - void (no return value).
91 : //! uint32_t*, // 1st argument - uint32_t* (machine reg-size).
92 : //! const uint32_t*, // 2nd argument - uint32_t* (machine reg-size).
93 : //! size_t>()); // 3rd argument - size_t (machine reg-size).
94 : //!
95 : //! Label L_Loop = cc.newLabel(); // Start of the loop.
96 : //! Label L_Exit = cc.newLabel(); // Used to exit early.
97 : //!
98 : //! x86::Gp dst = cc.newIntPtr("dst");// Create `dst` register (destination pointer).
99 : //! x86::Gp src = cc.newIntPtr("src");// Create `src` register (source pointer).
100 : //! x86::Gp i = cc.newUIntPtr("i"); // Create `i` register (loop counter).
101 : //!
102 : //! funcNode->setArg(0, dst); // Assign `dst` argument.
103 : //! funcNode->setArg(1, src); // Assign `src` argument.
104 : //! funcNode->setArg(2, i); // Assign `i` argument.
105 : //!
106 : //! cc.test(i, i); // Early exit if length is zero.
107 : //! cc.jz(L_Exit);
108 : //!
109 : //! cc.bind(L_Loop); // Bind the beginning of the loop here.
110 : //!
111 : //! x86::Gp tmp = cc.newInt32("tmp"); // Copy a single dword (4 bytes).
112 : //! cc.mov(tmp, x86::dword_ptr(src)); // Load DWORD from [src] address.
113 : //! cc.mov(x86::dword_ptr(dst), tmp); // Store DWORD to [dst] address.
114 : //!
115 : //! cc.add(src, 4); // Increment `src`.
116 : //! cc.add(dst, 4); // Increment `dst`.
117 : //!
118 : //! cc.dec(i); // Loop until `i` is non-zero.
119 : //! cc.jnz(L_Loop);
120 : //!
121 : //! cc.bind(L_Exit); // Label used by early exit.
122 : //! cc.endFunc(); // End of the function body.
123 : //!
124 : //! cc.finalize(); // Translate and assemble the whole 'cc' content.
125 : //! // ----> x86::Compiler is no longer needed from here and can be destroyed <----
126 : //!
127 : //! // Add the generated code to the runtime.
128 : //! MemCpy32 memcpy32;
129 : //! Error err = rt.add(&memcpy32, &code);
130 : //!
131 : //! // Handle a possible error returned by AsmJit.
132 : //! if (err)
133 : //! return 1;
134 : //! // ----> CodeHolder is no longer needed from here and can be destroyed <----
135 : //!
136 : //! // Test the generated code.
137 : //! uint32_t input[6] = { 1, 2, 3, 5, 8, 13 };
138 : //! uint32_t output[6];
139 : //! memcpy32(output, input, 6);
140 : //!
141 : //! for (uint32_t i = 0; i < 6; i++)
142 : //! printf("%d\n", output[i]);
143 : //!
144 : //! rt.release(memcpy32);
145 : //! return 0;
146 : //! }
147 : //! ```
148 : //!
149 : //! ### AVX and AVX-512
150 : //!
151 : //! AVX and AVX-512 code generation must be explicitly enabled via \ref FuncFrame to work properly. If it's not setup
152 : //! correctly then Prolog & Epilog would use SSE instead of AVX instructions to work with SIMD registers. In addition,
153 : //! Compiler requires explicitly enable AVX-512 via \ref FuncFrame in order to use all 32 SIMD registers.
154 : //!
155 : //! ```
156 : //! #include <asmjit/x86.h>
157 : //! #include <stdio.h>
158 : //!
159 : //! using namespace asmjit;
160 : //!
161 : //! // Signature of the generated function.
162 : //! typedef void (*Func)(void*);
163 : //!
164 : //! int main() {
165 : //! JitRuntime rt; // Runtime specialized for JIT code execution.
166 : //! CodeHolder code; // Holds code and relocation information.
167 : //!
168 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
169 : //! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
170 : //!
171 : //! FuncNode* funcNode = cc.addFunc(FuncSignatureT<void, void*>());
172 : //!
173 : //! // Use the following to enable AVX and/or AVX-512.
174 : //! funcNode->frame().setAvxEnabled();
175 : //! funcNode->frame().setAvx512Enabled();
176 : //!
177 : //! // Do something with the input pointer.
178 : //! x86::Gp addr = cc.newIntPtr("addr");
179 : //! x86::Zmm vreg = cc.newZmm("vreg");
180 : //!
181 : //! funcNode->setArg(0, addr);
182 : //!
183 : //! cc.vmovdqu32(vreg, x86::ptr(addr));
184 : //! cc.vpaddq(vreg, vreg, vreg);
185 : //! cc.vmovdqu32(x86::ptr(addr), vreg);
186 : //!
187 : //! cc.endFunc(); // End of the function body.
188 : //! cc.finalize(); // Translate and assemble the whole 'cc' content.
189 : //! // ----> x86::Compiler is no longer needed from here and can be destroyed <----
190 : //!
191 : //! Func fn;
192 : //! Error err = rt.add(&fn, &code); // Add the generated code to the runtime.
193 : //! if (err) return 1; // Handle a possible error returned by AsmJit.
194 : //! // ----> CodeHolder is no longer needed from here and can be destroyed <----
195 : //!
196 : //! // Execute the generated code and print some output.
197 : //! uint64_t data[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
198 : //! fn(data);
199 : //! printf("%llu\n", (unsigned long long)data[0]);
200 : //!
201 : //! rt.release(fn); // Explicitly remove the function from the runtime.
202 : //! return 0;
203 : //! }
204 : //! ```
205 : //!
206 : //! ### Recursive Functions
207 : //!
208 : //! It's possible to create more functions by using the same \ref x86::Compiler instance and make links between them.
209 : //! In such case it's important to keep the pointer to \ref FuncNode.
210 : //!
211 : //! The example below creates a simple Fibonacci function that calls itself recursively:
212 : //!
213 : //! ```
214 : //! #include <asmjit/x86.h>
215 : //! #include <stdio.h>
216 : //!
217 : //! using namespace asmjit;
218 : //!
219 : //! // Signature of the generated function.
220 : //! typedef uint32_t (*Fibonacci)(uint32_t x);
221 : //!
222 : //! int main() {
223 : //! JitRuntime rt; // Runtime specialized for JIT code execution.
224 : //! CodeHolder code; // Holds code and relocation information.
225 : //!
226 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
227 : //! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
228 : //!
229 : //! FuncNode* funcNode = cc.addFunc( // Begin of the Fibonacci function, addFunc()
230 : //! FuncSignatureT<int, int>()); // Returns a pointer to the FuncNode node.
231 : //!
232 : //! Label L_Exit = cc.newLabel() // Exit label.
233 : //! x86::Gp x = cc.newUInt32(); // Function x argument.
234 : //! x86::Gp y = cc.newUInt32(); // Temporary.
235 : //!
236 : //! funcNode->setArg(0, x);
237 : //!
238 : //! cc.cmp(x, 3); // Return x if less than 3.
239 : //! cc.jb(L_Exit);
240 : //!
241 : //! cc.mov(y, x); // Make copy of the original x.
242 : //! cc.dec(x); // Decrease x.
243 : //!
244 : //! InvokeNode* invokeNode; // Function invocation:
245 : //! cc.invoke(&invokeNode, // - InvokeNode (output).
246 : //! funcNode->label(), // - Function address or Label.
247 : //! FuncSignatureT<int, int>()); // - Function signature.
248 : //!
249 : //! invokeNode->setArg(0, x); // Assign x as the first argument.
250 : //! invokeNode->setRet(0, x); // Assign x as a return value as well.
251 : //!
252 : //! cc.add(x, y); // Combine the return value with y.
253 : //!
254 : //! cc.bind(L_Exit);
255 : //! cc.ret(x); // Return x.
256 : //! cc.endFunc(); // End of the function body.
257 : //!
258 : //! cc.finalize(); // Translate and assemble the whole 'cc' content.
259 : //! // ----> x86::Compiler is no longer needed from here and can be destroyed <----
260 : //!
261 : //! Fibonacci fib;
262 : //! Error err = rt.add(&fib, &code); // Add the generated code to the runtime.
263 : //! if (err) return 1; // Handle a possible error returned by AsmJit.
264 : //! // ----> CodeHolder is no longer needed from here and can be destroyed <----
265 : //!
266 : //! // Test the generated code.
267 : //! printf("Fib(%u) -> %u\n", 8, fib(8));
268 : //!
269 : //! rt.release(fib);
270 : //! return 0;
271 : //! }
272 : //! ```
273 : //!
274 : //! ### Stack Management
275 : //!
276 : //! Function's stack-frame is managed automatically, which is used by the register allocator to spill virtual
277 : //! registers. It also provides an interface to allocate user-defined block of the stack, which can be used as
278 : //! a temporary storage by the generated function. In the following example a stack of 256 bytes size is allocated,
279 : //! filled by bytes starting from 0 to 255 and then iterated again to sum all the values.
280 : //!
281 : //! ```
282 : //! #include <asmjit/x86.h>
283 : //! #include <stdio.h>
284 : //!
285 : //! using namespace asmjit;
286 : //!
287 : //! // Signature of the generated function.
288 : //! typedef int (*Func)(void);
289 : //!
290 : //! int main() {
291 : //! JitRuntime rt; // Runtime specialized for JIT code execution.
292 : //! CodeHolder code; // Holds code and relocation information.
293 : //!
294 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
295 : //! x86::Compiler cc(&code); // Create and attach x86::Compiler to code.
296 : //!
297 : //! cc.addFunc(FuncSignatureT<int>());// Create a function that returns int.
298 : //!
299 : //! x86::Gp p = cc.newIntPtr("p");
300 : //! x86::Gp i = cc.newIntPtr("i");
301 : //!
302 : //! // Allocate 256 bytes on the stack aligned to 4 bytes.
303 : //! x86::Mem stack = cc.newStack(256, 4);
304 : //!
305 : //! x86::Mem stackIdx(stack); // Copy of stack with i added.
306 : //! stackIdx.setIndex(i); // stackIdx <- stack[i].
307 : //! stackIdx.setSize(1); // stackIdx <- byte ptr stack[i].
308 : //!
309 : //! // Load a stack address to `p`. This step is purely optional and shows
310 : //! // that `lea` is useful to load a memory operands address (even absolute)
311 : //! // to a general purpose register.
312 : //! cc.lea(p, stack);
313 : //!
314 : //! // Clear i (xor is a C++ keyword, hence 'xor_' is used instead).
315 : //! cc.xor_(i, i);
316 : //!
317 : //! Label L1 = cc.newLabel();
318 : //! Label L2 = cc.newLabel();
319 : //!
320 : //! cc.bind(L1); // First loop, fill the stack.
321 : //! cc.mov(stackIdx, i.r8()); // stack[i] = uint8_t(i).
322 : //!
323 : //! cc.inc(i); // i++;
324 : //! cc.cmp(i, 256); // if (i < 256)
325 : //! cc.jb(L1); // goto L1;
326 : //!
327 : //! // Second loop, sum all bytes stored in `stack`.
328 : //! x86::Gp sum = cc.newInt32("sum");
329 : //! x86::Gp val = cc.newInt32("val");
330 : //!
331 : //! cc.xor_(i, i);
332 : //! cc.xor_(sum, sum);
333 : //!
334 : //! cc.bind(L2);
335 : //!
336 : //! cc.movzx(val, stackIdx); // val = uint32_t(stack[i]);
337 : //! cc.add(sum, val); // sum += val;
338 : //!
339 : //! cc.inc(i); // i++;
340 : //! cc.cmp(i, 256); // if (i < 256)
341 : //! cc.jb(L2); // goto L2;
342 : //!
343 : //! cc.ret(sum); // Return the `sum` of all values.
344 : //! cc.endFunc(); // End of the function body.
345 : //!
346 : //! cc.finalize(); // Translate and assemble the whole 'cc' content.
347 : //! // ----> x86::Compiler is no longer needed from here and can be destroyed <----
348 : //!
349 : //! Func func;
350 : //! Error err = rt.add(&func, &code); // Add the generated code to the runtime.
351 : //! if (err) return 1; // Handle a possible error returned by AsmJit.
352 : //! // ----> CodeHolder is no longer needed from here and can be destroyed <----
353 : //!
354 : //! printf("Func() -> %d\n", func()); // Test the generated code.
355 : //!
356 : //! rt.release(func);
357 : //! return 0;
358 : //! }
359 : //! ```
360 : //!
361 : //! ### Constant Pool
362 : //!
363 : //! Compiler provides two constant pools for a general purpose code generation:
364 : //!
365 : //! - Local constant pool - Part of \ref FuncNode, can be only used by a single function and added after the
366 : //! function epilog sequence (after `ret` instruction).
367 : //!
368 : //! - Global constant pool - Part of \ref BaseCompiler, flushed at the end of the generated code by \ref
369 : //! BaseEmitter::finalize().
370 : //!
371 : //! The example below illustrates how a built-in constant pool can be used:
372 : //!
373 : //! ```
374 : //! #include <asmjit/x86.h>
375 : //!
376 : //! using namespace asmjit;
377 : //!
378 : //! static void exampleUseOfConstPool(x86::Compiler& cc) {
379 : //! cc.addFunc(FuncSignatureT<int>());
380 : //!
381 : //! x86::Gp v0 = cc.newGpd("v0");
382 : //! x86::Gp v1 = cc.newGpd("v1");
383 : //!
384 : //! x86::Mem c0 = cc.newInt32Const(ConstPoolScope::kLocal, 200);
385 : //! x86::Mem c1 = cc.newInt32Const(ConstPoolScope::kLocal, 33);
386 : //!
387 : //! cc.mov(v0, c0);
388 : //! cc.mov(v1, c1);
389 : //! cc.add(v0, v1);
390 : //!
391 : //! cc.ret(v0);
392 : //! cc.endFunc();
393 : //! }
394 : //! ```
395 : //!
396 : //! ### Jump Tables
397 : //!
398 : //! x86::Compiler supports `jmp` instruction with reg/mem operand, which is a commonly used pattern to implement
399 : //! indirect jumps within a function, for example to implement `switch()` statement in a programming languages.
400 : //! By default AsmJit assumes that every basic block can be a possible jump target as it's unable to deduce targets
401 : //! from instruction's operands. This is a very pessimistic default that should be avoided if possible as it's costly
402 : //! and very unfriendly to liveness analysis and register allocation.
403 : //!
404 : //! Instead of relying on such pessimistic default behavior, let's use \ref JumpAnnotation to annotate a jump where
405 : //! all targets are known:
406 : //!
407 : //! ```
408 : //! #include <asmjit/x86.h>
409 : //!
410 : //! using namespace asmjit;
411 : //!
412 : //! static void exampleUseOfIndirectJump(x86::Compiler& cc) {
413 : //! FuncNode* funcNode = cc.addFunc(FuncSignatureT<float, float, float, uint32_t>(CallConvId::kHost));
414 : //!
415 : //! // Function arguments
416 : //! x86::Xmm a = cc.newXmmSs("a");
417 : //! x86::Xmm b = cc.newXmmSs("b");
418 : //! x86::Gp op = cc.newUInt32("op");
419 : //!
420 : //! x86::Gp target = cc.newIntPtr("target");
421 : //! x86::Gp offset = cc.newIntPtr("offset");
422 : //!
423 : //! Label L_Table = cc.newLabel();
424 : //! Label L_Add = cc.newLabel();
425 : //! Label L_Sub = cc.newLabel();
426 : //! Label L_Mul = cc.newLabel();
427 : //! Label L_Div = cc.newLabel();
428 : //! Label L_End = cc.newLabel();
429 : //!
430 : //! funcNode->setArg(0, a);
431 : //! funcNode->setArg(1, b);
432 : //! funcNode->setArg(2, op);
433 : //!
434 : //! // Jump annotation is a building block that allows to annotate all possible targets where `jmp()` can
435 : //! // jump. It then drives the CFG construction and liveness analysis, which impacts register allocation.
436 : //! JumpAnnotation* annotation = cc.newJumpAnnotation();
437 : //! annotation->addLabel(L_Add);
438 : //! annotation->addLabel(L_Sub);
439 : //! annotation->addLabel(L_Mul);
440 : //! annotation->addLabel(L_Div);
441 : //!
442 : //! // Most likely not the common indirect jump approach, but it
443 : //! // doesn't really matter how final address is calculated. The
444 : //! // most important path using JumpAnnotation with `jmp()`.
445 : //! cc.lea(offset, x86::ptr(L_Table));
446 : //! if (cc.is64Bit())
447 : //! cc.movsxd(target, x86::dword_ptr(offset, op.cloneAs(offset), 2));
448 : //! else
449 : //! cc.mov(target, x86::dword_ptr(offset, op.cloneAs(offset), 2));
450 : //! cc.add(target, offset);
451 : //! cc.jmp(target, annotation);
452 : //!
453 : //! // Acts like a switch() statement in C.
454 : //! cc.bind(L_Add);
455 : //! cc.addss(a, b);
456 : //! cc.jmp(L_End);
457 : //!
458 : //! cc.bind(L_Sub);
459 : //! cc.subss(a, b);
460 : //! cc.jmp(L_End);
461 : //!
462 : //! cc.bind(L_Mul);
463 : //! cc.mulss(a, b);
464 : //! cc.jmp(L_End);
465 : //!
466 : //! cc.bind(L_Div);
467 : //! cc.divss(a, b);
468 : //!
469 : //! cc.bind(L_End);
470 : //! cc.ret(a);
471 : //!
472 : //! cc.endFunc();
473 : //!
474 : //! // Relative int32_t offsets of `L_XXX - L_Table`.
475 : //! cc.bind(L_Table);
476 : //! cc.embedLabelDelta(L_Add, L_Table, 4);
477 : //! cc.embedLabelDelta(L_Sub, L_Table, 4);
478 : //! cc.embedLabelDelta(L_Mul, L_Table, 4);
479 : //! cc.embedLabelDelta(L_Div, L_Table, 4);
480 : //! }
481 : //! ```
482 : class ASMJIT_VIRTAPI Compiler
483 : : public BaseCompiler,
484 : public EmitterExplicitT<Compiler> {
485 : public:
486 : ASMJIT_NONCOPYABLE(Compiler)
487 : typedef BaseCompiler Base;
488 :
489 : //! \name Construction & Destruction
490 : //! \{
491 :
492 : ASMJIT_API explicit Compiler(CodeHolder* code = nullptr) noexcept;
493 : ASMJIT_API virtual ~Compiler() noexcept;
494 :
495 : //! \}
496 :
497 : //! \name Virtual Registers
498 : //! \{
499 :
500 : #ifndef ASMJIT_NO_LOGGING
501 : # define ASMJIT_NEW_REG_FMT(OUT, PARAM, FORMAT, ARGS) \
502 : _newRegFmt(&OUT, PARAM, FORMAT, ARGS)
503 : #else
504 : # define ASMJIT_NEW_REG_FMT(OUT, PARAM, FORMAT, ARGS) \
505 : DebugUtils::unused(FORMAT); \
506 : DebugUtils::unused(std::forward<Args>(args)...); \
507 : _newReg(&OUT, PARAM)
508 : #endif
509 :
510 : #define ASMJIT_NEW_REG_CUSTOM(FUNC, REG) \
511 : inline REG FUNC(TypeId typeId) { \
512 : REG reg(Globals::NoInit); \
513 : _newReg(®, typeId); \
514 : return reg; \
515 : } \
516 : \
517 : template<typename... Args> \
518 : inline REG FUNC(TypeId typeId, const char* fmt, Args&&... args) { \
519 : REG reg(Globals::NoInit); \
520 : ASMJIT_NEW_REG_FMT(reg, typeId, fmt, std::forward<Args>(args)...); \
521 : return reg; \
522 : }
523 :
524 : #define ASMJIT_NEW_REG_TYPED(FUNC, REG, TYPE_ID) \
525 : inline REG FUNC() { \
526 : REG reg(Globals::NoInit); \
527 : _newReg(®, TYPE_ID); \
528 : return reg; \
529 : } \
530 : \
531 : template<typename... Args> \
532 : inline REG FUNC(const char* fmt, Args&&... args) { \
533 : REG reg(Globals::NoInit); \
534 : ASMJIT_NEW_REG_FMT(reg, TYPE_ID, fmt, std::forward<Args>(args)...); \
535 : return reg; \
536 : }
537 :
538 : template<typename RegT>
539 : inline RegT newSimilarReg(const RegT& ref) {
540 : RegT reg(Globals::NoInit);
541 : _newReg(reg, ref);
542 : return reg;
543 : }
544 :
545 : template<typename RegT, typename... Args>
546 : inline RegT newSimilarReg(const RegT& ref, const char* fmt, Args&&... args) {
547 : RegT reg(Globals::NoInit);
548 : ASMJIT_NEW_REG_FMT(reg, ref, fmt, std::forward<Args>(args)...);
549 : return reg;
550 : }
551 :
552 : ASMJIT_NEW_REG_CUSTOM(newReg , Reg )
553 : ASMJIT_NEW_REG_CUSTOM(newGp , Gp )
554 : ASMJIT_NEW_REG_CUSTOM(newVec , Vec )
555 : ASMJIT_NEW_REG_CUSTOM(newK , KReg)
556 :
557 : ASMJIT_NEW_REG_TYPED(newInt8 , Gp , TypeId::kInt8)
558 : ASMJIT_NEW_REG_TYPED(newUInt8 , Gp , TypeId::kUInt8)
559 : ASMJIT_NEW_REG_TYPED(newInt16 , Gp , TypeId::kInt16)
560 : ASMJIT_NEW_REG_TYPED(newUInt16 , Gp , TypeId::kUInt16)
561 : ASMJIT_NEW_REG_TYPED(newInt32 , Gp , TypeId::kInt32)
562 : ASMJIT_NEW_REG_TYPED(newUInt32 , Gp , TypeId::kUInt32)
563 : ASMJIT_NEW_REG_TYPED(newInt64 , Gp , TypeId::kInt64)
564 : ASMJIT_NEW_REG_TYPED(newUInt64 , Gp , TypeId::kUInt64)
565 78651 : ASMJIT_NEW_REG_TYPED(newIntPtr , Gp , TypeId::kIntPtr)
566 : ASMJIT_NEW_REG_TYPED(newUIntPtr, Gp , TypeId::kUIntPtr)
567 :
568 : ASMJIT_NEW_REG_TYPED(newGpb , Gp , TypeId::kUInt8)
569 : ASMJIT_NEW_REG_TYPED(newGpw , Gp , TypeId::kUInt16)
570 : ASMJIT_NEW_REG_TYPED(newGpd , Gp , TypeId::kUInt32)
571 : ASMJIT_NEW_REG_TYPED(newGpq , Gp , TypeId::kUInt64)
572 : ASMJIT_NEW_REG_TYPED(newGpz , Gp , TypeId::kUIntPtr)
573 : ASMJIT_NEW_REG_TYPED(newXmm , Xmm , TypeId::kInt32x4)
574 : ASMJIT_NEW_REG_TYPED(newXmmSs , Xmm , TypeId::kFloat32x1)
575 194938 : ASMJIT_NEW_REG_TYPED(newXmmSd , Xmm , TypeId::kFloat64x1)
576 : ASMJIT_NEW_REG_TYPED(newXmmPs , Xmm , TypeId::kFloat32x4)
577 : ASMJIT_NEW_REG_TYPED(newXmmPd , Xmm , TypeId::kFloat64x2)
578 : ASMJIT_NEW_REG_TYPED(newYmm , Ymm , TypeId::kInt32x8)
579 : ASMJIT_NEW_REG_TYPED(newYmmPs , Ymm , TypeId::kFloat32x8)
580 : ASMJIT_NEW_REG_TYPED(newYmmPd , Ymm , TypeId::kFloat64x4)
581 : ASMJIT_NEW_REG_TYPED(newZmm , Zmm , TypeId::kInt32x16)
582 : ASMJIT_NEW_REG_TYPED(newZmmPs , Zmm , TypeId::kFloat32x16)
583 : ASMJIT_NEW_REG_TYPED(newZmmPd , Zmm , TypeId::kFloat64x8)
584 : ASMJIT_NEW_REG_TYPED(newMm , Mm , TypeId::kMmx64)
585 : ASMJIT_NEW_REG_TYPED(newKb , KReg, TypeId::kMask8)
586 : ASMJIT_NEW_REG_TYPED(newKw , KReg, TypeId::kMask16)
587 : ASMJIT_NEW_REG_TYPED(newKd , KReg, TypeId::kMask32)
588 : ASMJIT_NEW_REG_TYPED(newKq , KReg, TypeId::kMask64)
589 :
590 : #undef ASMJIT_NEW_REG_TYPED
591 : #undef ASMJIT_NEW_REG_CUSTOM
592 : #undef ASMJIT_NEW_REG_FMT
593 :
594 : //! \}
595 :
596 : //! \name Stack
597 : //! \{
598 :
599 : //! Creates a new memory chunk allocated on the current function's stack.
600 : inline Mem newStack(uint32_t size, uint32_t alignment, const char* name = nullptr) {
601 : Mem m(Globals::NoInit);
602 : _newStack(&m, size, alignment, name);
603 : return m;
604 : }
605 :
606 : //! \}
607 :
608 : //! \name Constants
609 : //! \{
610 :
611 : //! Put data to a constant-pool and get a memory reference to it.
612 : inline Mem newConst(ConstPoolScope scope, const void* data, size_t size) {
613 : Mem m(Globals::NoInit);
614 : _newConst(&m, scope, data, size);
615 : return m;
616 : }
617 :
618 : //! Put a BYTE `val` to a constant-pool.
619 : inline Mem newByteConst(ConstPoolScope scope, uint8_t val) noexcept { return newConst(scope, &val, 1); }
620 : //! Put a WORD `val` to a constant-pool.
621 : inline Mem newWordConst(ConstPoolScope scope, uint16_t val) noexcept { return newConst(scope, &val, 2); }
622 : //! Put a DWORD `val` to a constant-pool.
623 : inline Mem newDWordConst(ConstPoolScope scope, uint32_t val) noexcept { return newConst(scope, &val, 4); }
624 : //! Put a QWORD `val` to a constant-pool.
625 : inline Mem newQWordConst(ConstPoolScope scope, uint64_t val) noexcept { return newConst(scope, &val, 8); }
626 :
627 : //! Put a WORD `val` to a constant-pool.
628 : inline Mem newInt16Const(ConstPoolScope scope, int16_t val) noexcept { return newConst(scope, &val, 2); }
629 : //! Put a WORD `val` to a constant-pool.
630 : inline Mem newUInt16Const(ConstPoolScope scope, uint16_t val) noexcept { return newConst(scope, &val, 2); }
631 : //! Put a DWORD `val` to a constant-pool.
632 : inline Mem newInt32Const(ConstPoolScope scope, int32_t val) noexcept { return newConst(scope, &val, 4); }
633 : //! Put a DWORD `val` to a constant-pool.
634 : inline Mem newUInt32Const(ConstPoolScope scope, uint32_t val) noexcept { return newConst(scope, &val, 4); }
635 : //! Put a QWORD `val` to a constant-pool.
636 : inline Mem newInt64Const(ConstPoolScope scope, int64_t val) noexcept { return newConst(scope, &val, 8); }
637 : //! Put a QWORD `val` to a constant-pool.
638 : inline Mem newUInt64Const(ConstPoolScope scope, uint64_t val) noexcept { return newConst(scope, &val, 8); }
639 :
640 : //! Put a SP-FP `val` to a constant-pool.
641 : inline Mem newFloatConst(ConstPoolScope scope, float val) noexcept { return newConst(scope, &val, 4); }
642 : //! Put a DP-FP `val` to a constant-pool.
643 : inline Mem newDoubleConst(ConstPoolScope scope, double val) noexcept { return newConst(scope, &val, 8); }
644 :
645 : //! \}
646 :
647 : //! \name Instruction Options
648 : //! \{
649 :
650 : //! Force the compiler to not follow the conditional or unconditional jump.
651 : inline Compiler& unfollow() noexcept { addInstOptions(InstOptions::kUnfollow); return *this; }
652 : //! Tell the compiler that the destination variable will be overwritten.
653 : inline Compiler& overwrite() noexcept { addInstOptions(InstOptions::kOverwrite); return *this; }
654 :
655 : //! \}
656 :
657 : //! \name Function Call & Ret Intrinsics
658 : //! \{
659 :
660 : //! Invoke a function call without `target` type enforcement.
661 : inline Error invoke_(InvokeNode** out, const Operand_& target, const FuncSignature& signature) {
662 9639 : return addInvokeNode(out, Inst::kIdCall, target, signature);
663 : }
664 :
665 : //! Invoke a function call of the given `target` and `signature` and store the added node to `out`.
666 : //!
667 : //! Creates a new \ref InvokeNode, initializes all the necessary members to match the given function `signature`,
668 : //! adds the node to the compiler, and stores its pointer to `out`. The operation is atomic, if anything fails
669 : //! nullptr is stored in `out` and error code is returned.
670 94 : inline Error invoke(InvokeNode** out, const Gp& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
671 : //! \overload
672 : inline Error invoke(InvokeNode** out, const Mem& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
673 : //! \overload
674 : inline Error invoke(InvokeNode** out, const Label& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
675 : //! \overload
676 : inline Error invoke(InvokeNode** out, const Imm& target, const FuncSignature& signature) { return invoke_(out, target, signature); }
677 : //! \overload
678 : inline Error invoke(InvokeNode** out, uint64_t target, const FuncSignature& signature) { return invoke_(out, Imm(int64_t(target)), signature); }
679 :
680 : //! Return from function.
681 : inline Error ret() { return addRet(Operand(), Operand()); }
682 : //! \overload
683 27492 : inline Error ret(const BaseReg& o0) { return addRet(o0, Operand()); }
684 : //! \overload
685 : inline Error ret(const BaseReg& o0, const BaseReg& o1) { return addRet(o0, o1); }
686 :
687 : //! \}
688 :
689 : //! \name Jump Tables Support
690 : //! \{
691 :
692 : using EmitterExplicitT<Compiler>::jmp;
693 :
694 : //! Adds a jump to the given `target` with the provided jump `annotation`.
695 : inline Error jmp(const BaseReg& target, JumpAnnotation* annotation) { return emitAnnotatedJump(Inst::kIdJmp, target, annotation); }
696 : //! \overload
697 : inline Error jmp(const BaseMem& target, JumpAnnotation* annotation) { return emitAnnotatedJump(Inst::kIdJmp, target, annotation); }
698 :
699 : //! \}
700 :
701 : //! \name Events
702 : //! \{
703 :
704 : ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
705 : ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
706 :
707 : //! \}
708 :
709 : //! \name Finalize
710 : //! \{
711 :
712 : ASMJIT_API Error finalize() override;
713 :
714 : //! \}
715 : };
716 :
717 : //! \}
718 :
719 : ASMJIT_END_SUB_NAMESPACE
720 :
721 : #endif // !ASMJIT_NO_COMPILER
722 : #endif // ASMJIT_X86_X86COMPILER_H_INCLUDED
723 :
724 : #endif // __PLUMED_HAS_ASMJIT
|