operand.h 70.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
// [AsmJit]
// Complete x86/x64 JIT and Remote Assembler for C++.
//
// [License]
// Zlib - See LICENSE.md file in the package.

// [Guard]
#ifndef _ASMJIT_BASE_OPERAND_H
#define _ASMJIT_BASE_OPERAND_H

11
12
// [Dependencies]
#include "../base/utils.h"
13
14

// [Api-Begin]
15
#include "../asmjit_apibegin.h"
16
17
18

namespace asmjit {

19
//! \addtogroup asmjit_base
20
21
22
//! \{

// ============================================================================
23
// [asmjit::Operand_]
24
25
// ============================================================================

26
27
28
29
30
31
32
33
34
//! Constructor-less \ref Operand.
//!
//! Contains no initialization code and can be used safely to define an array
//! of operands that won't be initialized. This is a \ref Operand compatible
//! data structure designed to be statically initialized or `static const`.
struct Operand_ {
  // --------------------------------------------------------------------------
  // [Operand Type]
  // --------------------------------------------------------------------------
35

36
37
38
39
40
41
42
43
  //! Operand types that can be encoded in \ref Operand.
  ASMJIT_ENUM(OpType) {
    kOpNone  = 0,                        //!< Not an operand or not initialized.
    kOpReg   = 1,                        //!< Operand is a register.
    kOpMem   = 2,                        //!< Operand is a memory.
    kOpImm   = 3,                        //!< Operand is an immediate value.
    kOpLabel = 4                         //!< Operand is a label.
  };
44

45
46
47
  // --------------------------------------------------------------------------
  // [Operand Signature (Bits)]
  // --------------------------------------------------------------------------
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
  ASMJIT_ENUM(SignatureBits) {
    // Operand type (3 least significant bits).
    // |........|........|........|.....XXX|
    kSignatureOpShift           = 0,
    kSignatureOpBits            = 0x07U,
    kSignatureOpMask            = kSignatureOpBits << kSignatureOpShift,

    // Operand size (8 most significant bits).
    // |XXXXXXXX|........|........|........|
    kSignatureSizeShift         = 24,
    kSignatureSizeBits          = 0xFFU,
    kSignatureSizeMask          = kSignatureSizeBits << kSignatureSizeShift,

    // Register type (5 bits).
    // |........|........|........|XXXXX...|
    kSignatureRegTypeShift      = 3,
    kSignatureRegTypeBits       = 0x1FU,
    kSignatureRegTypeMask       = kSignatureRegTypeBits << kSignatureRegTypeShift,

    // Register kind (4 bits).
    // |........|........|....XXXX|........|
    kSignatureRegKindShift      = 8,
    kSignatureRegKindBits       = 0x0FU,
    kSignatureRegKindMask       = kSignatureRegKindBits << kSignatureRegKindShift,

    // Memory base type (5 bits).
    // |........|........|........|XXXXX...|
    kSignatureMemBaseTypeShift  = 3,
    kSignatureMemBaseTypeBits   = 0x1FU,
    kSignatureMemBaseTypeMask   = kSignatureMemBaseTypeBits << kSignatureMemBaseTypeShift,

    // Memory index type (5 bits).
    // |........|........|...XXXXX|........|
    kSignatureMemIndexTypeShift = 8,
    kSignatureMemIndexTypeBits  = 0x1FU,
    kSignatureMemIndexTypeMask  = kSignatureMemIndexTypeBits << kSignatureMemIndexTypeShift,

    // Memory base+index combined (10 bits).
    // |........|........|...XXXXX|XXXXX...|
    kSignatureMemBaseIndexShift = 3,
    kSignatureMemBaseIndexBits  = 0x3FFU,
    kSignatureMemBaseIndexMask  = kSignatureMemBaseIndexBits << kSignatureMemBaseIndexShift,

    // Memory should be encoded as absolute immediate (X86|X64).
    // |........|........|.XX.....|........|
    kSignatureMemAddrTypeShift  = 13,
    kSignatureMemAddrTypeBits   = 0x03U,
    kSignatureMemAddrTypeMask   = kSignatureMemAddrTypeBits << kSignatureMemAddrTypeShift,

    // This memory operand represents a function argument's stack location (CodeCompiler)
    // |........|........|.X......|........|
    kSignatureMemArgHomeShift   = 15,
    kSignatureMemArgHomeBits    = 0x01U,
    kSignatureMemArgHomeFlag    = kSignatureMemArgHomeBits << kSignatureMemArgHomeShift,

    // This memory operand represents a virtual register's home-slot (CodeCompiler).
    // |........|........|X.......|........|
    kSignatureMemRegHomeShift   = 16,
    kSignatureMemRegHomeBits    = 0x01U,
    kSignatureMemRegHomeFlag    = kSignatureMemRegHomeBits << kSignatureMemRegHomeShift
  };
110

111
112
113
  // --------------------------------------------------------------------------
  // [Operand Id]
  // --------------------------------------------------------------------------
114

115
116
117
118
119
120
121
122
123
  //! Operand id helpers useful for id <-> index translation.
  ASMJIT_ENUM(PackedId) {
    //! Minimum valid packed-id.
    kPackedIdMin    = 0x00000100U,
    //! Maximum valid packed-id.
    kPackedIdMax    = 0xFFFFFFFFU,
    //! Count of valid packed-ids.
    kPackedIdCount  = kPackedIdMax - kPackedIdMin + 1
  };
124

125
126
127
  // --------------------------------------------------------------------------
  // [Operand Utilities]
  // --------------------------------------------------------------------------
128

129
130
131
132
133
134
135
136
137
138
139
140
  //! Get if the given `id` is a valid packed-id that can be used by Operand.
  //! Packed ids are those equal or greater than `kPackedIdMin` and lesser or
  //! equal to `kPackedIdMax`. This concept was created to support virtual
  //! registers and to make them distinguishable from physical ones. It allows
  //! a single uint32_t to contain either physical register id or virtual
  //! register id represented as `packed-id`. This concept is used also for
  //! labels to make the API consistent.
  static ASMJIT_INLINE bool isPackedId(uint32_t id) noexcept { return id - kPackedIdMin < kPackedIdCount; }
  //! Convert a real-id into a packed-id that can be stored in Operand.
  static ASMJIT_INLINE uint32_t packId(uint32_t id) noexcept { return id + kPackedIdMin; }
  //! Convert a packed-id back to real-id.
  static ASMJIT_INLINE uint32_t unpackId(uint32_t id) noexcept { return id - kPackedIdMin; }
141
142

  // --------------------------------------------------------------------------
143
  // [Operand Data]
144
145
  // --------------------------------------------------------------------------

146
147
148
149
150
151
  //! Any operand.
  struct AnyData {
    uint32_t signature;                  //!< Type of the operand (see \ref OpType) and other data.
    uint32_t id;                         //!< Operand id or `0`.
    uint32_t reserved8_4;                //!< \internal
    uint32_t reserved12_4;               //!< \internal
152
153
  };

154
155
156
157
158
159
160
  //! Register operand data.
  struct RegData {
    uint32_t signature;                  //!< Type of the operand (always \ref kOpReg) and other data.
    uint32_t id;                         //!< Physical or virtual register id.
    uint32_t reserved8_4;                //!< \internal
    uint32_t reserved12_4;               //!< \internal
  };
161

162
163
164
165
  //! Memory operand data.
  struct MemData {
    uint32_t signature;                  //!< Type of the operand (always \ref kOpMem) and other data.
    uint32_t index;                      //!< INDEX register id or `0`.
166

167
168
169
    // [BASE + OFF32] vs just [OFF64].
    union {
      uint64_t offset64;                 //!< 64-bit offset, combining low and high 32-bit parts.
170
      struct {
171
172
173
#if ASMJIT_ARCH_LE
        uint32_t offsetLo32;             //!< 32-bit low offset part.
        uint32_t base;                   //!< 32-bit high offset part or BASE.
174
#else
175
176
        uint32_t base;                   //!< 32-bit high offset part or BASE.
        uint32_t offsetLo32;             //!< 32-bit low offset part.
177
#endif
178
179
      };
    };
180
  };
181

182
183
184
185
186
187
  //! Immediate operand data.
  struct ImmData {
    uint32_t signature;                  //!< Type of the operand (always \ref kOpImm) and other data.
    uint32_t id;                         //!< Immediate id (always `0`).
    UInt64 value;                        //!< Immediate value.
  };
188

189
190
191
192
193
194
  //! Label operand data.
  struct LabelData {
    uint32_t signature;                  //!< Type of the operand (always \ref kOpLabel) and other data.
    uint32_t id;                         //!< Label id (`0` if not initialized).
    uint32_t reserved8_4;                //!< \internal
    uint32_t reserved12_4;               //!< \internal
195
196
  };

197
198
199
200
  // --------------------------------------------------------------------------
  // [Init & Copy]
  // --------------------------------------------------------------------------

201
202
  //! \internal
  //!
203
204
  //! Initialize the operand to `other` (used by constructors).
  ASMJIT_INLINE void _init(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); }
205
206

  //! \internal
207
208
209
210
  ASMJIT_INLINE void _initReg(uint32_t signature, uint32_t rd) {
    _init_packed_d0_d1(signature, rd);
    _init_packed_d2_d3(0, 0);
  }
211

212
213
214
215
  //! \internal
  ASMJIT_INLINE void _init_packed_d0_d1(uint32_t d0, uint32_t d1) noexcept { _packed[0].setPacked_2x32(d0, d1); }
  //! \internal
  ASMJIT_INLINE void _init_packed_d2_d3(uint32_t d2, uint32_t d3) noexcept { _packed[1].setPacked_2x32(d2, d3); }
216
217
218

  //! \internal
  //!
219
220
  //! Initialize the operand from `other` (used by operator overloads).
  ASMJIT_INLINE void copyFrom(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); }
221
222

  // --------------------------------------------------------------------------
223
  // [Accessors]
224
225
  // --------------------------------------------------------------------------

226
227
  //! Get if the operand matches the given signature `sign`.
  ASMJIT_INLINE bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; }
228

229
230
231
  //! Get if the operand matches a signature of the `other` operand.
  ASMJIT_INLINE bool hasSignature(const Operand_& other) const noexcept {
    return _signature == other.getSignature();
232
233
  }

234
235
236
237
238
239
  //! Get a 32-bit operand signature.
  //!
  //! Signature is first 4 bytes of the operand data. It's used mostly for
  //! operand checking as it's much faster to check 4 bytes at once than having
  //! to check these bytes individually.
  ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; }
240

241
242
243
244
  //! Set the operand signature (see \ref getSignature).
  //!
  //! Improper use of `setSignature()` can lead to hard-to-debug errors.
  ASMJIT_INLINE void setSignature(uint32_t signature) noexcept { _signature = signature; }
245

246
  ASMJIT_INLINE bool _hasSignatureData(uint32_t bits) const noexcept { return (_signature & bits) != 0; }
247

248
249
250
251
  //! \internal
  //!
  //! Unpacks information from operand's signature.
  ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; }
252
253
254

  //! \internal
  //!
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
  //! Packs information to operand's signature.
  ASMJIT_INLINE void _setSignatureData(uint32_t value, uint32_t bits, uint32_t shift) noexcept {
    ASMJIT_ASSERT(value <= bits);
    _signature = (_signature & ~(bits << shift)) | (value << shift);
  }

  ASMJIT_INLINE void _addSignatureData(uint32_t data) noexcept { _signature |= data; }

  //! Clears specified bits in operand's signature.
  ASMJIT_INLINE void _clearSignatureData(uint32_t bits, uint32_t shift) noexcept { _signature &= ~(bits << shift); }

  //! Get type of the operand, see \ref OpType.
  ASMJIT_INLINE uint32_t getOp() const noexcept { return _getSignatureData(kSignatureOpBits, kSignatureOpShift); }
  //! Get if the operand is none (\ref kOpNone).
  ASMJIT_INLINE bool isNone() const noexcept { return getOp() == 0; }
  //! Get if the operand is a register (\ref kOpReg).
  ASMJIT_INLINE bool isReg() const noexcept { return getOp() == kOpReg; }
  //! Get if the operand is a memory location (\ref kOpMem).
  ASMJIT_INLINE bool isMem() const noexcept { return getOp() == kOpMem; }
  //! Get if the operand is an immediate (\ref kOpImm).
  ASMJIT_INLINE bool isImm() const noexcept { return getOp() == kOpImm; }
  //! Get if the operand is a label (\ref kOpLabel).
  ASMJIT_INLINE bool isLabel() const noexcept { return getOp() == kOpLabel; }

  //! Get if the operand is a physical register.
  ASMJIT_INLINE bool isPhysReg() const noexcept { return isReg() && _reg.id < Globals::kInvalidRegId; }
  //! Get if the operand is a virtual register.
  ASMJIT_INLINE bool isVirtReg() const noexcept { return isReg() && isPackedId(_reg.id); }

  //! Get if the operand specifies a size (i.e. the size is not zero).
  ASMJIT_INLINE bool hasSize() const noexcept { return _hasSignatureData(kSignatureSizeMask); }
  //! Get if the size of the operand matches `size`.
  ASMJIT_INLINE bool hasSize(uint32_t size) const noexcept { return getSize() == size; }

  //! Get size of the operand (in bytes).
  //!
  //! The value returned depends on the operand type:
  //!   * None  - Should always return zero size.
  //!   * Reg   - Should always return the size of the register. If the register
  //!             size depends on architecture (like \ref X86CReg and \ref X86DReg)
  //!             the size returned should be the greatest possible (so it should
  //!             return 64-bit size in such case).
  //!   * Mem   - Size is optional and will be in most cases zero.
  //!   * Imm   - Should always return zero size.
  //!   * Label - Should always return zero size.
  ASMJIT_INLINE uint32_t getSize() const noexcept { return _getSignatureData(kSignatureSizeBits, kSignatureSizeShift); }

  //! Get the operand id.
  //!
  //! The value returned should be interpreted accordingly to the operand type:
  //!   * None  - Should be `0`.
  //!   * Reg   - Physical or virtual register id.
  //!   * Mem   - Multiple meanings - BASE address (register or label id), or
  //!             high value of a 64-bit absolute address.
  //!   * Imm   - Should be `0`.
  //!   * Label - Label id if it was created by using `newLabel()` or `0`
  //!             if the label is invalid or uninitialized.
  ASMJIT_INLINE uint32_t getId() const noexcept { return _any.id; }
313

314
315
316
317
  //! Get if the operand is 100% equal to `other`.
  ASMJIT_INLINE bool isEqual(const Operand_& other) const noexcept {
    return (_packed[0] == other._packed[0]) &
           (_packed[1] == other._packed[1]) ;
318
319
  }

320
321
322
323
324
  //! Get if the operand is a register matching `rType`.
  ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept {
    const uint32_t kMsk = (kSignatureOpBits << kSignatureOpShift) | (kSignatureRegTypeBits << kSignatureRegTypeShift);
    const uint32_t kSgn = (kOpReg           << kSignatureOpShift) | (rType                 << kSignatureRegTypeShift);
    return (_signature & kMsk) == kSgn;
325
326
  }

327
328
329
  //! Get whether the operand is register and of `type` and `id`.
  ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept {
    return isReg(rType) && getId() == rId;
330
331
  }

332
333
334
335
  //! Get whether the operand is a register or memory.
  ASMJIT_INLINE bool isRegOrMem() const noexcept {
    ASMJIT_ASSERT(kOpMem - kOpReg == 1);
    return Utils::inInterval<uint32_t>(getOp(), kOpReg, kOpMem);
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
  //! Cast this operand to `T` type.
  template<typename T>
  ASMJIT_INLINE T& as() noexcept { return static_cast<T&>(*this); }
  //! Cast this operand to `T` type (const).
  template<typename T>
  ASMJIT_INLINE const T& as() const noexcept { return static_cast<const T&>(*this); }

  // --------------------------------------------------------------------------
  // [Reset]
  // --------------------------------------------------------------------------

  //! Reset the `Operand` to none.
  //!
  //! None operand is defined the following way:
  //!   - Its signature is zero (kOpNone, and the rest zero as well).
  //!   - Its id is `0`.
  //!   - The reserved8_4 field is set to `0`.
  //!   - The reserved12_4 field is set to zero.
  //!
  //! In other words, reset operands have all members set to zero. Reset operand
  //! must match the Operand state right after its construction. Alternatively,
  //! if you have an array of operands, you can simply use `memset()`.
  //!
  //! ```
  //! using namespace asmjit;
  //!
  //! Operand a;
  //! Operand b;
  //! assert(a == b);
  //!
  //! b = x86::eax;
  //! assert(a != b);
370
  //!
371
372
373
374
375
376
377
378
379
  //! b.reset();
  //! assert(a == b);
  //!
  //! memset(&b, 0, sizeof(Operand));
  //! assert(a == b);
  //! ```
  ASMJIT_INLINE void reset() noexcept {
    _init_packed_d0_d1(kOpNone, 0);
    _init_packed_d2_d3(0, 0);
380
381
382
  }

  // --------------------------------------------------------------------------
383
  // [Operator Overload]
384
385
386
  // --------------------------------------------------------------------------

  template<typename T>
387
  ASMJIT_INLINE bool operator==(const T& other) const noexcept { return  isEqual(other); }
388
  template<typename T>
389
  ASMJIT_INLINE bool operator!=(const T& other) const noexcept { return !isEqual(other); }
390
391

  // --------------------------------------------------------------------------
392
  // [Members]
393
394
  // --------------------------------------------------------------------------

395
396
397
398
399
400
401
402
403
404
405
  union {
    AnyData _any;                        //!< Generic data.
    RegData _reg;                        //!< Physical or virtual register data.
    MemData _mem;                        //!< Memory address data.
    ImmData _imm;                        //!< Immediate value data.
    LabelData _label;                    //!< Label data.

    uint32_t _signature;                 //!< Operand signature (first 32-bits).
    UInt64 _packed[2];                   //!< Operand packed into two 64-bit integers.
  };
};
406

407
408
409
// ============================================================================
// [asmjit::Operand]
// ============================================================================
410

411
412
413
//! Operand can contain register, memory location, immediate, or label.
class Operand : public Operand_ {
public:
414
  // --------------------------------------------------------------------------
415
  // [Construction / Destruction]
416
417
  // --------------------------------------------------------------------------

418
419
420
421
422
423
424
425
  //! Create an uninitialized operand.
  ASMJIT_INLINE Operand() noexcept { reset(); }
  //! Create a reference to `other` operand.
  ASMJIT_INLINE Operand(const Operand& other) noexcept { _init(other); }
  //! Create a reference to `other` operand.
  explicit ASMJIT_INLINE Operand(const Operand_& other) noexcept { _init(other); }
  //! Create a completely uninitialized operand (dangerous).
  explicit ASMJIT_INLINE Operand(const _NoInit&) noexcept {}
426

427
428
429
  // --------------------------------------------------------------------------
  // [Clone]
  // --------------------------------------------------------------------------
430

431
432
  //! Clone the `Operand`.
  ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); }
433

434
435
  ASMJIT_INLINE Operand& operator=(const Operand_& other) noexcept { copyFrom(other); return *this; }
};
436

437
438
439
// ============================================================================
// [asmjit::Label]
// ============================================================================
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
//! Label (jump target or data location).
//!
//! Label represents a location in code typically used as a jump target, but
//! may be also a reference to some data or a static variable. Label has to be
//! explicitly created by CodeEmitter.
//!
//! Example of using labels:
//!
//! ~~~
//! // Create a CodeEmitter (for example X86Assembler).
//! X86Assembler a;
//!
//! // Create Label instance.
//! Label L1 = a.newLabel();
//!
//! // ... your code ...
//!
//! // Using label.
//! a.jump(L1);
//!
//! // ... your code ...
//!
//! // Bind label to the current position, see `CodeEmitter::bind()`.
//! a.bind(L1);
//! ~~~
class Label : public Operand {
public:
  //! Type of the Label.
  enum Type {
    kTypeAnonymous = 0,                  //!< Anonymous (unnamed) label.
    kTypeLocal     = 1,                  //!< Local label (always has parentId).
    kTypeGlobal    = 2,                  //!< Global label (never has parentId).
    kTypeCount     = 3                   //!< Number of label types.
  };

  // TODO: Find a better place, find a better name.
  enum {
    //! Label tag is used as a sub-type, forming a unique signature across all
    //! operand types as 0x1 is never associated with any register (reg-type).
    //! This means that a memory operand's BASE register can be constructed
    //! from virtually any operand (register vs. label) by just assigning its
    //! type (reg type or label-tag) and operand id.
    kLabelTag = 0x1
  };
485
486

  // --------------------------------------------------------------------------
487
  // [Construction / Destruction]
488
489
  // --------------------------------------------------------------------------

490
491
492
493
494
495
496
497
  //! Create new, unassociated label.
  ASMJIT_INLINE Label() noexcept : Operand(NoInit) { reset(); }
  //! Create a reference to another label.
  ASMJIT_INLINE Label(const Label& other) noexcept : Operand(other) {}

  explicit ASMJIT_INLINE Label(uint32_t id) noexcept : Operand(NoInit) {
    _init_packed_d0_d1(kOpLabel, id);
    _init_packed_d2_d3(0, 0);
498
499
  }

500
501
  explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {}

502
  // --------------------------------------------------------------------------
503
  // [Reset]
504
505
  // --------------------------------------------------------------------------

506
507
508
509
510
  // TODO: I think that if operand is reset it shouldn't say it's a Label, it
  // should be none like all other operands.
  ASMJIT_INLINE void reset() noexcept {
    _init_packed_d0_d1(kOpLabel, 0);
    _init_packed_d2_d3(0, 0);
511
512
513
  }

  // --------------------------------------------------------------------------
514
  // [Label Specific]
515
516
  // --------------------------------------------------------------------------

517
518
519
520
521
522
523
524
525
526
  //! Get if the label was created by CodeEmitter and has an assigned id.
  ASMJIT_INLINE bool isValid() const noexcept { return _label.id != 0; }
  //! Set label id.
  ASMJIT_INLINE void setId(uint32_t id) { _label.id = id; }

  // --------------------------------------------------------------------------
  // [Operator Overload]
  // --------------------------------------------------------------------------

  ASMJIT_INLINE Label& operator=(const Label& other) noexcept { copyFrom(other); return *this; }
527
528
529
};

// ============================================================================
530
// [asmjit::Reg]
531
532
// ============================================================================

533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
#define ASMJIT_DEFINE_REG_TRAITS(TRAITS_T, REG_T, TYPE, KIND, SIZE, COUNT, TYPE_ID) \
template<>                                                                    \
struct TRAITS_T < TYPE > {                                                    \
  typedef REG_T Reg;                                                          \
                                                                              \
  enum {                                                                      \
    kValid     = 1,                                                           \
    kCount     = COUNT,                                                       \
    kTypeId    = TYPE_ID,                                                     \
                                                                              \
    kType      = TYPE,                                                        \
    kKind      = KIND,                                                        \
    kSize      = SIZE,                                                        \
    kSignature = (Operand::kOpReg << Operand::kSignatureOpShift     ) |       \
                 (kType           << Operand::kSignatureRegTypeShift) |       \
                 (kKind           << Operand::kSignatureRegKindShift) |       \
                 (kSize           << Operand::kSignatureSizeShift   )         \
  };                                                                          \
}                                                                             \

#define ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T)                             \
public:                                                                       \
  /*! Default constructor doesn't setup anything, it's like `Operand()`. */   \
  ASMJIT_INLINE REG_T() ASMJIT_NOEXCEPT                                       \
    : BASE_T() {}                                                             \
                                                                              \
  /*! Copy the `other` REG_T register operand. */                             \
  ASMJIT_INLINE REG_T(const REG_T& other) ASMJIT_NOEXCEPT                     \
    : BASE_T(other) {}                                                        \
                                                                              \
  /*! Copy the `other` REG_T register operand having its id set to `rId` */   \
  ASMJIT_INLINE REG_T(const Reg& other, uint32_t rId) ASMJIT_NOEXCEPT         \
    : BASE_T(other, rId) {}                                                   \
                                                                              \
  /*! Create a REG_T register operand based on `signature` and `rId`. */      \
  ASMJIT_INLINE REG_T(const _Init& init, uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT \
    : BASE_T(init, signature, rId) {}                                         \
                                                                              \
  /*! Create a completely uninitialized REG_T register operand (garbage). */  \
  explicit ASMJIT_INLINE REG_T(const _NoInit&) ASMJIT_NOEXCEPT                \
    : BASE_T(NoInit) {}                                                       \
                                                                              \
  /*! Clone the register operand. */                                          \
  ASMJIT_INLINE REG_T clone() const ASMJIT_NOEXCEPT { return REG_T(*this); }  \
                                                                              \
  /*! Create a new register from register type and id. */                     \
  static ASMJIT_INLINE REG_T fromTypeAndId(uint32_t rType, uint32_t rId) ASMJIT_NOEXCEPT { \
    return REG_T(Init, signatureOf(rType), rId);                              \
  }                                                                           \
                                                                              \
  /*! Create a new register from signature and id. */                         \
  static ASMJIT_INLINE REG_T fromSignature(uint32_t signature, uint32_t rId) ASMJIT_NOEXCEPT { \
    return REG_T(Init, signature, rId);                                       \
  }                                                                           \
                                                                              \
  ASMJIT_INLINE REG_T& operator=(const REG_T& other) ASMJIT_NOEXCEPT {        \
    copyFrom(other); return *this;                                            \
  }

#define ASMJIT_DEFINE_FINAL_REG(REG_T, BASE_T, TRAITS_T)                      \
  ASMJIT_DEFINE_ABSTRACT_REG(REG_T, BASE_T)                                   \
                                                                              \
  /*! Create a REG_T register with `id`. */                                   \
  explicit ASMJIT_INLINE REG_T(uint32_t rId) ASMJIT_NOEXCEPT                  \
    : BASE_T(Init, kSignature, rId) {}                                        \
                                                                              \
  enum {                                                                      \
    kThisType  = TRAITS_T::kType,                                             \
    kThisKind  = TRAITS_T::kKind,                                             \
    kThisSize  = TRAITS_T::kSize,                                             \
    kSignature = TRAITS_T::kSignature                                         \
  };

//! Structure that contains core register information.
//!
//! This information is compatible with operand's signature (32-bit integer)
//! and `RegInfo` just provides easy way to access it.
struct RegInfo {
  ASMJIT_INLINE uint32_t getSignature() const noexcept {
    return _signature;
613
614
  }

615
616
  ASMJIT_INLINE uint32_t getOp() const noexcept {
    return (_signature >> Operand::kSignatureOpShift) & Operand::kSignatureOpBits;
617
618
  }

619
620
  ASMJIT_INLINE uint32_t getType() const noexcept {
    return (_signature >> Operand::kSignatureRegTypeShift) & Operand::kSignatureRegTypeBits;
621
622
  }

623
624
  ASMJIT_INLINE uint32_t getKind() const noexcept {
    return (_signature >> Operand::kSignatureRegKindShift) & Operand::kSignatureRegKindBits;
625
626
  }

627
628
  ASMJIT_INLINE uint32_t getSize() const noexcept {
    return (_signature >> Operand::kSignatureSizeShift) & Operand::kSignatureSizeBits;
629
  }
630
631

  uint32_t _signature;
632
633
};

634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
//! Physical/Virtual register operand.
class Reg : public Operand {
public:
  //! Architecture neutral register types.
  //!
  //! These must be reused by any platform that contains that types. All GP
  //! and VEC registers are also allowed by design to be part of a BASE|INDEX
  //! of a memory operand.
  ASMJIT_ENUM(RegType) {
    kRegNone      = 0,                   //!< No register - unused, invalid, multiple meanings.
    // (1 is used as a LabelTag)
    kRegGp8Lo     = 2,                   //!< 8-bit low general purpose register (X86).
    kRegGp8Hi     = 3,                   //!< 8-bit high general purpose register (X86).
    kRegGp16      = 4,                   //!< 16-bit general purpose register (X86).
    kRegGp32      = 5,                   //!< 32-bit general purpose register (X86|ARM).
    kRegGp64      = 6,                   //!< 64-bit general purpose register (X86|ARM).
    kRegVec32     = 7,                   //!< 32-bit view of a vector register (ARM).
    kRegVec64     = 8,                   //!< 64-bit view of a vector register (ARM).
    kRegVec128    = 9,                   //!< 128-bit view of a vector register (X86|ARM).
    kRegVec256    = 10,                  //!< 256-bit view of a vector register (X86).
    kRegVec512    = 11,                  //!< 512-bit view of a vector register (X86).
    kRegVec1024   = 12,                  //!< 1024-bit view of a vector register (future).
    kRegVec2048   = 13,                  //!< 2048-bit view of a vector register (future).
    kRegIP        = 14,                  //!< Universal id of IP/PC register (if separate).
    kRegCustom    = 15,                  //!< Start of platform dependent register types (must be honored).
    kRegMax       = 31                   //!< Maximum possible register id of all architectures.
  };

  //! Architecture neutral register kinds.
  ASMJIT_ENUM(Kind) {
    kKindGp       = 0,                   //!< General purpose register (X86|ARM).
    kKindVec      = 1,                   //!< Vector register (X86|ARM).
    kKindMax      = 15                   //!< Maximum possible register kind of all architectures.
  };
668
669
670
671
672

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

673
674
675
676
677
678
679
680
  //! Create a dummy register operand.
  ASMJIT_INLINE Reg() noexcept : Operand() {}
  //! Create a new register operand which is the same as `other` .
  ASMJIT_INLINE Reg(const Reg& other) noexcept : Operand(other) {}
  //! Create a new register operand compatible with `other`, but with a different `rId`.
  ASMJIT_INLINE Reg(const Reg& other, uint32_t rId) noexcept : Operand(NoInit) {
    _init_packed_d0_d1(other._signature, rId);
    _packed[1] = other._packed[1];
681
682
  }

683
684
685
  //! Create a register initialized to `signature` and `rId`.
  ASMJIT_INLINE Reg(const _Init&, uint32_t signature, uint32_t rId) noexcept : Operand(NoInit) {
    _initReg(signature, rId);
686
  }
687
  explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {}
688

689
690
  //! Create a new register based on `signature` and `rId`.
  static ASMJIT_INLINE Reg fromSignature(uint32_t signature, uint32_t rId) noexcept { return Reg(Init, signature, rId); }
691
692
693
694
695

  // --------------------------------------------------------------------------
  // [Reg Specific]
  // --------------------------------------------------------------------------

696
697
698
699
700
701
  //! Get if the register is valid (either virtual or physical).
  ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; }
  //! Get if this is a physical register.
  ASMJIT_INLINE bool isPhysReg() const noexcept { return _reg.id < Globals::kInvalidRegId; }
  //! Get if this is a virtual register (used by \ref CodeCompiler).
  ASMJIT_INLINE bool isVirtReg() const noexcept { return isPackedId(_reg.id); }
702

703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
  //! Get if this register is the same as `other`.
  //!
  //! This is just an optimization. Registers by default only use the first
  //! 8 bytes of the Operand, so this method takes advantage of this knowledge
  //! and only compares these 8 bytes. If both operands were created correctly
  //! then `isEqual()` and `isSame()` should give the same answer, however, if
  //! some operands contains a garbage or other metadata in the upper 8 bytes
  //! then `isSame()` may return `true` in cases where `isEqual()` returns
  //! false. However. no such case is known at the moment.
  ASMJIT_INLINE bool isSame(const Reg& other) const noexcept { return _packed[0] == other._packed[0]; }

  //! Get if the register type matches `rType` - same as `isReg(rType)`, provided for convenience.
  ASMJIT_INLINE bool isType(uint32_t rType) const noexcept { return (_signature & kSignatureRegTypeMask) == (rType << kSignatureRegTypeShift); }
  //! Get if the register kind matches `rKind`.
  ASMJIT_INLINE bool isKind(uint32_t rKind) const noexcept { return (_signature & kSignatureRegKindMask) == (rKind << kSignatureRegKindShift); }

  //! Get if the register is a general purpose register (any size).
  ASMJIT_INLINE bool isGp() const noexcept { return isKind(kKindGp); }
  //! Get if the register is a vector register.
  ASMJIT_INLINE bool isVec() const noexcept { return isKind(kKindVec); }

  using Operand_::isReg;

  //! Same as `isType()`, provided for convenience.
  ASMJIT_INLINE bool isReg(uint32_t rType) const noexcept { return isType(rType); }
  //! Get if the register type matches `type` and register id matches `rId`.
  ASMJIT_INLINE bool isReg(uint32_t rType, uint32_t rId) const noexcept { return isType(rType) && getId() == rId; }

  //! Get the register type.
  ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(kSignatureRegTypeBits, kSignatureRegTypeShift); }
  //! Get the register kind.
  ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(kSignatureRegKindBits, kSignatureRegKindShift); }

  //! Clone the register operand.
  ASMJIT_INLINE Reg clone() const noexcept { return Reg(*this); }

  //! Cast this register to `RegT` by also changing its signature.
  //!
  //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors.
  template<typename RegT>
  ASMJIT_INLINE RegT cloneAs() const noexcept { return RegT(Init, RegT::kSignature, getId()); }
744

745
746
747
748
749
750
751
752
  //! Cast this register to `other` by also changing its signature.
  //!
  //! NOTE: Improper use of `cloneAs()` can lead to hard-to-debug errors.
  template<typename RegT>
  ASMJIT_INLINE RegT cloneAs(const RegT& other) const noexcept { return RegT(Init, other.getSignature(), getId()); }

  //! Set the register id to `id`.
  ASMJIT_INLINE void setId(uint32_t rId) noexcept { _reg.id = rId; }
753

754
755
756
757
758
759
760
761
  //! Set a 32-bit operand signature based on traits of `RegT`.
  template<typename RegT>
  ASMJIT_INLINE void setSignatureT() noexcept { _signature = RegT::kSignature; }

  //! Set register's `signature` and `rId`.
  ASMJIT_INLINE void setSignatureAndId(uint32_t signature, uint32_t rId) noexcept {
    _signature = signature;
    _reg.id = rId;
762
763
  }

764
765
766
767
768
769
770
771
772
  // --------------------------------------------------------------------------
  // [Reg Statics]
  // --------------------------------------------------------------------------

  static ASMJIT_INLINE bool isGp(const Operand_& op) noexcept {
    // Check operand type and register kind. Not interested in register type and size.
    const uint32_t kSgn = (kOpReg  << kSignatureOpShift     ) |
                          (kKindGp << kSignatureRegKindShift) ;
    return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn;
773
774
  }

775
776
777
778
779
780
  //! Get if the `op` operand is either a low or high 8-bit GPB register.
  static ASMJIT_INLINE bool isVec(const Operand_& op) noexcept {
    // Check operand type and register kind. Not interested in register type and size.
    const uint32_t kSgn = (kOpReg   << kSignatureOpShift     ) |
                          (kKindVec << kSignatureRegKindShift) ;
    return (op.getSignature() & (kSignatureOpMask | kSignatureRegKindMask)) == kSgn;
781
782
  }

783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
  static ASMJIT_INLINE bool isGp(const Operand_& op, uint32_t rId) noexcept { return isGp(op) & (op.getId() == rId); }
  static ASMJIT_INLINE bool isVec(const Operand_& op, uint32_t rId) noexcept { return isVec(op) & (op.getId() == rId); }
};

// ============================================================================
// [asmjit::RegOnly]
// ============================================================================

//! RegOnly is 8-byte version of `Reg` that only allows to store either `Reg`
//! or nothing. This class was designed to decrease the space consumed by each
//! extra "operand" in `CodeEmitter` and `CBInst` classes.
struct RegOnly {
  // --------------------------------------------------------------------------
  // [Init / Reset]
  // --------------------------------------------------------------------------

  //! Initialize the `RegOnly` instance to hold register `signature` and `id`.
  ASMJIT_INLINE void init(uint32_t signature, uint32_t id) noexcept {
    _signature = signature;
    _id = id;
803
804
  }

805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
  ASMJIT_INLINE void init(const Reg& reg) noexcept { init(reg.getSignature(), reg.getId()); }
  ASMJIT_INLINE void init(const RegOnly& reg) noexcept { init(reg.getSignature(), reg.getId()); }

  //! Reset the `RegOnly` to none.
  ASMJIT_INLINE void reset() noexcept { init(0, 0); }

  // --------------------------------------------------------------------------
  // [Accessors]
  // --------------------------------------------------------------------------

  //! Get if the `ExtraReg` is none (same as calling `Operand_::isNone()`).
  ASMJIT_INLINE bool isNone() const noexcept { return _signature == 0; }
  //! Get if the register is valid (either virtual or physical).
  ASMJIT_INLINE bool isValid() const noexcept { return _signature != 0; }

  //! Get if this is a physical register.
  ASMJIT_INLINE bool isPhysReg() const noexcept { return _id < Globals::kInvalidRegId; }
  //! Get if this is a virtual register (used by \ref CodeCompiler).
  ASMJIT_INLINE bool isVirtReg() const noexcept { return Operand::isPackedId(_id); }

  //! Get register signature or 0.
  ASMJIT_INLINE uint32_t getSignature() const noexcept { return _signature; }
  //! Get register id or 0.
  ASMJIT_INLINE uint32_t getId() const noexcept { return _id; }

  //! \internal
  //!
  //! Unpacks information from operand's signature.
  ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept { return (_signature >> shift) & bits; }

  //! Get the register type.
  ASMJIT_INLINE uint32_t getType() const noexcept { return _getSignatureData(Operand::kSignatureRegTypeBits, Operand::kSignatureRegTypeShift); }
  //! Get the register kind.
  ASMJIT_INLINE uint32_t getKind() const noexcept { return _getSignatureData(Operand::kSignatureRegKindBits, Operand::kSignatureRegKindShift); }

  // --------------------------------------------------------------------------
  // [ToReg]
  // --------------------------------------------------------------------------

  //! Convert back to `RegT` operand.
  template<typename RegT>
  ASMJIT_INLINE RegT toReg() const noexcept { return RegT(Init, _signature, _id); }

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

  //! Type of the operand, either `kOpNone` or `kOpReg`.
  uint32_t _signature;
  //! Physical or virtual register id.
  uint32_t _id;
856
857
858
};

// ============================================================================
859
// [asmjit::Mem]
860
861
862
// ============================================================================

//! Base class for all memory operands.
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
//!
//! NOTE: It's tricky to pack all possible cases that define a memory operand
//! into just 16 bytes. The `Mem` splits data into the following parts:
//!
//!   BASE - Base register or label - requires 36 bits total. 4 bits are used
//!     to encode the type of the BASE operand (label vs. register type) and
//!     the remaining 32 bits define the BASE id, which can be a physical or
//!     virtual register index. If BASE type is zero, which is never used as
//!     a register-type and label doesn't use it as well then BASE field
//!     contains a high DWORD of a possible 64-bit absolute address, which is
//!     possible on X64.
//!
//!   INDEX - Index register (or theoretically Label, which doesn't make sense).
//!     Encoding is similar to BASE - it also requires 36 bits and splits the
//!     encoding to INDEX type (4 bits defining the register type) and id (32-bits).
//!
//!   OFFSET - A relative offset of the address. Basically if BASE is specified
//!     the relative displacement adjusts BASE and an optional INDEX. if BASE is
//!     not specified then the OFFSET should be considered as ABSOLUTE address
//!     (at least on X86/X64). In that case its low 32 bits are stored in
//!     DISPLACEMENT field and the remaining high 32 bits are stored in BASE.
//!
//!   OTHER FIELDS - There is rest 8 bits that can be used for whatever purpose.
//!          The X86Mem operand uses these bits to store segment override
//!          prefix and index shift (scale).
class Mem : public Operand {
public:
  enum AddrType {
    kAddrTypeDefault = 0,
    kAddrTypeAbs     = 1,
    kAddrTypeRel     = 2,
    kAddrTypeWrt     = 3
  };

  // Shortcuts.
  enum SignatureMem {
    kSignatureMemAbs = kAddrTypeAbs << kSignatureMemAddrTypeShift,
    kSignatureMemRel = kAddrTypeRel << kSignatureMemAddrTypeShift,
    kSignatureMemWrt = kAddrTypeWrt << kSignatureMemAddrTypeShift
  };

904
905
906
907
  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

908
909
910
  //! Construct a default `Mem` operand, that points to [0].
  ASMJIT_INLINE Mem() noexcept : Operand(NoInit) { reset(); }
  ASMJIT_INLINE Mem(const Mem& other) noexcept : Operand(other) {}
911

912
913
914
915
916
917
918
919
920
921
922
923
924
925
  ASMJIT_INLINE Mem(const _Init&,
    uint32_t baseType, uint32_t baseId,
    uint32_t indexType, uint32_t indexId,
    int32_t off, uint32_t size, uint32_t flags) noexcept : Operand(NoInit) {

    uint32_t signature = (baseType  << kSignatureMemBaseTypeShift ) |
                         (indexType << kSignatureMemIndexTypeShift) |
                         (size      << kSignatureSizeShift        ) ;

    _init_packed_d0_d1(kOpMem | signature | flags, indexId);
    _mem.base = baseId;
    _mem.offsetLo32 = static_cast<uint32_t>(off);
  }
  explicit ASMJIT_INLINE Mem(const _NoInit&) noexcept : Operand(NoInit) {}
926
927

  // --------------------------------------------------------------------------
928
  // [Mem Specific]
929
930
  // --------------------------------------------------------------------------

931
932
  //! Clone `Mem` operand.
  ASMJIT_INLINE Mem clone() const noexcept { return Mem(*this); }
933

934
935
936
937
  //! Reset the memory operand - after reset the memory points to [0].
  ASMJIT_INLINE void reset() noexcept {
    _init_packed_d0_d1(kOpMem, 0);
    _init_packed_d2_d3(0, 0);
938
939
  }

940
941
942
943
  ASMJIT_INLINE bool hasAddrType() const noexcept { return _hasSignatureData(kSignatureMemAddrTypeMask); }
  ASMJIT_INLINE uint32_t getAddrType() const noexcept { return _getSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); }
  ASMJIT_INLINE void setAddrType(uint32_t addrType) noexcept { return _setSignatureData(addrType, kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); }
  ASMJIT_INLINE void resetAddrType() noexcept { return _clearSignatureData(kSignatureMemAddrTypeBits, kSignatureMemAddrTypeShift); }
944

945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
  ASMJIT_INLINE bool isAbs() const noexcept { return getAddrType() == kAddrTypeAbs; }
  ASMJIT_INLINE bool isRel() const noexcept { return getAddrType() == kAddrTypeRel; }
  ASMJIT_INLINE bool isWrt() const noexcept { return getAddrType() == kAddrTypeWrt; }

  ASMJIT_INLINE void setAbs() noexcept { setAddrType(kAddrTypeAbs); }
  ASMJIT_INLINE void setRel() noexcept { setAddrType(kAddrTypeRel); }
  ASMJIT_INLINE void setWrt() noexcept { setAddrType(kAddrTypeWrt); }

  ASMJIT_INLINE bool isArgHome() const noexcept { return _hasSignatureData(kSignatureMemArgHomeFlag); }
  ASMJIT_INLINE bool isRegHome() const noexcept { return _hasSignatureData(kSignatureMemRegHomeFlag); }

  ASMJIT_INLINE void setArgHome() noexcept { _signature |= kSignatureMemArgHomeFlag; }
  ASMJIT_INLINE void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; }

  ASMJIT_INLINE void clearArgHome() noexcept { _signature &= ~kSignatureMemArgHomeFlag; }
  ASMJIT_INLINE void clearRegHome() noexcept { _signature &= ~kSignatureMemRegHomeFlag; }

  //! Get if the memory operand has a BASE register or label specified.
  ASMJIT_INLINE bool hasBase() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0; }
  //! Get if the memory operand has an INDEX register specified.
  ASMJIT_INLINE bool hasIndex() const noexcept { return (_signature & kSignatureMemIndexTypeMask) != 0; }
  //! Get whether the memory operand has BASE and INDEX register.
  ASMJIT_INLINE bool hasBaseOrIndex() const noexcept { return (_signature & kSignatureMemBaseIndexMask) != 0; }
  //! Get whether the memory operand has BASE and INDEX register.
  ASMJIT_INLINE bool hasBaseAndIndex() const noexcept { return (_signature & kSignatureMemBaseTypeMask) != 0 && (_signature & kSignatureMemIndexTypeMask) != 0; }

  //! Get if the BASE operand is a register (registers start after `kLabelTag`).
  ASMJIT_INLINE bool hasBaseReg() const noexcept { return (_signature & kSignatureMemBaseTypeMask) > (Label::kLabelTag << kSignatureMemBaseTypeShift); }
  //! Get if the BASE operand is a label.
  ASMJIT_INLINE bool hasBaseLabel() const noexcept { return (_signature & kSignatureMemBaseTypeMask) == (Label::kLabelTag << kSignatureMemBaseTypeShift); }
  //! Get if the INDEX operand is a register (registers start after `kLabelTag`).
  ASMJIT_INLINE bool hasIndexReg() const noexcept { return (_signature & kSignatureMemIndexTypeMask) > (Label::kLabelTag << kSignatureMemIndexTypeShift); }

  //! Get type of a BASE register (0 if this memory operand doesn't use the BASE register).
  //!
  //! NOTE: If the returned type is one (a value never associated to a register
  //! type) the BASE is not register, but it's a label. One equals to `kLabelTag`.
  //! You should always check `hasBaseLabel()` before using `getBaseId()` result.
  ASMJIT_INLINE uint32_t getBaseType() const noexcept { return _getSignatureData(kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift); }
  //! Get type of an INDEX register (0 if this memory operand doesn't use the INDEX register).
  ASMJIT_INLINE uint32_t getIndexType() const noexcept { return _getSignatureData(kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift); }

  //! Get both BASE (4:0 bits) and INDEX (9:5 bits) types combined into a single integer.
  //!
  //! This is used internally for BASE+INDEX validation.
  ASMJIT_INLINE uint32_t getBaseIndexType() const noexcept { return _getSignatureData(kSignatureMemBaseIndexBits, kSignatureMemBaseIndexShift); }

  //! Get id of the BASE register or label (if the BASE was specified as label).
  ASMJIT_INLINE uint32_t getBaseId() const noexcept { return _mem.base; }
  //! Get id of the INDEX register.
  ASMJIT_INLINE uint32_t getIndexId() const noexcept { return _mem.index; }
996

997
998
999
  ASMJIT_INLINE void _setBase(uint32_t rType, uint32_t rId) noexcept {
    _setSignatureData(rType, kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift);
    _mem.base = rId;
1000
1001
  }

1002
1003
1004
  ASMJIT_INLINE void _setIndex(uint32_t rType, uint32_t rId) noexcept {
    _setSignatureData(rType, kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift);
    _mem.index = rId;
1005
1006
  }

1007
1008
1009
1010
1011
1012
1013
1014
  ASMJIT_INLINE void setBase(const Reg& base) noexcept { return _setBase(base.getType(), base.getId()); }
  ASMJIT_INLINE void setIndex(const Reg& index) noexcept { return _setIndex(index.getType(), index.getId()); }

  //! Reset the memory operand's BASE register / label.
  ASMJIT_INLINE void resetBase() noexcept { _setBase(0, 0); }
  //! Reset the memory operand's INDEX register.
  ASMJIT_INLINE void resetIndex() noexcept { _setIndex(0, 0); }

1015
  //! Set memory operand size.
1016
1017
  ASMJIT_INLINE void setSize(uint32_t size) noexcept {
    _setSignatureData(size, kSignatureSizeBits, kSignatureSizeShift);
1018
1019
  }

1020
1021
1022
1023
  ASMJIT_INLINE bool hasOffset() const noexcept {
    int32_t lo = static_cast<int32_t>(_mem.offsetLo32);
    int32_t hi = static_cast<int32_t>(_mem.base) & -static_cast<int32_t>(getBaseType() == 0);
    return (lo | hi) != 0;
1024
1025
  }

1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
  //! Get if the memory operand has 64-bit offset or absolute address.
  //!
  //! If this is true then `hasBase()` must always report false.
  ASMJIT_INLINE bool has64BitOffset() const noexcept { return getBaseType() == 0; }

  //! Get a 64-bit offset or absolute address.
  ASMJIT_INLINE int64_t getOffset() const noexcept {
    return has64BitOffset()
      ? static_cast<int64_t>(_mem.offset64)
      : static_cast<int64_t>(static_cast<int32_t>(_mem.offsetLo32)); // Sign-Extend.
1036
1037
  }

1038
1039
1040
1041
1042
1043
1044
  //! Get a lower part of a 64-bit offset or absolute address.
  ASMJIT_INLINE int32_t getOffsetLo32() const noexcept { return static_cast<int32_t>(_mem.offsetLo32); }
  //! Get a higher part of a 64-bit offset or absolute address.
  //!
  //! NOTE: This function is UNSAFE and returns garbage if `has64BitOffset()`
  //! returns false. Never use it blindly without checking it.
  ASMJIT_INLINE int32_t getOffsetHi32() const noexcept { return static_cast<int32_t>(_mem.base); }
1045

1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
  //! Set a 64-bit offset or an absolute address to `offset`.
  //!
  //! NOTE: This functions attempts to set both high and low parts of a 64-bit
  //! offset, however, if the operand has a BASE register it will store only the
  //! low 32 bits of the offset / address as there is no way to store both BASE
  //! and 64-bit offset, and there is currently no architecture that has such
  //! capability targeted by AsmJit.
  ASMJIT_INLINE void setOffset(int64_t offset) noexcept {
    if (has64BitOffset())
      _mem.offset64 = static_cast<uint64_t>(offset);
    else
      _mem.offsetLo32 = static_cast<int32_t>(offset & 0xFFFFFFFF);
1058
  }
1059
1060
1061
1062
1063
1064
  //! Adjust the offset by a 64-bit `off`.
  ASMJIT_INLINE void addOffset(int64_t off) noexcept {
    if (has64BitOffset())
      _mem.offset64 += static_cast<uint64_t>(off);
    else
      _mem.offsetLo32 += static_cast<uint32_t>(off & 0xFFFFFFFF);
1065
  }
1066
1067
  //! Reset the memory offset to zero.
  ASMJIT_INLINE void resetOffset() noexcept { setOffset(0); }
1068

1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
  //! Set a low 32-bit offset to `off`.
  ASMJIT_INLINE void setOffsetLo32(int32_t off) noexcept {
    _mem.offsetLo32 = static_cast<uint32_t>(off);
  }
  //! Adjust the offset by `off`.
  //!
  //! NOTE: This is a fast function that doesn't use the HI 32-bits of a
  //! 64-bit offset. Use it only if you know that there is a BASE register
  //! and the offset is only 32 bits anyway.
  ASMJIT_INLINE void addOffsetLo32(int32_t off) noexcept {
    _mem.offsetLo32 += static_cast<uint32_t>(off);
1080
  }
1081
1082
1083
1084
1085
1086
1087
1088
  //! Reset the memory offset to zero.
  ASMJIT_INLINE void resetOffsetLo32() noexcept { setOffsetLo32(0); }

  // --------------------------------------------------------------------------
  // [Operator Overload]
  // --------------------------------------------------------------------------

  ASMJIT_INLINE Mem& operator=(const Mem& other) noexcept { copyFrom(other); return *this; }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
};

// ============================================================================
// [asmjit::Imm]
// ============================================================================

//! Immediate operand.
//!
//! Immediate operand is usually part of instruction itself. It's inlined after
//! or before the instruction opcode. Immediates can be only signed or unsigned
//! integers.
//!
//! To create immediate operand use `imm()` or `imm_u()` non-members or `Imm`
//! constructors.
1103
1104
class Imm : public Operand {
public:
1105
1106
1107
1108
1109
  // --------------------------------------------------------------------------
  // [Construction / Destruction]
  // --------------------------------------------------------------------------

  //! Create a new immediate value (initial value is 0).
1110
1111
1112
  Imm() noexcept : Operand(NoInit) {
    _init_packed_d0_d1(kOpImm, 0);
    _imm.value.i64 = 0;
1113
1114
1115
  }

  //! Create a new signed immediate value, assigning the value to `val`.
1116
1117
1118
  explicit Imm(int64_t val) noexcept : Operand(NoInit) {
    _init_packed_d0_d1(kOpImm, 0);
    _imm.value.i64 = val;
1119
1120
1121
  }

  //! Create a new immediate value from `other`.
1122
  ASMJIT_INLINE Imm(const Imm& other) noexcept : Operand(other) {}
1123

1124
  explicit ASMJIT_INLINE Imm(const _NoInit&) noexcept : Operand(NoInit) {}
1125
1126
1127
1128
1129
1130

  // --------------------------------------------------------------------------
  // [Immediate Specific]
  // --------------------------------------------------------------------------

  //! Clone `Imm` operand.
1131
  ASMJIT_INLINE Imm clone() const noexcept { return Imm(*this); }
1132
1133

  //! Get whether the immediate can be casted to 8-bit signed integer.
1134
  ASMJIT_INLINE bool isInt8() const noexcept { return Utils::isInt8(_imm.value.i64); }
1135
  //! Get whether the immediate can be casted to 8-bit unsigned integer.
1136
  ASMJIT_INLINE bool isUInt8() const noexcept { return Utils::isUInt8(_imm.value.i64); }
1137
1138

  //! Get whether the immediate can be casted to 16-bit signed integer.
1139
  ASMJIT_INLINE bool isInt16() const noexcept { return Utils::isInt16(_imm.value.i64); }
1140
  //! Get whether the immediate can be casted to 16-bit unsigned integer.
1141
  ASMJIT_INLINE bool isUInt16() const noexcept { return Utils::isUInt16(_imm.value.i64); }
1142
1143

  //! Get whether the immediate can be casted to 32-bit signed integer.
1144
  ASMJIT_INLINE bool isInt32() const noexcept { return Utils::isInt32(_imm.value.i64); }
1145
  //! Get whether the immediate can be casted to 32-bit unsigned integer.
1146
  ASMJIT_INLINE bool isUInt32() const noexcept { return Utils::isUInt32(_imm.value.i64); }
1147
1148

  //! Get immediate value as 8-bit signed integer.
1149
  ASMJIT_INLINE int8_t getInt8() const noexcept { return static_cast<int8_t>(_imm.value.i32Lo & 0xFF); }
1150
  //! Get immediate value as 8-bit unsigned integer.
1151
  ASMJIT_INLINE uint8_t getUInt8() const noexcept { return static_cast<uint8_t>(_imm.value.u32Lo & 0xFFU); }
1152
  //! Get immediate value as 16-bit signed integer.
1153
  ASMJIT_INLINE int16_t getInt16() const noexcept { return static_cast<int16_t>(_imm.value.i32Lo & 0xFFFF);}
1154
  //! Get immediate value as 16-bit unsigned integer.
1155
1156
  ASMJIT_INLINE uint16_t getUInt16() const noexcept { return static_cast<uint16_t>(_imm.value.u32Lo & 0xFFFFU);}

1157
  //! Get immediate value as 32-bit signed integer.
1158
1159
1160
1161
1162
1163
  ASMJIT_INLINE int32_t getInt32() const noexcept { return _imm.value.i32Lo; }
  //! Get low 32-bit signed integer.
  ASMJIT_INLINE int32_t getInt32Lo() const noexcept { return _imm.value.i32Lo; }
  //! Get high 32-bit signed integer.
  ASMJIT_INLINE int32_t getInt32Hi() const noexcept { return _imm.value.i32Hi; }

1164
  //! Get immediate value as 32-bit unsigned integer.
1165
1166
1167
1168
1169
1170
  ASMJIT_INLINE uint32_t getUInt32() const noexcept { return _imm.value.u32Lo; }
  //! Get low 32-bit signed integer.
  ASMJIT_INLINE uint32_t getUInt32Lo() const noexcept { return _imm.value.u32Lo; }
  //! Get high 32-bit signed integer.
  ASMJIT_INLINE uint32_t getUInt32Hi() const noexcept { return _imm.value.u32Hi; }

1171
  //! Get immediate value as 64-bit signed integer.
1172
  ASMJIT_INLINE int64_t getInt64() const noexcept { return _imm.value.i64; }
1173
  //! Get immediate value as 64-bit unsigned integer.
1174
  ASMJIT_INLINE uint64_t getUInt64() const noexcept { return _imm.value.u64; }
1175
1176

  //! Get immediate value as `intptr_t`.
1177
  ASMJIT_INLINE intptr_t getIntPtr() const noexcept {
1178
1179
1180
1181
1182
1183
1184
    if (sizeof(intptr_t) == sizeof(int64_t))
      return static_cast<intptr_t>(getInt64());
    else
      return static_cast<intptr_t>(getInt32());
  }

  //! Get immediate value as `uintptr_t`.
1185
  ASMJIT_INLINE uintptr_t getUIntPtr() const noexcept {
1186
1187
1188
1189
1190
1191
1192
    if (sizeof(uintptr_t) == sizeof(uint64_t))
      return static_cast<uintptr_t>(getUInt64());
    else
      return static_cast<uintptr_t>(getUInt32());
  }

  //! Set immediate value to 8-bit signed integer `val`.
1193
  ASMJIT_INLINE void setInt8(int8_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
1194
  //! Set immediate value to 8-bit unsigned integer `val`.
1195
  ASMJIT_INLINE void setUInt8(uint8_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
1196
1197

  //! Set immediate value to 16-bit signed integer `val`.
1198
  ASMJIT_INLINE void setInt16(int16_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
1199
  //! Set immediate value to 16-bit unsigned integer `val`.
1200
  ASMJIT_INLINE void setUInt16(uint16_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
1201
1202

  //! Set immediate value to 32-bit signed integer `val`.
1203
  ASMJIT_INLINE void setInt32(int32_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
1204
  //! Set immediate value to 32-bit unsigned integer `val`.
1205
  ASMJIT_INLINE void setUInt32(uint32_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
1206
1207

  //! Set immediate value to 64-bit signed integer `val`.
1208
  ASMJIT_INLINE void setInt64(int64_t val) noexcept { _imm.value.i64 = val; }
1209
  //! Set immediate value to 64-bit unsigned integer `val`.
1210
  ASMJIT_INLINE void setUInt64(uint64_t val) noexcept { _imm.value.u64 = val; }
1211
  //! Set immediate value to intptr_t `val`.
1212
  ASMJIT_INLINE void setIntPtr(intptr_t val) noexcept { _imm.value.i64 = static_cast<int64_t>(val); }
1213
  //! Set immediate value to uintptr_t `val`.
1214
  ASMJIT_INLINE void setUIntPtr(uintptr_t val) noexcept { _imm.value.u64 = static_cast<uint64_t>(val); }
1215
1216

  //! Set immediate value as unsigned type to `val`.
1217
1218
1219
1220
  ASMJIT_INLINE void setPtr(void* p) noexcept { setIntPtr((uint64_t)p); }
  //! Set immediate value to `val`.
  template<typename T>
  ASMJIT_INLINE void setValue(T val) noexcept { setIntPtr((int64_t)val); }
1221
1222
1223
1224
1225

  // --------------------------------------------------------------------------
  // [Float]
  // --------------------------------------------------------------------------

1226
1227
1228
  ASMJIT_INLINE void setFloat(float f) noexcept {
    _imm.value.f32Lo = f;
    _imm.value.u32Hi = 0;
1229
1230
  }

1231
1232
  ASMJIT_INLINE void setDouble(double d) noexcept {
    _imm.value.f64 = d;
1233
1234
1235
1236
1237
1238
  }

  // --------------------------------------------------------------------------
  // [Truncate]
  // --------------------------------------------------------------------------

1239
1240
1241
  ASMJIT_INLINE void truncateTo8Bits() noexcept {
    if (ASMJIT_ARCH_64BIT) {
      _imm.value.u64   &= static_cast<uint64_t>(0x000000FFU);
1242
1243
    }
    else {
1244
1245
      _imm.value.u32Lo &= 0x000000FFU;
      _imm.value.u32Hi  = 0;
1246
1247
1248
    }
  }

1249
1250
1251
  ASMJIT_INLINE void truncateTo16Bits() noexcept {
    if (ASMJIT_ARCH_64BIT) {
      _imm.value.u64   &= static_cast<uint64_t>(0x0000FFFFU);
1252
1253
    }
    else {
1254
1255
      _imm.value.u32Lo &= 0x0000FFFFU;
      _imm.value.u32Hi  = 0;
1256
1257
1258
    }
  }

1259
  ASMJIT_INLINE void truncateTo32Bits() noexcept { _imm.value.u32Hi = 0; }
1260
1261
1262
1263
1264
1265

  // --------------------------------------------------------------------------
  // [Operator Overload]
  // --------------------------------------------------------------------------

  //! Assign `other` to the immediate operand.
1266
  ASMJIT_INLINE Imm& operator=(const Imm& other) noexcept { copyFrom(other); return *this; }
1267
1268
};

1269
1270
1271
1272
1273
1274
1275
1276
//! Create a signed immediate operand.
static ASMJIT_INLINE Imm imm(int64_t val) noexcept { return Imm(val); }
//! Create an unsigned immediate operand.
static ASMJIT_INLINE Imm imm_u(uint64_t val) noexcept { return Imm(static_cast<int64_t>(val)); }
//! Create an immediate operand from `p`.
template<typename T>
static ASMJIT_INLINE Imm imm_ptr(T p) noexcept { return Imm(static_cast<int64_t>((intptr_t)p)); }

1277
// ============================================================================
1278
// [asmjit::TypeId]
1279
1280
// ============================================================================

1281
//! Type-id.
1282
//!
1283
1284
1285
1286
1287
1288
//! This is an additional information that can be used to describe a physical
//! or virtual register. it's used mostly by CodeCompiler to describe register
//! representation (the kind of data stored in the register and the width used)
//! and it's also used by APIs that allow to describe and work with function
//! signatures.
struct TypeId {
1289
  // --------------------------------------------------------------------------
1290
  // [Id]
1291
1292
  // --------------------------------------------------------------------------

1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
  enum Id {
    kVoid         = 0,

    _kIntStart    = 32,
    _kIntEnd      = 41,

    kIntPtr       = 32,
    kUIntPtr      = 33,

    kI8           = 34,
    kU8           = 35,
    kI16          = 36,
    kU16          = 37,
    kI32          = 38,
    kU32          = 39,
    kI64          = 40,
    kU64          = 41,

    _kFloatStart  = 42,
    _kFloatEnd    = 44,

    kF32          = 42,
    kF64          = 43,
    kF80          = 44,

    _kMaskStart   = 45,
    _kMaskEnd     = 48,

    kMask8        = 45,
    kMask16       = 46,
    kMask32       = 47,
    kMask64       = 48,

    _kMmxStart    = 49,
    _kMmxEnd      = 50,

    kMmx32        = 49,
    kMmx64        = 50,

    _kVec32Start  = 51,
    _kVec32End    = 60,

    kI8x4         = 51,
    kU8x4         = 52,
    kI16x2        = 53,
    kU16x2        = 54,
    kI32x1        = 55,
    kU32x1        = 56,
    kF32x1        = 59,

    _kVec64Start  = 61,
    _kVec64End    = 70,

    kI8x8         = 61,
    kU8x8         = 62,
    kI16x4        = 63,
    kU16x4        = 64,
    kI32x2        = 65,
    kU32x2        = 66,
    kI64x1        = 67,
    kU64x1        = 68,
    kF32x2        = 69,
    kF64x1        = 70,

    _kVec128Start = 71,
    _kVec128End   = 80,

    kI8x16        = 71,
    kU8x16        = 72,
    kI16x8        = 73,
    kU16x8        = 74,
    kI32x4        = 75,
    kU32x4        = 76,
    kI64x2        = 77,
    kU64x2        = 78,
    kF32x4        = 79,
    kF64x2        = 80,

    _kVec256Start = 81,
    _kVec256End   = 90,

    kI8x32        = 81,
    kU8x32        = 82,
    kI16x16       = 83,
    kU16x16       = 84,
    kI32x8        = 85,
    kU32x8        = 86,
    kI64x4        = 87,
    kU64x4        = 88,
    kF32x8        = 89,
    kF64x4        = 90,

    _kVec512Start = 91,
    _kVec512End   = 100,

    kI8x64        = 91,
    kU8x64        = 92,
    kI16x32       = 93,
    kU16x32       = 94,
    kI32x16       = 95,
    kU32x16       = 96,
    kI64x8        = 97,
    kU64x8        = 98,
    kF32x16       = 99,
    kF64x8        = 100,

    kCount        = 101
  };
1401
1402

  // --------------------------------------------------------------------------
1403
  // [TypeName - Used by Templates]
1404
1405
  // --------------------------------------------------------------------------

1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
  struct Int8    {};                     //!< int8_t as C++ type-name.
  struct UInt8   {};                     //!< uint8_t as C++ type-name.
  struct Int16   {};                     //!< int16_t as C++ type-name.
  struct UInt16  {};                     //!< uint16_t as C++ type-name.
  struct Int32   {};                     //!< int32_t as C++ type-name.
  struct UInt32  {};                     //!< uint32_t as C++ type-name.
  struct Int64   {};                     //!< int64_t as C++ type-name.
  struct UInt64  {};                     //!< uint64_t as C++ type-name.
  struct IntPtr  {};                     //!< intptr_t as C++ type-name.
  struct UIntPtr {};                     //!< uintptr_t as C++ type-name.
  struct Float   {};                     //!< float as C++ type-name.
  struct Double  {};                     //!< double as C++ type-name.
  struct MmxReg  {};                     //!< MMX register as C++ type-name.
  struct Vec128  {};                     //!< SIMD128/XMM register as C++ type-name.
  struct Vec256  {};                     //!< SIMD256/YMM register as C++ type-name.
  struct Vec512  {};                     //!< SIMD512/ZMM register as C++ type-name.
1422
1423

  // --------------------------------------------------------------------------
1424
  // [Utilities]
1425
1426
  // --------------------------------------------------------------------------

1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
  struct Info {
    uint8_t sizeOf[128];
    uint8_t elementOf[128];
  };

  ASMJIT_API static const Info _info;

  static ASMJIT_INLINE bool isVoid(uint32_t typeId) noexcept { return typeId == 0; }
  static ASMJIT_INLINE bool isValid(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kVec512End; }
  static ASMJIT_INLINE bool isAbstract(uint32_t typeId) noexcept { return typeId >= kIntPtr && typeId <= kUIntPtr; }
  static ASMJIT_INLINE bool isInt(uint32_t typeId) noexcept { return typeId >= _kIntStart && typeId <= _kIntEnd; }
  static ASMJIT_INLINE bool isGpb(uint32_t typeId) noexcept { return typeId >= kI8 && typeId <= kU8; }
  static ASMJIT_INLINE bool isGpw(uint32_t typeId) noexcept { return typeId >= kI16 && typeId <= kU16; }
  static ASMJIT_INLINE bool isGpd(uint32_t typeId) noexcept { return typeId >= kI32 && typeId <= kU32; }
  static ASMJIT_INLINE bool isGpq(uint32_t typeId) noexcept { return typeId >= kI64 && typeId <= kU64; }
  static ASMJIT_INLINE bool isFloat(uint32_t typeId) noexcept { return typeId >= _kFloatStart && typeId <= _kFloatEnd; }
  static ASMJIT_INLINE bool isMask(uint32_t typeId) noexcept { return typeId >= _kMaskStart && typeId <= _kMaskEnd; }
  static ASMJIT_INLINE bool isMmx(uint32_t typeId) noexcept { return typeId >= _kMmxStart && typeId <= _kMmxEnd; }

  static ASMJIT_INLINE bool isVec(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec512End; }
  static ASMJIT_INLINE bool isVec32(uint32_t typeId) noexcept { return typeId >= _kVec32Start && typeId <= _kVec32End; }
  static ASMJIT_INLINE bool isVec64(uint32_t typeId) noexcept { return typeId >= _kVec64Start && typeId <= _kVec64End; }
  static ASMJIT_INLINE bool isVec128(uint32_t typeId) noexcept { return typeId >= _kVec128Start && typeId <= _kVec128End; }
  static ASMJIT_INLINE bool isVec256(uint32_t typeId) noexcept { return typeId >= _kVec256Start && typeId <= _kVec256End; }
  static ASMJIT_INLINE bool isVec512(uint32_t typeId) noexcept { return typeId >= _kVec512Start && typeId <= _kVec512End; }

  static ASMJIT_INLINE uint32_t sizeOf(uint32_t typeId) noexcept {
    ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.sizeOf));
    return _info.sizeOf[typeId];
1456
1457
  }

1458
1459
1460
1461
  static ASMJIT_INLINE uint32_t elementOf(uint32_t typeId) noexcept {
    ASMJIT_ASSERT(typeId < ASMJIT_ARRAY_SIZE(_info.elementOf));
    return _info.elementOf[typeId];
  }
1462

1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
  //! Get an offset to convert a `kIntPtr` and `kUIntPtr` TypeId into a
  //! type that matches `gpSize` (general-purpose register size). If you
  //! find such TypeId it's then only about adding the offset to it.
  //!
  //! For example:
  //! ~~~
  //! uint32_t gpSize = '4' or '8';
  //! uint32_t deabstractDelta = TypeId::deabstractDeltaOfSize(gpSize);
  //!
  //! uint32_t typeId = 'some type-id';
  //!
  //! // Normalize some typeId into a non-abstract typeId.
  //! if (TypeId::isAbstract(typeId)) typeId += deabstractDelta;
  //!
  //! // The same, but by using TypeId::deabstract() function.
  //! typeId = TypeId::deabstract(typeId, deabstractDelta);
  //! ~~~
  static ASMJIT_INLINE uint32_t deabstractDeltaOfSize(uint32_t gpSize) noexcept {
    return gpSize >= 8 ? kI64 - kIntPtr : kI32 - kIntPtr;
  }
1483

1484
1485
1486
  static ASMJIT_INLINE uint32_t deabstract(uint32_t typeId, uint32_t deabstractDelta) noexcept {
    return TypeId::isAbstract(typeId) ? typeId += deabstractDelta : typeId;
  }
1487
1488
};

1489
1490
1491
1492
1493
1494
1495
//! TypeIdOf<> template allows to get a TypeId of a C++ type.
template<typename T> struct TypeIdOf {
  // Don't provide anything if not specialized.
};
template<typename T> struct TypeIdOf<T*> {
  enum { kTypeId = TypeId::kUIntPtr };
};
1496

1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
template<typename T>
struct TypeIdOfInt {
  enum {
    kSigned = int(~T(0) < T(0)),
    kTypeId = (sizeof(T) == 1) ? (int)(kSigned ? TypeId::kI8  : TypeId::kU8 ) :
              (sizeof(T) == 2) ? (int)(kSigned ? TypeId::kI16 : TypeId::kU16) :
              (sizeof(T) == 4) ? (int)(kSigned ? TypeId::kI32 : TypeId::kU32) :
              (sizeof(T) == 8) ? (int)(kSigned ? TypeId::kI64 : TypeId::kU64) : (int)TypeId::kVoid
  };
};
1507

1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
#define ASMJIT_DEFINE_TYPE_ID(T, TYPE_ID) \
  template<> \
  struct TypeIdOf<T> { enum { kTypeId = TYPE_ID}; }

ASMJIT_DEFINE_TYPE_ID(signed char       , TypeIdOfInt< signed char        >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned char     , TypeIdOfInt< unsigned char      >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(short             , TypeIdOfInt< short              >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned short    , TypeIdOfInt< unsigned short     >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(int               , TypeIdOfInt< int                >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned int      , TypeIdOfInt< unsigned int       >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(long              , TypeIdOfInt< long               >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned long     , TypeIdOfInt< unsigned long      >::kTypeId);
#if ASMJIT_CC_MSC && !ASMJIT_CC_MSC_GE(16, 0, 0)
ASMJIT_DEFINE_TYPE_ID(__int64           , TypeIdOfInt< __int64            >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned __int64  , TypeIdOfInt< unsigned __int64   >::kTypeId);
#else
ASMJIT_DEFINE_TYPE_ID(long long         , TypeIdOfInt< long long          >::kTypeId);
ASMJIT_DEFINE_TYPE_ID(unsigned long long, TypeIdOfInt< unsigned long long >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR
ASMJIT_DEFINE_TYPE_ID(char              , TypeIdOfInt< char               >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR16_T
ASMJIT_DEFINE_TYPE_ID(char16_t          , TypeIdOfInt< char16_t           >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_CHAR32_T
ASMJIT_DEFINE_TYPE_ID(char32_t          , TypeIdOfInt< char32_t           >::kTypeId);
#endif
#if ASMJIT_CC_HAS_NATIVE_WCHAR_T
ASMJIT_DEFINE_TYPE_ID(wchar_t           , TypeIdOfInt< wchar_t            >::kTypeId);
#endif
1539

1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
ASMJIT_DEFINE_TYPE_ID(void              , TypeId::kVoid);
ASMJIT_DEFINE_TYPE_ID(bool              , TypeId::kI8);
ASMJIT_DEFINE_TYPE_ID(float             , TypeId::kF32);
ASMJIT_DEFINE_TYPE_ID(double            , TypeId::kF64);

ASMJIT_DEFINE_TYPE_ID(TypeId::Int8      , TypeId::kI8);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt8     , TypeId::kU8);
ASMJIT_DEFINE_TYPE_ID(TypeId::Int16     , TypeId::kI16);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt16    , TypeId::kU16);
ASMJIT_DEFINE_TYPE_ID(TypeId::Int32     , TypeId::kI32);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt32    , TypeId::kU32);
ASMJIT_DEFINE_TYPE_ID(TypeId::Int64     , TypeId::kI64);
ASMJIT_DEFINE_TYPE_ID(TypeId::UInt64    , TypeId::kU64);
ASMJIT_DEFINE_TYPE_ID(TypeId::IntPtr    , TypeId::kIntPtr);
ASMJIT_DEFINE_TYPE_ID(TypeId::UIntPtr   , TypeId::kUIntPtr);
ASMJIT_DEFINE_TYPE_ID(TypeId::Float     , TypeId::kF32);
ASMJIT_DEFINE_TYPE_ID(TypeId::Double    , TypeId::kF64);
ASMJIT_DEFINE_TYPE_ID(TypeId::MmxReg    , TypeId::kMmx64);
ASMJIT_DEFINE_TYPE_ID(TypeId::Vec128    , TypeId::kI32x4);
ASMJIT_DEFINE_TYPE_ID(TypeId::Vec256    , TypeId::kI32x8);
ASMJIT_DEFINE_TYPE_ID(TypeId::Vec512    , TypeId::kI32x16);
1561
1562
1563
1564
1565
1566

//! \}

} // asmjit namespace

// [Api-End]
1567
#include "../asmjit_apiend.h"
1568
1569
1570

// [Guard]
#endif // _ASMJIT_BASE_OPERAND_H