codeemitter.h 23.2 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.

// [Guard]
#ifndef _ASMJIT_BASE_CODEEMITTER_H
#define _ASMJIT_BASE_CODEEMITTER_H

// [Dependencies]
#include "../base/arch.h"
#include "../base/codeholder.h"
#include "../base/operand.h"

// [Api-Begin]
#include "../asmjit_apibegin.h"

namespace asmjit {

//! \addtogroup asmjit_base
//! \{

// ============================================================================
// [Forward Declarations]
// ============================================================================

class ConstPool;

// ============================================================================
// [asmjit::CodeEmitter]
// ============================================================================

//! Provides a base foundation to emit code - specialized by \ref Assembler and
//! \ref CodeBuilder.
class ASMJIT_VIRTAPI CodeEmitter {
public:
  //! CodeEmitter type.
  ASMJIT_ENUM(Type) {
    kTypeNone       = 0,
    kTypeAssembler  = 1,
    kTypeBuilder    = 2,
    kTypeCompiler   = 3,
    kTypeCount      = 4
  };

  //! CodeEmitter hints - global settings that affect machine-code generation.
  ASMJIT_ENUM(Hints) {
    //! Emit optimized code-alignment sequences.
    //!
    //! Default `true`.
    //!
    //! X86/X64 Specific
    //! ----------------
    //!
    //! Default align sequence used by X86/X64 architecture is one-byte (0x90)
    //! opcode that is often shown by disassemblers as nop. However there are
    //! more optimized align sequences for 2-11 bytes that may execute faster.
    //! If this feature is enabled AsmJit will generate specialized sequences
    //! for alignment between 2 to 11 bytes.
    kHintOptimizedAlign = 0x00000001U,

    //! Emit jump-prediction hints.
    //!
    //! Default `false`.
    //!
    //! X86/X64 Specific
    //! ----------------
    //!
    //! Jump prediction is usually based on the direction of the jump. If the
    //! jump is backward it is usually predicted as taken; and if the jump is
    //! forward it is usually predicted as not-taken. The reason is that loops
    //! generally use backward jumps and conditions usually use forward jumps.
    //! However this behavior can be overridden by using instruction prefixes.
    //! If this option is enabled these hints will be emitted.
    //!
    //! This feature is disabled by default, because the only processor that
    //! used to take into consideration prediction hints was P4. Newer processors
    //! implement heuristics for branch prediction that ignores any static hints.
    kHintPredictedJumps = 0x00000002U
  };

  //! CodeEmitter options that are merged with instruction options.
  ASMJIT_ENUM(Options) {
    //! Reserved, used to check for errors in `Assembler::_emit()`. In addition,
    //! if an emitter is in error state it will have `kOptionMaybeFailureCase`
    //! set
    kOptionMaybeFailureCase = 0x00000001U,

    //! Perform a strict validation before the instruction is emitted.
    kOptionStrictValidation = 0x00000002U,

    //! Logging is enabled and `CodeHolder::getLogger()` should return a valid
    //! \ref Logger pointer.
    kOptionLoggingEnabled   = 0x00000004U,

    //! Mask of all internal options that are not used to represent instruction
    //! options, but are used to instrument Assembler and CodeBuilder. These
    //! options are internal and should not be used outside of AsmJit itself.
    //!
    //! NOTE: Reserved options should never appear in `CBInst` options.
    kOptionReservedMask = 0x00000007U,

    //! Used only by Assembler to mark `_op4` and `_op5` are used.
    kOptionOp4Op5Used = 0x00000008U,

    //! Prevents following a jump during compilation (CodeCompiler).
    kOptionUnfollow = 0x00000010U,

    //! Overwrite the destination operand (CodeCompiler).
    //!
    //! Hint that is important for register liveness analysis. It tells the
    //! compiler that the destination operand will be overwritten now or by
    //! adjacent instructions. CodeCompiler knows when a register is completely
    //! overwritten by a single instruction, for example you don't have to
    //! mark "movaps" or "pxor x, x", however, if a pair of instructions is
    //! used and the first of them doesn't completely overwrite the content
    //! of the destination, CodeCompiler fails to mark that register as dead.
    //!
    //! X86/X64 Specific
    //! ----------------
    //!
    //!   - All instructions that always overwrite at least the size of the
    //!     register the virtual-register uses , for example "mov", "movq",
    //!     "movaps" don't need the overwrite option to be used - conversion,
    //!     shuffle, and other miscellaneous instructions included.
    //!
    //!   - All instructions that clear the destination register if all operands
    //!     are the same, for example "xor x, x", "pcmpeqb x x", etc...
    //!
    //!   - Consecutive instructions that partially overwrite the variable until
    //!     there is no old content require the `overwrite()` to be used. Some
    //!     examples (not always the best use cases thought):
    //!
    //!     - `movlps xmm0, ?` followed by `movhps xmm0, ?` and vice versa
    //!     - `movlpd xmm0, ?` followed by `movhpd xmm0, ?` and vice versa
    //!     - `mov al, ?` followed by `and ax, 0xFF`
    //!     - `mov al, ?` followed by `mov ah, al`
    //!     - `pinsrq xmm0, ?, 0` followed by `pinsrq xmm0, ?, 1`
    //!
    //!   - If allocated variable is used temporarily for scalar operations. For
    //!     example if you allocate a full vector like `X86Compiler::newXmm()`
    //!     and then use that vector for scalar operations you should use
    //!     `overwrite()` directive:
    //!
    //!     - `sqrtss x, y` - only LO element of `x` is changed, if you don't use
    //!       HI elements, use `X86Compiler.overwrite().sqrtss(x, y)`.
    kOptionOverwrite = 0x00000020U
  };

  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  ASMJIT_API CodeEmitter(uint32_t type) noexcept;
  ASMJIT_API virtual ~CodeEmitter() noexcept;

  // --------------------------------------------------------------------------
  // [Events]
  // --------------------------------------------------------------------------

  //! Called after the \ref CodeEmitter was attached to the \ref CodeHolder.
  virtual Error onAttach(CodeHolder* code) noexcept = 0;
  //! Called after the \ref CodeEmitter was detached from the \ref CodeHolder.
  virtual Error onDetach(CodeHolder* code) noexcept = 0;

  // --------------------------------------------------------------------------
  // [Code-Generation]
  // --------------------------------------------------------------------------

  //! Emit instruction having max 4 operands.
  virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3) = 0;
  //! Emit instruction having max 6 operands.
  virtual Error _emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5) = 0;
  //! Emit instruction having operands stored in array.
  virtual Error _emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount);

  //! Create a new label.
  virtual Label newLabel() = 0;
  //! Create a new named label.
  virtual Label newNamedLabel(
    const char* name,
    size_t nameLength = Globals::kInvalidIndex,
    uint32_t type = Label::kTypeGlobal,
    uint32_t parentId = 0) = 0;

  //! Get a label by name.
  //!
  //! Returns invalid Label in case that the name is invalid or label was not found.
  //!
  //! NOTE: This function doesn't trigger ErrorHandler in case the name is
  //! invalid or no such label exist. You must always check the validity of the
  //! \ref Label returned.
  ASMJIT_API Label getLabelByName(
    const char* name,
    size_t nameLength = Globals::kInvalidIndex,
    uint32_t parentId = 0) noexcept;

  //! Bind the `label` to the current position of the current section.
  //!
  //! NOTE: Attempt to bind the same label multiple times will return an error.
  virtual Error bind(const Label& label) = 0;

  //! Align to the `alignment` specified.
  //!
  //! The sequence that is used to fill the gap between the aligned location
  //! and the current location depends on the align `mode`, see \ref AlignMode.
  virtual Error align(uint32_t mode, uint32_t alignment) = 0;

  //! Embed raw data into the code-buffer.
  virtual Error embed(const void* data, uint32_t size) = 0;

  //! Embed absolute label address as data (4 or 8 bytes).
  virtual Error embedLabel(const Label& label) = 0;

  //! Embed a constant pool into the code-buffer in the following steps:
  //!   1. Align by using kAlignData to the minimum `pool` alignment.
  //!   2. Bind `label` so it's bound to an aligned location.
  //!   3. Emit constant pool data.
  virtual Error embedConstPool(const Label& label, const ConstPool& pool) = 0;

  //! Emit a comment string `s` with an optional `len` parameter.
  virtual Error comment(const char* s, size_t len = Globals::kInvalidIndex) = 0;

  // --------------------------------------------------------------------------
  // [Code-Generation Status]
  // --------------------------------------------------------------------------

  //! Get if the CodeEmitter is initialized (i.e. attached to a \ref CodeHolder).
  ASMJIT_INLINE bool isInitialized() const noexcept { return _code != nullptr; }

  ASMJIT_API virtual Error finalize();

  // --------------------------------------------------------------------------
  // [Code Information]
  // --------------------------------------------------------------------------

  //! Get information about the code, see \ref CodeInfo.
  ASMJIT_INLINE const CodeInfo& getCodeInfo() const noexcept { return _codeInfo; }
  //! Get \ref CodeHolder this CodeEmitter is attached to.
  ASMJIT_INLINE CodeHolder* getCode() const noexcept { return _code; }

  //! Get information about the architecture, see \ref ArchInfo.
  ASMJIT_INLINE const ArchInfo& getArchInfo() const noexcept { return _codeInfo.getArchInfo(); }

  //! Get if the target architecture is 32-bit.
  ASMJIT_INLINE bool is32Bit() const noexcept { return getArchInfo().is32Bit(); }
  //! Get if the target architecture is 64-bit.
  ASMJIT_INLINE bool is64Bit() const noexcept { return getArchInfo().is64Bit(); }

  //! Get the target architecture type.
  ASMJIT_INLINE uint32_t getArchType() const noexcept { return getArchInfo().getType(); }
  //! Get the target architecture sub-type.
  ASMJIT_INLINE uint32_t getArchSubType() const noexcept { return getArchInfo().getSubType(); }
  //! Get the target architecture's GP register size (4 or 8 bytes).
  ASMJIT_INLINE uint32_t getGpSize() const noexcept { return getArchInfo().getGpSize(); }
  //! Get the number of target GP registers.
  ASMJIT_INLINE uint32_t getGpCount() const noexcept { return getArchInfo().getGpCount(); }

  // --------------------------------------------------------------------------
  // [Code-Emitter Type]
  // --------------------------------------------------------------------------

  //! Get the type of this CodeEmitter, see \ref Type.
  ASMJIT_INLINE uint32_t getType() const noexcept { return _type; }

  ASMJIT_INLINE bool isAssembler() const noexcept { return _type == kTypeAssembler; }
  ASMJIT_INLINE bool isCodeBuilder() const noexcept { return _type == kTypeBuilder; }
  ASMJIT_INLINE bool isCodeCompiler() const noexcept { return _type == kTypeCompiler; }

  // --------------------------------------------------------------------------
  // [Global Information]
  // --------------------------------------------------------------------------

  //! Get global hints.
  ASMJIT_INLINE uint32_t getGlobalHints() const noexcept { return _globalHints; }

  //! Get global options.
  //!
  //! Global options are merged with instruction options before the instruction
  //! is encoded. These options have some bits reserved that are used for error
  //! checking, logging, and strict validation. Other options are globals that
  //! affect each instruction, for example if VEX3 is set globally, it will all
  //! instructions, even those that don't have such option set.
  ASMJIT_INLINE uint32_t getGlobalOptions() const noexcept { return _globalOptions; }

  // --------------------------------------------------------------------------
  // [Error Handling]
  // --------------------------------------------------------------------------

  //! Get if the object is in error state.
  //!
  //! Error state means that it does not consume anything unless the error
  //! state is reset by calling `resetLastError()`. Use `getLastError()` to
  //! get the last error that put the object into the error state.
  ASMJIT_INLINE bool isInErrorState() const noexcept { return _lastError != kErrorOk; }

  //! Get the last error code.
  ASMJIT_INLINE Error getLastError() const noexcept { return _lastError; }
  //! Set the last error code and propagate it through the error handler.
  ASMJIT_API Error setLastError(Error error, const char* message = nullptr);
  //! Clear the last error code and return `kErrorOk`.
  ASMJIT_INLINE Error resetLastError() noexcept { return setLastError(kErrorOk); }

  // --------------------------------------------------------------------------
  // [Accessors That Affect the Next Instruction]
  // --------------------------------------------------------------------------

  //! Get options of the next instruction.
  ASMJIT_INLINE uint32_t getOptions() const noexcept { return _options; }
  //! Set options of the next instruction.
  ASMJIT_INLINE void setOptions(uint32_t options) noexcept { _options = options; }
  //! Add options of the next instruction.
  ASMJIT_INLINE void addOptions(uint32_t options) noexcept { _options |= options; }
  //! Reset options of the next instruction.
  ASMJIT_INLINE void resetOptions() noexcept { _options = 0; }

  //! Get if the extra register operand is valid.
  ASMJIT_INLINE bool hasExtraReg() const noexcept { return _extraReg.isValid(); }
  //! Get an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE const RegOnly& getExtraReg() const noexcept { return _extraReg; }
  //! Set an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE void setExtraReg(const Reg& reg) noexcept { _extraReg.init(reg); }
  //! Set an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE void setExtraReg(const RegOnly& reg) noexcept { _extraReg.init(reg); }
  //! Reset an extra operand that will be used by the next instruction (architecture specific).
  ASMJIT_INLINE void resetExtraReg() noexcept { _extraReg.reset(); }

  //! Get annotation of the next instruction.
  ASMJIT_INLINE const char* getInlineComment() const noexcept { return _inlineComment; }
  //! Set annotation of the next instruction.
  //!
  //! NOTE: This string is set back to null by `_emit()`, but until that it has
  //! to remain valid as `CodeEmitter` is not required to make a copy of it (and
  //! it would be slow to do that for each instruction).
  ASMJIT_INLINE void setInlineComment(const char* s) noexcept { _inlineComment = s; }
  //! Reset annotation of the next instruction to null.
  ASMJIT_INLINE void resetInlineComment() noexcept { _inlineComment = nullptr; }

  // --------------------------------------------------------------------------
  // [Helpers]
  // --------------------------------------------------------------------------

  //! Get if the `label` is valid (i.e. registered).
  ASMJIT_INLINE bool isLabelValid(const Label& label) const noexcept {
    return isLabelValid(label.getId());
  }

  //! Get if the label `id` is valid (i.e. registered).
  ASMJIT_API bool isLabelValid(uint32_t id) const noexcept;

  //! Emit a formatted string `fmt`.
  ASMJIT_API Error commentf(const char* fmt, ...);
  //! Emit a formatted string `fmt` (va_list version).
  ASMJIT_API Error commentv(const char* fmt, va_list ap);

  // --------------------------------------------------------------------------
  // [Emit]
  // --------------------------------------------------------------------------

  // NOTE: These `emit()` helpers are designed to address a code-bloat generated
  // by C++ compilers to call a function having many arguments. Each parameter to
  // `_emit()` requires code to pass it, which means that if we default to 4
  // operand parameters in `_emit()` and instId the C++ compiler would have to
  // generate a virtual function call having 5 parameters, which is quite a lot.
  // Since by default asm instructions have 2 to 3 operands it's better to
  // introduce helpers that pass those and fill all the remaining with `_none`.

  //! Emit an instruction.
  ASMJIT_API Error emit(uint32_t instId);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, const Operand_& o5);

  //! Emit an instruction that has a 32-bit signed immediate operand.
  ASMJIT_API Error emit(uint32_t instId, int o0);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int o1);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int o2);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int o3);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int o4);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int o5);

  //! Emit an instruction that has a 64-bit signed immediate operand.
  ASMJIT_API Error emit(uint32_t instId, int64_t o0);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, int64_t o1);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, int64_t o2);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, int64_t o3);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, int64_t o4);
  //! \overload
  ASMJIT_API Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, int64_t o5);

  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, unsigned int o0) {
    return emit(instId, static_cast<int64_t>(o0));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, unsigned int o1) {
    return emit(instId, o0, static_cast<int64_t>(o1));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, unsigned int o2) {
    return emit(instId, o0, o1, static_cast<int64_t>(o2));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, unsigned int o3) {
    return emit(instId, o0, o1, o2, static_cast<int64_t>(o3));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, unsigned int o4) {
    return emit(instId, o0, o1, o2, o3, static_cast<int64_t>(o4));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, unsigned int o5) {
    return emit(instId, o0, o1, o2, o3, o4, static_cast<int64_t>(o5));
  }

  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, uint64_t o0) {
    return emit(instId, static_cast<int64_t>(o0));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, uint64_t o1) {
    return emit(instId, o0, static_cast<int64_t>(o1));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, uint64_t o2) {
    return emit(instId, o0, o1, static_cast<int64_t>(o2));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, uint64_t o3) {
    return emit(instId, o0, o1, o2, static_cast<int64_t>(o3));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, uint64_t o4) {
    return emit(instId, o0, o1, o2, o3, static_cast<int64_t>(o4));
  }
  //! \overload
  ASMJIT_INLINE Error emit(uint32_t instId, const Operand_& o0, const Operand_& o1, const Operand_& o2, const Operand_& o3, const Operand_& o4, uint64_t o5) {
    return emit(instId, o0, o1, o2, o3, o4, static_cast<int64_t>(o5));
  }

  ASMJIT_INLINE Error emitOpArray(uint32_t instId, const Operand_* opArray, size_t opCount) {
    return _emitOpArray(instId, opArray, opCount);
  }

  // --------------------------------------------------------------------------
  // [Members]
  // --------------------------------------------------------------------------

  CodeInfo _codeInfo;                    //!< Basic information about the code (matches CodeHolder::_codeInfo).
  CodeHolder* _code;                     //!< CodeHolder the CodeEmitter is attached to.
  CodeEmitter* _nextEmitter;             //!< Linked list of `CodeEmitter`s attached to the same \ref CodeHolder.

  uint8_t _type;                         //!< See CodeEmitter::Type.
  uint8_t _destroyed;                    //!< Set by ~CodeEmitter() before calling `_code->detach()`.
  uint8_t _finalized;                    //!< True if the CodeEmitter is finalized (CodeBuilder & CodeCompiler).
  uint8_t _reserved;                     //!< \internal
  Error _lastError;                      //!< Last error code.

  uint32_t _privateData;                 //!< Internal private data used freely by any CodeEmitter.
  uint32_t _globalHints;                 //!< Global hints, always in sync with CodeHolder.
  uint32_t _globalOptions;               //!< Global options, combined with `_options` before used by each instruction.

  uint32_t _options;                     //!< Used to pass instruction options        (affects the next instruction).
  RegOnly _extraReg;                     //!< Extra register (op-mask {k} on AVX-512) (affects the next instruction).
  const char* _inlineComment;            //!< Inline comment of the next instruction  (affects the next instruction).

  Operand_ _none;                        //!< Used to pass unused operands to `_emit()` instead of passing null.
  Reg _nativeGpReg;                      //!< Native GP register with zero id.
  const Reg* _nativeGpArray;             //!< Array of native registers indexed from zero.
};

//! \}

} // asmjit namespace

// [Api-End]
#include "../asmjit_apiend.h"

// [Guard]
#endif // _ASMJIT_BASE_CODEEMITTER_H