LCOV - code coverage report
Current view: top level - asmjit - x86compiler.h (source / functions) Hit Total Coverage
Test: plumed test coverage (other modules) Lines: 5 5 100.0 %
Date: 2026-11-05 10:19:06 Functions: 1 1 100.0 %

          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(&reg, 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(&reg, 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

Generated by: LCOV version 1.16