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