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_X86ASSEMBLER_H_INCLUDED
8 : #define ASMJIT_X86_X86ASSEMBLER_H_INCLUDED
9 :
10 : #include "assembler.h"
11 : #include "x86emitter.h"
12 : #include "x86operand.h"
13 :
14 : ASMJIT_BEGIN_SUB_NAMESPACE(x86)
15 :
16 : //! \addtogroup asmjit_x86
17 : //! \{
18 :
19 : //! X86/X64 assembler implementation.
20 : //!
21 : //! x86::Assembler is a code emitter that emits machine code directly into the \ref CodeBuffer. The assembler is capable
22 : //! of targeting both 32-bit and 64-bit instruction sets, the instruction set can be configured through \ref CodeHolder.
23 : //!
24 : //! ### Basics
25 : //!
26 : //! The following example shows a basic use of `x86::Assembler`, how to generate a function that works in both 32-bit
27 : //! and 64-bit modes, and how to connect \ref JitRuntime, \ref CodeHolder, and `x86::Assembler`.
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 (*SumFunc)(const int* arr, size_t count);
37 : //!
38 : //! int main() {
39 : //! JitRuntime rt; // Create a runtime specialized for JIT.
40 : //! CodeHolder code; // Create a CodeHolder.
41 : //!
42 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
43 : //! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
44 : //!
45 : //! // Decide between 32-bit CDECL, WIN64, and SysV64 calling conventions:
46 : //! // 32-BIT - passed all arguments by stack.
47 : //! // WIN64 - passes first 4 arguments by RCX, RDX, R8, and R9.
48 : //! // UNIX64 - passes first 6 arguments by RDI, RSI, RCX, RDX, R8, and R9.
49 : //! x86::Gp arr, cnt;
50 : //! x86::Gp sum = x86::eax; // Use EAX as 'sum' as it's a return register.
51 : //!
52 : //! if (ASMJIT_ARCH_BITS == 64) {
53 : //! #if defined(_WIN32)
54 : //! arr = x86::rcx; // First argument (array ptr).
55 : //! cnt = x86::rdx; // Second argument (number of elements)
56 : //! #else
57 : //! arr = x86::rdi; // First argument (array ptr).
58 : //! cnt = x86::rsi; // Second argument (number of elements)
59 : //! #endif
60 : //! }
61 : //! else {
62 : //! arr = x86::edx; // Use EDX to hold the array pointer.
63 : //! cnt = x86::ecx; // Use ECX to hold the counter.
64 : //! // Fetch first and second arguments from [ESP + 4] and [ESP + 8].
65 : //! a.mov(arr, x86::ptr(x86::esp, 4));
66 : //! a.mov(cnt, x86::ptr(x86::esp, 8));
67 : //! }
68 : //!
69 : //! Label Loop = a.newLabel(); // To construct the loop, we need some labels.
70 : //! Label Exit = a.newLabel();
71 : //!
72 : //! a.xor_(sum, sum); // Clear 'sum' register (shorter than 'mov').
73 : //! a.test(cnt, cnt); // Border case:
74 : //! a.jz(Exit); // If 'cnt' is zero jump to 'Exit' now.
75 : //!
76 : //! a.bind(Loop); // Start of a loop iteration.
77 : //! a.add(sum, x86::dword_ptr(arr)); // Add int at [arr] to 'sum'.
78 : //! a.add(arr, 4); // Increment 'arr' pointer.
79 : //! a.dec(cnt); // Decrease 'cnt'.
80 : //! a.jnz(Loop); // If not zero jump to 'Loop'.
81 : //!
82 : //! a.bind(Exit); // Exit to handle the border case.
83 : //! a.ret(); // Return from function ('sum' == 'eax').
84 : //! // ----> x86::Assembler is no longer needed from here and can be destroyed <----
85 : //!
86 : //! SumFunc fn;
87 : //! Error err = rt.add(&fn, &code); // Add the generated code to the runtime.
88 : //!
89 : //! if (err) return 1; // Handle a possible error returned by AsmJit.
90 : //! // ----> CodeHolder is no longer needed from here and can be destroyed <----
91 : //!
92 : //! static const int array[6] = { 4, 8, 15, 16, 23, 42 };
93 : //!
94 : //! int result = fn(array, 6); // Execute the generated code.
95 : //! printf("%d\n", result); // Print sum of array (108).
96 : //!
97 : //! rt.release(fn); // Explicitly remove the function from the runtime
98 : //! return 0; // Everything successful...
99 : //! }
100 : //! ```
101 : //!
102 : //! The example should be self-explanatory. It shows how to work with labels, how to use operands, and how to emit
103 : //! instructions that can use different registers based on runtime selection. It implements 32-bit CDECL, WIN64,
104 : //! and SysV64 caling conventions and will work on most X86/X64 environments.
105 : //!
106 : //! Although functions prologs / epilogs can be implemented manually, AsmJit provides utilities that can be used
107 : //! to create function prologs and epilogs automatically, see \ref asmjit_function for more details.
108 : //!
109 : //! ### Instruction Validation
110 : //!
111 : //! Assembler prefers speed over strictness by default. The implementation checks the type of operands and fails
112 : //! if the signature of types is invalid, however, it does only basic checks regarding registers and their groups
113 : //! used in instructions. It's possible to pass operands that don't form any valid signature to the implementation
114 : //! and succeed. This is usually not a problem as Assembler provides typed API so operand types are normally checked
115 : //! by C++ compiler at compile time, however, Assembler is fully dynamic and its \ref emit() function can be called
116 : //! with any instruction id, options, and operands. Moreover, it's also possible to form instructions that will be
117 : //! accepted by the typed API, for example by calling `mov(x86::eax, x86::al)` - the C++ compiler won't see a problem
118 : //! as both EAX and AL are \ref Gp registers.
119 : //!
120 : //! To help with common mistakes AsmJit allows to activate instruction validation. This feature instruments
121 : //! the Assembler to call \ref InstAPI::validate() before it attempts to encode any instruction.
122 : //!
123 : //! The example below illustrates how validation can be turned on:
124 : //!
125 : //! ```
126 : //! #include <asmjit/x86.h>
127 : //! #include <stdio.h>
128 : //!
129 : //! using namespace asmjit;
130 : //!
131 : //! int main(int argc, char* argv[]) {
132 : //! JitRuntime rt; // Create a runtime specialized for JIT.
133 : //! CodeHolder code; // Create a CodeHolder.
134 : //!
135 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
136 : //! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
137 : //!
138 : //! // Enable strict validation.
139 : //! a.addDiagnosticOptions(DiagnosticOptions::kValidateAssembler);
140 : //!
141 : //! // Try to encode invalid or ill-formed instructions.
142 : //! Error err;
143 : //!
144 : //! // Invalid instruction.
145 : //! err = a.mov(x86::eax, x86::al);
146 : //! printf("Status: %s\n", DebugUtils::errorAsString(err));
147 : //!
148 : //! // Invalid instruction.
149 : //! err = a.emit(x86::Inst::kIdMovss, x86::eax, x86::xmm0);
150 : //! printf("Status: %s\n", DebugUtils::errorAsString(err));
151 : //!
152 : //! // Ambiguous operand size - the pointer requires size.
153 : //! err = a.inc(x86::ptr(x86::rax), 1);
154 : //! printf("Status: %s\n", DebugUtils::errorAsString(err));
155 : //!
156 : //! return 0;
157 : //! }
158 : //! ```
159 : //!
160 : //! ### Native Registers
161 : //!
162 : //! All emitters provide functions to construct machine-size registers depending on the target. This feature is
163 : //! for users that want to write code targeting both 32-bit and 64-bit architectures at the same time. In AsmJit
164 : //! terminology such registers have prefix `z`, so for example on X86 architecture the following native registers
165 : //! are provided:
166 : //!
167 : //! - `zax` - mapped to either `eax` or `rax`
168 : //! - `zbx` - mapped to either `ebx` or `rbx`
169 : //! - `zcx` - mapped to either `ecx` or `rcx`
170 : //! - `zdx` - mapped to either `edx` or `rdx`
171 : //! - `zsp` - mapped to either `esp` or `rsp`
172 : //! - `zbp` - mapped to either `ebp` or `rbp`
173 : //! - `zsi` - mapped to either `esi` or `rsi`
174 : //! - `zdi` - mapped to either `edi` or `rdi`
175 : //!
176 : //! They are accessible through \ref x86::Assembler, \ref x86::Builder, and \ref x86::Compiler. The example below
177 : //! illustrates how to use this feature:
178 : //!
179 : //! ```
180 : //! #include <asmjit/x86.h>
181 : //! #include <stdio.h>
182 : //!
183 : //! using namespace asmjit;
184 : //!
185 : //! typedef int (*Func)(void);
186 : //!
187 : //! int main(int argc, char* argv[]) {
188 : //! JitRuntime rt; // Create a runtime specialized for JIT.
189 : //! CodeHolder code; // Create a CodeHolder.
190 : //!
191 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
192 : //! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
193 : //!
194 : //! // Let's get these registers from x86::Assembler.
195 : //! x86::Gp zbp = a.zbp();
196 : //! x86::Gp zsp = a.zsp();
197 : //!
198 : //! int stackSize = 32;
199 : //!
200 : //! // Function prolog.
201 : //! a.push(zbp);
202 : //! a.mov(zbp, zsp);
203 : //! a.sub(zsp, stackSize);
204 : //!
205 : //! // ... emit some code (this just sets return value to zero) ...
206 : //! a.xor_(x86::eax, x86::eax);
207 : //!
208 : //! // Function epilog and return.
209 : //! a.mov(zsp, zbp);
210 : //! a.pop(zbp);
211 : //! a.ret();
212 : //!
213 : //! // To make the example complete let's call it.
214 : //! Func fn;
215 : //! Error err = rt.add(&fn, &code); // Add the generated code to the runtime.
216 : //! if (err) return 1; // Handle a possible error returned by AsmJit.
217 : //!
218 : //! int result = fn(); // Execute the generated code.
219 : //! printf("%d\n", result); // Print the resulting "0".
220 : //!
221 : //! rt.release(fn); // Remove the function from the runtime.
222 : //! return 0;
223 : //! }
224 : //! ```
225 : //!
226 : //! The example just returns `0`, but the function generated contains a standard prolog and epilog sequence and the
227 : //! function itself reserves 32 bytes of local stack. The advantage is clear - a single code-base can handle multiple
228 : //! targets easily. If you want to create a register of native size dynamically by specifying its id it's also possible:
229 : //!
230 : //! ```
231 : //! void example(x86::Assembler& a) {
232 : //! x86::Gp zax = a.gpz(x86::Gp::kIdAx);
233 : //! x86::Gp zbx = a.gpz(x86::Gp::kIdBx);
234 : //! x86::Gp zcx = a.gpz(x86::Gp::kIdCx);
235 : //! x86::Gp zdx = a.gpz(x86::Gp::kIdDx);
236 : //!
237 : //! // You can also change register's id easily.
238 : //! x86::Gp zsp = zax;
239 : //! zsp.setId(4); // or x86::Gp::kIdSp.
240 : //! }
241 : //! ```
242 : //!
243 : //! ### Data Embedding
244 : //!
245 : //! x86::Assembler extends the standard \ref BaseAssembler with X86/X64 specific conventions that are often used by
246 : //! assemblers to embed data next to the code. The following functions can be used to embed data:
247 : //!
248 : //! - \ref BaseAssembler::embedInt8() - embeds int8_t (portable naming).
249 : //! - \ref BaseAssembler::embedUInt8() - embeds uint8_t (portable naming).
250 : //! - \ref BaseAssembler::embedInt16() - embeds int16_t (portable naming).
251 : //! - \ref BaseAssembler::embedUInt16() - embeds uint16_t (portable naming).
252 : //! - \ref BaseAssembler::embedInt32() - embeds int32_t (portable naming).
253 : //! - \ref BaseAssembler::embedUInt32() - embeds uint32_t (portable naming).
254 : //! - \ref BaseAssembler::embedInt64() - embeds int64_t (portable naming).
255 : //! - \ref BaseAssembler::embedUInt64() - embeds uint64_t (portable naming).
256 : //! - \ref BaseAssembler::embedFloat() - embeds float (portable naming).
257 : //! - \ref BaseAssembler::embedDouble() - embeds double (portable naming).
258 : //!
259 : //! - \ref x86::Assembler::db() - embeds byte (8 bits) (x86 naming).
260 : //! - \ref x86::Assembler::dw() - embeds word (16 bits) (x86 naming).
261 : //! - \ref x86::Assembler::dd() - embeds dword (32 bits) (x86 naming).
262 : //! - \ref x86::Assembler::dq() - embeds qword (64 bits) (x86 naming).
263 : //!
264 : //! The following example illustrates how embed works:
265 : //!
266 : //! ```
267 : //! #include <asmjit/x86.h>
268 : //! using namespace asmjit;
269 : //!
270 : //! void embedData(x86::Assembler& a) {
271 : //! a.db(0xFF); // Embeds 0xFF byte.
272 : //! a.dw(0xFF00); // Embeds 0xFF00 word (little-endian).
273 : //! a.dd(0xFF000000); // Embeds 0xFF000000 dword (little-endian).
274 : //! a.embedFloat(0.4f); // Embeds 0.4f (32-bit float, little-endian).
275 : //! }
276 : //! ```
277 : //!
278 : //! Sometimes it's required to read the data that is embedded after code, for example. This can be done through
279 : //! \ref Label as shown below:
280 : //!
281 : //! ```
282 : //! #include <asmjit/x86.h>
283 : //! using namespace asmjit;
284 : //!
285 : //! void embedData(x86::Assembler& a, const Label& L_Data) {
286 : //! x86::Gp addr = a.zax(); // EAX or RAX.
287 : //! x86::Gp val = x86::edi; // Where to store some value...
288 : //!
289 : //! // Approach 1 - Load the address to register through LEA. This approach
290 : //! // is flexible as the address can be then manipulated, for
291 : //! // example if you have a data array, which would need index.
292 : //! a.lea(addr, L_Data); // Loads the address of the label to EAX or RAX.
293 : //! a.mov(val, dword_ptr(addr));
294 : //!
295 : //! // Approach 2 - Load the data directly by using L_Data in address. It's
296 : //! // worth noting that this doesn't work with indexes in X64
297 : //! // mode. It will use absolute address in 32-bit mode and
298 : //! // relative address (RIP) in 64-bit mode.
299 : //! a.mov(val, dword_ptr(L_Data));
300 : //! }
301 : //! ```
302 : //!
303 : //! ### Label Embedding
304 : //!
305 : //! It's also possible to embed labels. In general AsmJit provides the following options:
306 : //!
307 : //! - \ref BaseEmitter::embedLabel() - Embeds absolute address of a label. This is target dependent and would
308 : //! embed either 32-bit or 64-bit data that embeds absolute label address. This kind of embedding cannot be
309 : //! used in a position independent code.
310 : //!
311 : //! - \ref BaseEmitter::embedLabelDelta() - Embeds a difference between two labels. The size of the difference
312 : //! can be specified so it's possible to embed 8-bit, 16-bit, 32-bit, and 64-bit difference, which is sufficient
313 : //! for most purposes.
314 : //!
315 : //! The following example demonstrates how to embed labels and their differences:
316 : //!
317 : //! ```
318 : //! #include <asmjit/x86.h>
319 : //! using namespace asmjit;
320 : //!
321 : //! void embedLabel(x86::Assembler& a, const Label& L_Data) {
322 : //! // [1] Embed L_Data - the size of the data will be dependent on the target.
323 : //! a.embedLabel(L_Data);
324 : //!
325 : //! // [2] Embed a 32-bit difference of two labels.
326 : //! Label L_Here = a.newLabel();
327 : //! a.bind(L_Here);
328 : //! // Embeds int32_t(L_Data - L_Here).
329 : //! a.embedLabelDelta(L_Data, L_Here, 4);
330 : //! }
331 : //! ```
332 : //!
333 : //! ### Using FuncFrame and FuncDetail with x86::Assembler
334 : //!
335 : //! The example below demonstrates how \ref FuncFrame and \ref FuncDetail can be used together with \ref x86::Assembler
336 : //! to generate a function that will use platform dependent calling conventions automatically depending on the target:
337 : //!
338 : //! ```
339 : //! #include <asmjit/x86.h>
340 : //! #include <stdio.h>
341 : //!
342 : //! using namespace asmjit;
343 : //!
344 : //! typedef void (*SumIntsFunc)(int* dst, const int* a, const int* b);
345 : //!
346 : //! int main(int argc, char* argv[]) {
347 : //! JitRuntime rt; // Create JIT Runtime.
348 : //! CodeHolder code; // Create a CodeHolder.
349 : //!
350 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
351 : //! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
352 : //!
353 : //! // Decide which registers will be mapped to function arguments. Try changing
354 : //! // registers of dst, src_a, and src_b and see what happens in function's
355 : //! // prolog and epilog.
356 : //! x86::Gp dst = a.zax();
357 : //! x86::Gp src_a = a.zcx();
358 : //! x86::Gp src_b = a.zdx();
359 : //!
360 : //! X86::Xmm vec0 = x86::xmm0;
361 : //! X86::Xmm vec1 = x86::xmm1;
362 : //!
363 : //! // Create/initialize FuncDetail and FuncFrame.
364 : //! FuncDetail func;
365 : //! func.init(FuncSignatureT<void, int*, const int*, const int*>(CallConvId::kHost));
366 : //!
367 : //! FuncFrame frame;
368 : //! frame.init(func);
369 : //!
370 : //! // Make XMM0 and XMM1 dirty - RegGroup::kVec describes XMM|YMM|ZMM registers.
371 : //! frame.setDirtyRegs(RegGroup::kVec, IntUtils::mask(0, 1));
372 : //!
373 : //! // Alternatively, if you don't want to use register masks you can pass BaseReg
374 : //! // to addDirtyRegs(). The following code would add both xmm0 and xmm1.
375 : //! frame.addDirtyRegs(x86::xmm0, x86::xmm1);
376 : //!
377 : //! FuncArgsAssignment args(&func); // Create arguments assignment context.
378 : //! args.assignAll(dst, src_a, src_b);// Assign our registers to arguments.
379 : //! args.updateFrameInfo(frame); // Reflect our args in FuncFrame.
380 : //! frame.finalize(); // Finalize the FuncFrame (updates it).
381 : //!
382 : //! a.emitProlog(frame); // Emit function prolog.
383 : //! a.emitArgsAssignment(frame, args);// Assign arguments to registers.
384 : //! a.movdqu(vec0, x86::ptr(src_a)); // Load 4 ints from [src_a] to XMM0.
385 : //! a.movdqu(vec1, x86::ptr(src_b)); // Load 4 ints from [src_b] to XMM1.
386 : //! a.paddd(vec0, vec1); // Add 4 ints in XMM1 to XMM0.
387 : //! a.movdqu(x86::ptr(dst), vec0); // Store the result to [dst].
388 : //! a.emitEpilog(frame); // Emit function epilog and return.
389 : //!
390 : //! SumIntsFunc fn;
391 : //! Error err = rt.add(&fn, &code); // Add the generated code to the runtime.
392 : //! if (err) return 1; // Handle a possible error case.
393 : //!
394 : //! // Execute the generated function.
395 : //! int inA[4] = { 4, 3, 2, 1 };
396 : //! int inB[4] = { 1, 5, 2, 8 };
397 : //! int out[4];
398 : //! fn(out, inA, inB);
399 : //!
400 : //! // Prints {5 8 4 9}
401 : //! printf("{%d %d %d %d}\n", out[0], out[1], out[2], out[3]);
402 : //!
403 : //! rt.release(fn);
404 : //! return 0;
405 : //! }
406 : //! ```
407 : //!
408 : //! ### Using x86::Assembler as Code-Patcher
409 : //!
410 : //! This is an advanced topic that is sometimes unavoidable. AsmJit by default appends machine code it generates
411 : //! into a \ref CodeBuffer, however, it also allows to set the offset in \ref CodeBuffer explicitly and to overwrite
412 : //! its content. This technique is extremely dangerous as X86 instructions have variable length (see below), so you
413 : //! should in general only patch code to change instruction's immediate values or some other details not known the
414 : //! at a time the instruction was emitted. A typical scenario that requires code-patching is when you start emitting
415 : //! function and you don't know how much stack you want to reserve for it.
416 : //!
417 : //! Before we go further it's important to introduce instruction options, because they can help with code-patching
418 : //! (and not only patching, but that will be explained in AVX-512 section):
419 : //!
420 : //! - Many general-purpose instructions (especially arithmetic ones) on X86 have multiple encodings - in AsmJit
421 : //! this is usually called 'short form' and 'long form'.
422 : //!
423 : //! - AsmJit always tries to use 'short form' as it makes the resulting machine-code smaller, which is always
424 : //! good - this decision is used by majority of assemblers out there.
425 : //!
426 : //! - AsmJit allows to override the default decision by using `short_()` and `long_()` instruction options to force
427 : //! short or long form, respectively. The most useful is `long_()` as it basically forces AsmJit to always emit
428 : //! the longest form. The `short_()` is not that useful as it's automatic (except jumps to non-bound labels). Note
429 : //! that the underscore after each function name avoids collision with built-in C++ types.
430 : //!
431 : //! To illustrate what short form and long form means in binary let's assume we want to emit "add esp, 16" instruction,
432 : //! which has two possible binary encodings:
433 : //!
434 : //! - `83C410` - This is a short form aka `short add esp, 16` - You can see opcode byte (0x8C), MOD/RM byte (0xC4)
435 : //! and an 8-bit immediate value representing `16`.
436 : //!
437 : //! - `81C410000000` - This is a long form aka `long add esp, 16` - You can see a different opcode byte (0x81), the
438 : //! same Mod/RM byte (0xC4) and a 32-bit immediate in little-endian representing `16`.
439 : //!
440 : //! It should be obvious that patching an existing instruction into an instruction having a different size may create
441 : //! various problems. So it's recommended to be careful and to only patch instructions into instructions having the
442 : //! same size. The example below demonstrates how instruction options can be used to guarantee the size of an
443 : //! instruction by forcing the assembler to use long-form encoding:
444 : //!
445 : //! ```
446 : //! #include <asmjit/x86.h>
447 : //! #include <stdio.h>
448 : //!
449 : //! using namespace asmjit;
450 : //!
451 : //! typedef int (*Func)(void);
452 : //!
453 : //! int main(int argc, char* argv[]) {
454 : //! JitRuntime rt; // Create a runtime specialized for JIT.
455 : //! CodeHolder code; // Create a CodeHolder.
456 : //!
457 : //! code.init(rt.environment()); // Initialize code to match the JIT environment.
458 : //! x86::Assembler a(&code); // Create and attach x86::Assembler to code.
459 : //!
460 : //! // Let's get these registers from x86::Assembler.
461 : //! x86::Gp zbp = a.zbp();
462 : //! x86::Gp zsp = a.zsp();
463 : //!
464 : //! // Function prolog.
465 : //! a.push(zbp);
466 : //! a.mov(zbp, zsp);
467 : //!
468 : //! // This is where we are gonna patch the code later, so let's get the offset
469 : //! // (the current location) from the beginning of the code-buffer.
470 : //! size_t patchOffset = a.offset();
471 : //! // Let's just emit 'sub zsp, 0' for now, but don't forget to use LONG form.
472 : //! a.long_().sub(zsp, 0);
473 : //!
474 : //! // ... emit some code (this just sets return value to zero) ...
475 : //! a.xor_(x86::eax, x86::eax);
476 : //!
477 : //! // Function epilog and return.
478 : //! a.mov(zsp, zbp);
479 : //! a.pop(zbp);
480 : //! a.ret();
481 : //!
482 : //! // Now we know how much stack size we want to reserve. I have chosen 128
483 : //! // bytes on purpose as it's encodable only in long form that we have used.
484 : //!
485 : //! int stackSize = 128; // Number of bytes to reserve on the stack.
486 : //! a.setOffset(patchOffset); // Move the current cursor to `patchOffset`.
487 : //! a.long_().sub(zsp, stackSize); // Patch the code; don't forget to use LONG form.
488 : //!
489 : //! // Now the code is ready to be called
490 : //! Func fn;
491 : //! Error err = rt.add(&fn, &code); // Add the generated code to the runtime.
492 : //! if (err) return 1; // Handle a possible error returned by AsmJit.
493 : //!
494 : //! int result = fn(); // Execute the generated code.
495 : //! printf("%d\n", result); // Print the resulting "0".
496 : //!
497 : //! rt.release(fn); // Remove the function from the runtime.
498 : //! return 0;
499 : //! }
500 : //! ```
501 : //!
502 : //! If you run the example it will just work, because both instructions have the same size. As an experiment you can
503 : //! try removing `long_()` form to see what happens when wrong code is generated.
504 : //!
505 : //! ### Code Patching and REX Prefix
506 : //!
507 : //! In 64-bit mode there is one more thing to worry about when patching code: REX prefix. It's a single byte prefix
508 : //! designed to address registers with ids from 9 to 15 and to override the default width of operation from 32 to 64
509 : //! bits. AsmJit, like other assemblers, only emits REX prefix when it's necessary. If the patched code only changes
510 : //! the immediate value as shown in the previous example then there is nothing to worry about as it doesn't change
511 : //! the logic behind emitting REX prefix, however, if the patched code changes register id or overrides the operation
512 : //! width then it's important to take care of REX prefix as well.
513 : //!
514 : //! AsmJit contains another instruction option that controls (forces) REX prefix - `rex()`. If you use it the
515 : //! instruction emitted will always use REX prefix even when it's encodable without it. The following list contains
516 : //! some instructions and their binary representations to illustrate when it's emitted:
517 : //!
518 : //! - `__83C410` - `add esp, 16` - 32-bit operation in 64-bit mode doesn't require REX prefix.
519 : //! - `4083C410` - `rex add esp, 16` - 32-bit operation in 64-bit mode with forced REX prefix (0x40).
520 : //! - `4883C410` - `add rsp, 16` - 64-bit operation in 64-bit mode requires REX prefix (0x48).
521 : //! - `4183C410` - `add r12d, 16` - 32-bit operation in 64-bit mode using R12D requires REX prefix (0x41).
522 : //! - `4983C410` - `add r12, 16` - 64-bit operation in 64-bit mode using R12 requires REX prefix (0x49).
523 : //!
524 : //! ### More Prefixes
525 : //!
526 : //! X86 architecture is known for its prefixes. AsmJit supports all prefixes
527 : //! that can affect how the instruction is encoded:
528 : //!
529 : //! ```
530 : //! #include <asmjit/x86.h>
531 : //!
532 : //! using namespace asmjit;
533 : //!
534 : //! void prefixesExample(x86::Assembler& a) {
535 : //! // Lock prefix for implementing atomics:
536 : //! // lock add dword ptr [dst], 1
537 : //! a.lock().add(x86::dword_ptr(dst), 1);
538 : //!
539 : //! // Similarly, XAcquire/XRelease prefixes are also available:
540 : //! // xacquire add dword ptr [dst], 1
541 : //! a.xacquire().add(x86::dword_ptr(dst), 1);
542 : //!
543 : //! // Rep prefix (see also repe/repz and repne/repnz):
544 : //! // rep movs byte ptr [dst], byte ptr [src]
545 : //! a.rep().movs(x86::byte_ptr(dst), x86::byte_ptr(src));
546 : //!
547 : //! // Forcing REX prefix in 64-bit mode.
548 : //! // rex mov eax, 1
549 : //! a.rex().mov(x86::eax, 1);
550 : //!
551 : //! // AVX instruction without forced prefix uses the shortest encoding:
552 : //! // vaddpd xmm0, xmm1, xmm2 -> [C5|F1|58|C2]
553 : //! a.vaddpd(x86::xmm0, x86::xmm1, x86::xmm2);
554 : //!
555 : //! // Forcing VEX3 prefix (AVX):
556 : //! // vex3 vaddpd xmm0, xmm1, xmm2 -> [C4|E1|71|58|C2]
557 : //! a.vex3().vaddpd(x86::xmm0, x86::xmm1, x86::xmm2);
558 : //!
559 : //! // Forcing EVEX prefix (AVX512):
560 : //! // evex vaddpd xmm0, xmm1, xmm2 -> [62|F1|F5|08|58|C2]
561 : //! a.evex().vaddpd(x86::xmm0, x86::xmm1, x86::xmm2);
562 : //!
563 : //! // Some instructions accept prefixes not originally intended to:
564 : //! // rep ret
565 : //! a.rep().ret();
566 : //! }
567 : //! ```
568 : //!
569 : //! It's important to understand that prefixes are part of instruction options. When a member function that involves
570 : //! adding a prefix is called the prefix is combined with existing instruction options, which will affect the next
571 : //! instruction generated.
572 : //!
573 : //! ### Generating AVX512 code.
574 : //!
575 : //! x86::Assembler can generate AVX512+ code including the use of opmask registers. Opmask can be specified through
576 : //! \ref x86::Assembler::k() function, which stores it as an extra register, which will be used by the next
577 : //! instruction. AsmJit uses such concept for manipulating instruction options as well.
578 : //!
579 : //! The following AVX512 features are supported:
580 : //!
581 : //! - Opmask selector {k} and zeroing {z}.
582 : //! - Rounding modes {rn|rd|ru|rz} and suppress-all-exceptions {sae} option.
583 : //! - AVX512 broadcasts {1toN}.
584 : //!
585 : //! The following example demonstrates how AVX512 features can be used:
586 : //!
587 : //! ```
588 : //! #include <asmjit/x86.h>
589 : //!
590 : //! using namespace asmjit;
591 : //!
592 : //! void generateAVX512Code(x86::Assembler& a) {
593 : //! using namespace x86;
594 : //!
595 : //! // Opmask Selectors
596 : //! // ----------------
597 : //! //
598 : //! // - Opmask / zeroing is part of the instruction options / extraReg.
599 : //! // - k(reg) is like {kreg} in Intel syntax.
600 : //! // - z() is like {z} in Intel syntax.
601 : //!
602 : //! // vaddpd zmm {k1} {z}, zmm1, zmm2
603 : //! a.k(k1).z().vaddpd(zmm0, zmm1, zmm2);
604 : //!
605 : //! // Memory Broadcasts
606 : //! // -----------------
607 : //! //
608 : //! // - Broadcast data is part of memory operand.
609 : //! // - Use x86::Mem::_1toN(), which returns a new x86::Mem operand.
610 : //!
611 : //! // vaddpd zmm0 {k1} {z}, zmm1, [rcx] {1to8}
612 : //! a.k(k1).z().vaddpd(zmm0, zmm1, x86::mem(rcx)._1to8());
613 : //!
614 : //! // Embedded Rounding & Suppress-All-Exceptoins
615 : //! // -------------------------------------------
616 : //! //
617 : //! // - Rounding mode and {sae} are part of instruction options.
618 : //! // - Use sae() to enable exception suppression.
619 : //! // - Use rn_sae(), rd_sae(), ru_sae(), and rz_sae() - to enable rounding.
620 : //! // - Embedded rounding implicitly sets {sae} as well, that's why the API
621 : //! // also has sae() suffix, to make it clear.
622 : //!
623 : //! // vcmppd k1, zmm1, zmm2, 0x00 {sae}
624 : //! a.sae().vcmppd(k1, zmm1, zmm2, 0);
625 : //!
626 : //! // vaddpd zmm0, zmm1, zmm2 {rz}
627 : //! a.rz_sae().vaddpd(zmm0, zmm1, zmm2);
628 : //! }
629 : //! ```
630 : class ASMJIT_VIRTAPI Assembler
631 : : public BaseAssembler,
632 : public EmitterImplicitT<Assembler> {
633 : public:
634 : ASMJIT_NONCOPYABLE(Assembler)
635 : typedef BaseAssembler Base;
636 :
637 : //! \name Construction & Destruction
638 : //! \{
639 :
640 : ASMJIT_API explicit Assembler(CodeHolder* code = nullptr) noexcept;
641 : ASMJIT_API virtual ~Assembler() noexcept;
642 :
643 : //! \}
644 :
645 : //! \cond INTERNAL
646 : //! \name Internal
647 : //! \{
648 :
649 : // NOTE: x86::Assembler uses _privateData to store 'address-override' bit that is used to decide whether to emit
650 : // address-override (67H) prefix based on the memory BASE+INDEX registers. It's either `kX86MemInfo_67H_X86` or
651 : // `kX86MemInfo_67H_X64`.
652 142105 : inline uint32_t _addressOverrideMask() const noexcept { return _privateData; }
653 27492 : inline void _setAddressOverrideMask(uint32_t m) noexcept { _privateData = m; }
654 :
655 : //! \}
656 : //! \endcond
657 :
658 : //! \name Emit
659 : //! \{
660 :
661 : ASMJIT_API Error _emit(InstId instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_* opExt) override;
662 :
663 : //! \}
664 : //! \endcond
665 :
666 : //! \name Align
667 : //! \{
668 :
669 : ASMJIT_API Error align(AlignMode alignMode, uint32_t alignment) override;
670 :
671 : //! \}
672 :
673 : //! \name Events
674 : //! \{
675 :
676 : ASMJIT_API Error onAttach(CodeHolder* code) noexcept override;
677 : ASMJIT_API Error onDetach(CodeHolder* code) noexcept override;
678 :
679 : //! \}
680 : };
681 :
682 : //! \}
683 :
684 : ASMJIT_END_SUB_NAMESPACE
685 :
686 : #endif // ASMJIT_X86_X86ASSEMBLER_H_INCLUDED
687 :
688 : #endif // __PLUMED_HAS_ASMJIT
|