forked from asmjit/asmjit
-
Notifications
You must be signed in to change notification settings - Fork 0
/
operand.h
1494 lines (1273 loc) · 65.8 KB
/
operand.h
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
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
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
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
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
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
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
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
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
856
857
858
859
860
861
862
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
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
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
996
997
998
999
1000
// [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
// [Dependencies]
#include "../base/utils.h"
// [Api-Begin]
#include "../asmjit_apibegin.h"
namespace asmjit {
//! \addtogroup asmjit_base
//! \{
// ============================================================================
// [asmjit::Operand_]
// ============================================================================
//! 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]
// --------------------------------------------------------------------------
//! 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.
};
// --------------------------------------------------------------------------
// [Operand Signature (Bits)]
// --------------------------------------------------------------------------
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).
// |........|........|..X.....|........|
kSignatureMemAbsoluteShift = 13,
kSignatureMemAbsoluteBits = 0x01U,
kSignatureMemAbsoluteFlag = kSignatureMemAbsoluteBits << kSignatureMemAbsoluteShift,
// This memory operand represents a function argument's stack location (CodeCompiler)
// |........|........|.X......|........|
kSignatureMemArgHomeShift = 14,
kSignatureMemArgHomeBits = 0x01U,
kSignatureMemArgHomeFlag = kSignatureMemArgHomeBits << kSignatureMemArgHomeShift,
// This memory operand represents a virtual register's home-slot (CodeCompiler).
// |........|........|X.......|........|
kSignatureMemRegHomeShift = 15,
kSignatureMemRegHomeBits = 0x01U,
kSignatureMemRegHomeFlag = kSignatureMemRegHomeBits << kSignatureMemRegHomeShift
};
// --------------------------------------------------------------------------
// [Operand Id]
// --------------------------------------------------------------------------
//! 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
};
// --------------------------------------------------------------------------
// [Operand Utilities]
// --------------------------------------------------------------------------
//! 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; }
// --------------------------------------------------------------------------
// [Operand Data]
// --------------------------------------------------------------------------
//! 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
};
//! 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
};
//! 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`.
// [BASE + OFF32] vs just [OFF64].
union {
uint64_t offset64; //!< 64-bit offset, combining low and high 32-bit parts.
struct {
#if ASMJIT_ARCH_LE
uint32_t offsetLo32; //!< 32-bit low offset part.
uint32_t base; //!< 32-bit high offset part or BASE.
#else
uint32_t base; //!< 32-bit high offset part or BASE.
uint32_t offsetLo32; //!< 32-bit low offset part.
#endif
};
};
};
//! 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.
};
//! 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
};
// --------------------------------------------------------------------------
// [Init & Copy]
// --------------------------------------------------------------------------
//! \internal
//!
//! Initialize the operand to `other` (used by constructors).
ASMJIT_INLINE void _init(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); }
//! \internal
ASMJIT_INLINE void _initReg(uint32_t signature, uint32_t rd) {
_init_packed_d0_d1(signature, rd);
_init_packed_d2_d3(0, 0);
}
//! \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); }
//! \internal
//!
//! Initialize the operand from `other` (used by operator overloads).
ASMJIT_INLINE void copyFrom(const Operand_& other) noexcept { ::memcpy(this, &other, sizeof(Operand_)); }
// --------------------------------------------------------------------------
// [Accessors]
// --------------------------------------------------------------------------
//! Get if the operand matches the given signature `sign`.
ASMJIT_INLINE bool hasSignature(uint32_t signature) const noexcept { return _signature == signature; }
//! Get if the operand matches a signature of the `other` operand.
ASMJIT_INLINE bool hasSignature(const Operand_& other) const noexcept {
return _signature == other.getSignature();
}
//! 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; }
//! 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; }
ASMJIT_INLINE bool _hasSignatureData(uint32_t bits) const noexcept {
return (_signature & bits) != 0;
}
//! \internal
//!
//! Unpacks information from operand's signature.
ASMJIT_INLINE uint32_t _getSignatureData(uint32_t bits, uint32_t shift) const noexcept {
return (_signature >> shift) & bits;
}
//! \internal
//!
//! 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; }
//! 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; }
//! 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]) ;
}
//! 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;
}
//! 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;
}
//! 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);
}
//! 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);
//!
//! 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);
}
// --------------------------------------------------------------------------
// [Operator Overload]
// --------------------------------------------------------------------------
template<typename T>
ASMJIT_INLINE bool operator==(const T& other) const noexcept { return isEqual(other); }
template<typename T>
ASMJIT_INLINE bool operator!=(const T& other) const noexcept { return !isEqual(other); }
// --------------------------------------------------------------------------
// [Members]
// --------------------------------------------------------------------------
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.
};
};
// ============================================================================
// [asmjit::Operand]
// ============================================================================
//! Operand can contain register, memory location, immediate, or label.
class Operand : public Operand_ {
public:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! 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 {}
// --------------------------------------------------------------------------
// [Clone]
// --------------------------------------------------------------------------
//! Clone the `Operand`.
ASMJIT_INLINE Operand clone() const noexcept { return Operand(*this); }
ASMJIT_INLINE Operand& operator=(const Operand_& other) noexcept { copyFrom(other); return *this; }
};
// ============================================================================
// [asmjit::Label]
// ============================================================================
//! 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
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! 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);
}
explicit ASMJIT_INLINE Label(const _NoInit&) noexcept : Operand(NoInit) {}
// --------------------------------------------------------------------------
// [Reset]
// --------------------------------------------------------------------------
// 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);
}
// --------------------------------------------------------------------------
// [Label Specific]
// --------------------------------------------------------------------------
//! 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; }
};
// ============================================================================
// [asmjit::Reg]
// ============================================================================
#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;
}
ASMJIT_INLINE uint32_t getOp() const noexcept {
return (_signature >> Operand::kSignatureOpShift) & Operand::kSignatureOpBits;
}
ASMJIT_INLINE uint32_t getType() const noexcept {
return (_signature >> Operand::kSignatureRegTypeShift) & Operand::kSignatureRegTypeBits;
}
ASMJIT_INLINE uint32_t getKind() const noexcept {
return (_signature >> Operand::kSignatureRegKindShift) & Operand::kSignatureRegKindBits;
}
ASMJIT_INLINE uint32_t getSize() const noexcept {
return (_signature >> Operand::kSignatureSizeShift) & Operand::kSignatureSizeBits;
}
uint32_t _signature;
};
//! 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.
};
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! 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];
}
//! 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);
}
explicit ASMJIT_INLINE Reg(const _NoInit&) noexcept : Operand(NoInit) {}
//! 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); }
// --------------------------------------------------------------------------
// [Reg Specific]
// --------------------------------------------------------------------------
//! 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); }
//! 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] == _packed[1]; }
//! 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()); }
//! 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; }
//! 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;
}
// --------------------------------------------------------------------------
// [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;
}
//! 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;
}
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::Mem]
// ============================================================================
//! Base class for all memory operands.
//!
//! 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:
// --------------------------------------------------------------------------
// [Construction / Destruction]
// --------------------------------------------------------------------------
//! 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) {}
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) {}
// --------------------------------------------------------------------------
// [Mem Specific]
// --------------------------------------------------------------------------
//! Clone `Mem` operand.
ASMJIT_INLINE Mem clone() const noexcept { return Mem(*this); }
//! 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);
}
ASMJIT_INLINE bool isAbs() const noexcept { return _hasSignatureData(kSignatureMemAbsoluteFlag); }
ASMJIT_INLINE bool isArgHome() const noexcept { return _hasSignatureData(kSignatureMemArgHomeFlag); }
ASMJIT_INLINE bool isRegHome() const noexcept { return _hasSignatureData(kSignatureMemRegHomeFlag); }
ASMJIT_INLINE void setAbs() noexcept { _signature |= kSignatureMemAbsoluteFlag; }
ASMJIT_INLINE void setArgHome() noexcept { _signature |= kSignatureMemArgHomeFlag; }
ASMJIT_INLINE void setRegHome() noexcept { _signature |= kSignatureMemRegHomeFlag; }
ASMJIT_INLINE void clearAbs() noexcept { _signature &= ~kSignatureMemAbsoluteFlag; }
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.
ASMJIT_INLINE bool hasBaseReg() const noexcept {
// Registers start after kLabelTag.
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.
ASMJIT_INLINE bool hasIndexReg() const noexcept {
// Registers start after kLabelTag.
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; }
ASMJIT_INLINE void _setBase(uint32_t rType, uint32_t rId) noexcept {
_setSignatureData(rType, kSignatureMemBaseTypeBits, kSignatureMemBaseTypeShift);
_mem.base = rId;
}
ASMJIT_INLINE void _setIndex(uint32_t rType, uint32_t rId) noexcept {
_setSignatureData(rType, kSignatureMemIndexTypeBits, kSignatureMemIndexTypeShift);
_mem.index = rId;
}
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); }
//! Set memory operand size.
ASMJIT_INLINE void setSize(uint32_t size) noexcept {
_setSignatureData(size, kSignatureSizeBits, kSignatureSizeShift);
}
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;
}
//! 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.
}
//! 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 blindly without checking it.
ASMJIT_INLINE int32_t getOffsetHi32() const noexcept { return static_cast<int32_t>(_mem.base); }
//! 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);
}
//! 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);
}
//! Reset the memory offset to zero.
ASMJIT_INLINE void resetOffset() noexcept { setOffset(0); }
//! 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