-
Notifications
You must be signed in to change notification settings - Fork 91
/
Copy pathLIR.h
2901 lines (2561 loc) · 103 KB
/
LIR.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
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef __nanojit_LIR__
#define __nanojit_LIR__
namespace nanojit
{
enum LOpcode
{
#define OP___(op, repKind, retType, isCse) \
LIR_##op,
#include "LIRopcode.tbl"
LIR_sentinel,
#undef OP___
#ifdef NANOJIT_64BIT
# define PTR_SIZE(a,b) b
#else
# define PTR_SIZE(a,b) a
#endif
// Pointer-sized synonyms.
LIR_paramp = PTR_SIZE(LIR_parami, LIR_paramq),
LIR_retp = PTR_SIZE(LIR_reti, LIR_retq),
LIR_livep = PTR_SIZE(LIR_livei, LIR_liveq),
LIR_ldp = PTR_SIZE(LIR_ldi, LIR_ldq),
LIR_stp = PTR_SIZE(LIR_sti, LIR_stq),
LIR_callp = PTR_SIZE(LIR_calli, LIR_callq),
LIR_eqp = PTR_SIZE(LIR_eqi, LIR_eqq),
LIR_ltp = PTR_SIZE(LIR_lti, LIR_ltq),
LIR_gtp = PTR_SIZE(LIR_gti, LIR_gtq),
LIR_lep = PTR_SIZE(LIR_lei, LIR_leq),
LIR_gep = PTR_SIZE(LIR_gei, LIR_geq),
LIR_ltup = PTR_SIZE(LIR_ltui, LIR_ltuq),
LIR_gtup = PTR_SIZE(LIR_gtui, LIR_gtuq),
LIR_leup = PTR_SIZE(LIR_leui, LIR_leuq),
LIR_geup = PTR_SIZE(LIR_geui, LIR_geuq),
LIR_addp = PTR_SIZE(LIR_addi, LIR_addq),
LIR_subp = PTR_SIZE(LIR_subi, LIR_subq),
LIR_addjovp = PTR_SIZE(LIR_addjovi, LIR_addjovq),
LIR_andp = PTR_SIZE(LIR_andi, LIR_andq),
LIR_orp = PTR_SIZE(LIR_ori, LIR_orq),
LIR_xorp = PTR_SIZE(LIR_xori, LIR_xorq),
LIR_lshp = PTR_SIZE(LIR_lshi, LIR_lshq),
LIR_rshp = PTR_SIZE(LIR_rshi, LIR_rshq),
LIR_rshup = PTR_SIZE(LIR_rshui, LIR_rshuq),
LIR_cmovp = PTR_SIZE(LIR_cmovi, LIR_cmovq)
};
// Check that all opcodes are between 0 and 255.
#define OP___(op, repKind, retType, isCse) \
NanoStaticAssert(LIR_##op >= 0 && LIR_##op < 256);
#include "LIRopcode.tbl"
#undef OP___
NanoStaticAssert(LIR_start == 0 && LIR_sentinel <= 256); // It's ok if LIR_sentinel is 256 since it's not actually used as opcode.
// 32-bit integer comparisons must be contiguous, as must 64-bit integer
// comparisons and 64-bit float comparisons.
NanoStaticAssert(LIR_eqi + 1 == LIR_lti &&
LIR_eqi + 2 == LIR_gti &&
LIR_eqi + 3 == LIR_lei &&
LIR_eqi + 4 == LIR_gei &&
LIR_eqi + 5 == LIR_ltui &&
LIR_eqi + 6 == LIR_gtui &&
LIR_eqi + 7 == LIR_leui &&
LIR_eqi + 8 == LIR_geui);
#ifdef NANOJIT_64BIT
NanoStaticAssert(LIR_eqq + 1 == LIR_ltq &&
LIR_eqq + 2 == LIR_gtq &&
LIR_eqq + 3 == LIR_leq &&
LIR_eqq + 4 == LIR_geq &&
LIR_eqq + 5 == LIR_ltuq &&
LIR_eqq + 6 == LIR_gtuq &&
LIR_eqq + 7 == LIR_leuq &&
LIR_eqq + 8 == LIR_geuq);
#endif
NanoStaticAssert(LIR_eqd + 1 == LIR_ltd &&
LIR_eqd + 2 == LIR_gtd &&
LIR_eqd + 3 == LIR_led &&
LIR_eqd + 4 == LIR_ged);
NanoStaticAssert(LIR_eqf + 1 == LIR_ltf &&
LIR_eqf + 2 == LIR_gtf &&
LIR_eqf + 3 == LIR_lef &&
LIR_eqf + 4 == LIR_gef);
// Various opcodes must be changeable to their opposite with op^1
// (although we use invertXyz() when possible, ie. outside static
// assertions).
NanoStaticAssert((LIR_jt^1) == LIR_jf && (LIR_jf^1) == LIR_jt);
NanoStaticAssert((LIR_xt^1) == LIR_xf && (LIR_xf^1) == LIR_xt);
NanoStaticAssert((LIR_lti^1) == LIR_gti && (LIR_gti^1) == LIR_lti);
NanoStaticAssert((LIR_lei^1) == LIR_gei && (LIR_gei^1) == LIR_lei);
NanoStaticAssert((LIR_ltui^1) == LIR_gtui && (LIR_gtui^1) == LIR_ltui);
NanoStaticAssert((LIR_leui^1) == LIR_geui && (LIR_geui^1) == LIR_leui);
#ifdef NANOJIT_64BIT
NanoStaticAssert((LIR_ltq^1) == LIR_gtq && (LIR_gtq^1) == LIR_ltq);
NanoStaticAssert((LIR_leq^1) == LIR_geq && (LIR_geq^1) == LIR_leq);
NanoStaticAssert((LIR_ltuq^1) == LIR_gtuq && (LIR_gtuq^1) == LIR_ltuq);
NanoStaticAssert((LIR_leuq^1) == LIR_geuq && (LIR_geuq^1) == LIR_leuq);
#endif
NanoStaticAssert((LIR_ltd^1) == LIR_gtd && (LIR_gtd^1) == LIR_ltd);
NanoStaticAssert((LIR_led^1) == LIR_ged && (LIR_ged^1) == LIR_led);
NanoStaticAssert((LIR_ltf^1) == LIR_gtf && (LIR_gtf^1) == LIR_ltf);
NanoStaticAssert((LIR_lef^1) == LIR_gef && (LIR_gef^1) == LIR_lef);
NanoStaticAssert(LIR_dotf3 == LIR_dotf4 + 1);
NanoStaticAssert(LIR_dotf2 == LIR_dotf4 + 2);
struct GuardRecord;
struct SideExit;
enum AbiKind {
ABI_FASTCALL,
ABI_THISCALL,
ABI_STDCALL,
ABI_CDECL
};
// This is much the same as LTy, but we need to distinguish signed and
// unsigned 32-bit ints so that they will be extended to 64-bits correctly
// on 64-bit platforms.
//
// All values must fit into three bits. See CallInfo for details.
enum ArgType {
ARGTYPE_V = 0, // void
ARGTYPE_I = 1, // int32_t
ARGTYPE_UI = 2, // uint32_t
#ifdef NANOJIT_64BIT
ARGTYPE_Q = 3, // uint64_t
#endif
ARGTYPE_D = 4, // double
ARGTYPE_F = 5, // single-precision float;
ARGTYPE_F4 = 6, // SIMD vector of 4 single-precision floats;
// aliases
ARGTYPE_P = PTR_SIZE(ARGTYPE_I, ARGTYPE_Q), // pointer
ARGTYPE_B = ARGTYPE_I // bool
};
enum IndirectCall {
CALL_INDIRECT = 0
};
//-----------------------------------------------------------------------
// Aliasing
// --------
// *Aliasing* occurs when a single memory location can be accessed through
// multiple names. For example, consider this code:
//
// ld a[0]
// sti b[0]
// ld a[0]
//
// In general, it's possible that a[0] and b[0] may refer to the same
// memory location. This means, for example, that you cannot safely
// perform CSE on the two loads. However, if you know that 'a' cannot be
// an alias of 'b' (ie. the two loads do not alias with the store) then
// you can safely perform CSE.
//
// Access regions
// --------------
// Doing alias analysis precisely is difficult. But it turns out that
// keeping track of aliasing at a coarse level is enough to help with many
// optimisations. So we conceptually divide the memory that is accessible
// from LIR into a small number of "access regions" (aka. "Acc"). An
// access region may be non-contiguous. No two access regions can
// overlap. The union of all access regions covers all memory accessible
// from LIR.
//
// In general a (static) load or store may be executed more than once, and
// thus may access multiple regions; however, in practice almost all
// loads and stores will obviously access only a single region. A
// function called from LIR may load and/or store multiple access regions
// (even if executed only once).
//
// If two loads/stores/calls are known to not access the same region(s),
// then they do not alias.
//
// All regions are defined by the embedding. It makes sense to add new
// embedding-specific access regions when doing so will help with one or
// more optimisations.
//
// Access region sets and instruction markings
// -------------------------------------------
// Each load/store is marked with an "access region set" (aka. "AccSet"),
// which is a set of one or more access regions. This indicates which
// parts of LIR-accessible memory the load/store may touch.
//
// Each function called from LIR is also marked with an access region set
// for memory stored to by the function. (We could also have a marking
// for memory loads done by the function, but there's no need at the
// moment.) These markings apply to the function itself, not the call
// site, ie. they're not context-sensitive.
//
// These load/store/call markings MUST BE ACCURATE -- if not then invalid
// optimisations might occur that change the meaning of the code.
// However, they can safely be imprecise (ie. conservative), ie. a
// load/store/call can be marked with an access region set that is a
// superset of the actual access region set. Such imprecision is safe but
// may reduce optimisation opportunities.
//
// Optimisations that use access region info
// -----------------------------------------
// Currently only CseFilter uses this, and only for determining whether
// loads can be CSE'd. Note that CseFilter treats loads that are marked
// with a single access region precisely, but all loads marked with
// multiple access regions get lumped together. So if you can't mark a
// load with a single access region, you might as well use ACC_LOAD_ANY.
//-----------------------------------------------------------------------
// An access region set is represented as a bitset. Using a uint32_t
// restricts us to at most 32 alias regions for the moment. This could be
// expanded to a uint64_t easily if needed.
typedef uint32_t AccSet;
static const int NUM_ACCS = sizeof(AccSet) * 8;
// Some common (non-singleton) access region sets. ACCSET_NONE does not make
// sense for loads or stores (which must access at least one region), it
// only makes sense for calls.
//
static const AccSet ACCSET_NONE = 0x0;
static const AccSet ACCSET_ALL = 0xffffffff;
static const AccSet ACCSET_LOAD_ANY = ACCSET_ALL; // synonym
static const AccSet ACCSET_STORE_ANY = ACCSET_ALL; // synonym
inline bool isSingletonAccSet(AccSet accSet) {
// This is a neat way of testing if a value has only one bit set.
return (accSet & (accSet - 1)) == 0;
}
// Full AccSets don't fit into load and store instructions. But
// load/store AccSets almost always contain a single access region. We
// take advantage of this to create a compressed AccSet, MiniAccSet, that
// does fit.
//
// The 32 single-region AccSets get compressed into a number in the range
// 0..31 (according to the position of the set bit), and all other
// (multi-region) AccSets get converted into MINI_ACCSET_MULTIPLE. So the
// representation is lossy in the latter case, but that case is rare for
// loads/stores. We use a full AccSet for the storeAccSets of calls, for
// which multi-region AccSets are common.
//
// We wrap the uint8_t inside a struct to avoid the possiblity of subtle
// bugs caused by mixing up AccSet and MiniAccSet, which is easy to do.
// However, the struct gets padded inside LInsLd in an inconsistent way on
// Windows, so we actually store a MiniAccSetVal inside LInsLd. Sigh.
// But we use MiniAccSet everywhere else.
//
typedef uint8_t MiniAccSetVal;
struct MiniAccSet { MiniAccSetVal val; };
static const MiniAccSet MINI_ACCSET_MULTIPLE = { 99 };
static MiniAccSet compressAccSet(AccSet accSet) {
if (isSingletonAccSet(accSet)) {
MiniAccSet ret = { uint8_t(msbSet32(accSet)) };
return ret;
}
// If we got here, it must be a multi-region AccSet.
return MINI_ACCSET_MULTIPLE;
}
static AccSet decompressMiniAccSet(MiniAccSet miniAccSet) {
return (miniAccSet.val == MINI_ACCSET_MULTIPLE.val) ? ACCSET_ALL : (1 << miniAccSet.val);
}
// The LoadQual affects how a load can be optimised:
//
// - CONST: These loads are guaranteed to always return the same value
// during a single execution of a fragment (but the value is allowed to
// change between executions of the fragment). This means that the
// location is never stored to by the LIR, and is never modified by an
// external entity while the fragment is running.
//
// - NORMAL: These loads may be stored to by the LIR, but are never
// modified by an external entity while the fragment is running.
//
// - VOLATILE: These loads may be stored to by the LIR, and may be
// modified by an external entity while the fragment is running.
//
// This gives a lattice with the ordering: CONST < NORMAL < VOLATILE.
// As usual, it's safe to mark a load with a value higher (less precise)
// that actual, but it may result in fewer optimisations occurring.
//
// Generally CONST loads are highly amenable to optimisation (eg. CSE),
// VOLATILE loads are entirely unoptimisable, and NORMAL loads are in
// between and require some alias analysis to optimise.
//
// Note that CONST has a stronger meaning to "const" in C and C++; in C
// and C++ a "const" variable may be modified by an external entity, such
// as hardware. Hence "const volatile" makes sense in C and C++, but
// CONST+VOLATILE doesn't make sense in LIR.
//
// Note also that a 2-bit bitfield in LInsLd is used to hold LoadQual
// values, so you can one add one more value without expanding it.
//
enum LoadQual {
LOAD_CONST = 0,
LOAD_NORMAL = 1,
LOAD_VOLATILE = 2
};
struct CallInfo
{
private:
// In CallInfo::_typesig, each entry is three bits.
static const int TYPESIG_FIELDSZB = 3;
static const int TYPESIG_FIELDMASK = 7;
template <class FILTER> uint32_t countArgs() const;
public:
uintptr_t _address;
uint32_t _typesig:27; // 9 3-bit fields indicating arg type, by ARGTYPE above (including ret type): a1 a2 a3 a4 a5 ret
AbiKind _abi:3;
uint32_t _isPure:1; // _isPure=1 means no side-effects, result only depends on args
AccSet _storeAccSet; // access regions stored by the function
verbose_only ( const char* _name; )
// The following encode 'r func()' through to 'r func(a1, a2, a3, a4, a5, a6, a7, a8)'.
static inline uint32_t typeSig0(ArgType r) {
return r;
}
static inline uint32_t typeSig1(ArgType r, ArgType a1) {
return a1 << TYPESIG_FIELDSZB*1 | typeSig0(r);
}
static inline uint32_t typeSig2(ArgType r, ArgType a1, ArgType a2) {
return a1 << TYPESIG_FIELDSZB*2 | typeSig1(r, a2);
}
static inline uint32_t typeSig3(ArgType r, ArgType a1, ArgType a2, ArgType a3) {
return a1 << TYPESIG_FIELDSZB*3 | typeSig2(r, a2, a3);
}
static inline uint32_t typeSig4(ArgType r, ArgType a1, ArgType a2, ArgType a3, ArgType a4) {
return a1 << TYPESIG_FIELDSZB*4 | typeSig3(r, a2, a3, a4);
}
static inline uint32_t typeSig5(ArgType r, ArgType a1, ArgType a2, ArgType a3,
ArgType a4, ArgType a5) {
return a1 << TYPESIG_FIELDSZB*5 | typeSig4(r, a2, a3, a4, a5);
}
static inline uint32_t typeSig6(ArgType r, ArgType a1, ArgType a2, ArgType a3,
ArgType a4, ArgType a5, ArgType a6) {
return a1 << TYPESIG_FIELDSZB*6 | typeSig5(r, a2, a3, a4, a5, a6);
}
static inline uint32_t typeSig7(ArgType r, ArgType a1, ArgType a2, ArgType a3,
ArgType a4, ArgType a5, ArgType a6, ArgType a7) {
return a1 << TYPESIG_FIELDSZB*7 | typeSig6(r, a2, a3, a4, a5, a6, a7);
}
static inline uint32_t typeSig8(ArgType r, ArgType a1, ArgType a2, ArgType a3, ArgType a4,
ArgType a5, ArgType a6, ArgType a7, ArgType a8) {
return a1 << TYPESIG_FIELDSZB*8 | typeSig7(r, a2, a3, a4, a5, a6, a7, a8);
}
// Encode 'r func(a1, ..., aN))'
static inline uint32_t typeSigN(ArgType r, int N, ArgType a[]) {
uint32_t typesig = r;
for (int i = 0; i < N; i++) {
typesig |= a[i] << TYPESIG_FIELDSZB*(N-i);
}
return typesig;
}
uint32_t count_args() const;
uint32_t count_int_args() const;
uint32_t count_float_args() const;
uint32_t count_float4_args() const;
// Nb: uses right-to-left order, eg. sizes[0] is the size of the right-most arg.
// XXX: See bug 525815 for fixing this.
uint32_t getArgTypes(ArgType* types) const;
inline ArgType returnType() const {
return ArgType(_typesig & TYPESIG_FIELDMASK);
}
inline bool isIndirect() const {
return _address < 256;
}
};
// Array holding the 'isCse' field from LIRopcode.tbl.
extern const int8_t isCses[]; // cannot be uint8_t, some values are negative
inline bool isCseOpcode(LOpcode op) {
NanoAssert(isCses[op] != -1); // see LIRopcode.tbl to understand this
return isCses[op] == 1;
}
inline bool isLiveOpcode(LOpcode op) {
return
#if defined NANOJIT_64BIT
op == LIR_liveq ||
#endif
op == LIR_livef || op == LIR_livef4 ||
op == LIR_livei || op == LIR_lived;
}
inline bool isRetOpcode(LOpcode op) {
return
#if defined NANOJIT_64BIT
op == LIR_retq ||
#endif
op == LIR_retf || op == LIR_retf4 ||
op == LIR_reti || op == LIR_retd;
}
inline bool isCmovOpcode(LOpcode op) {
return
#if defined NANOJIT_64BIT
op == LIR_cmovq ||
#endif
op == LIR_cmovf ||
op == LIR_cmovf4||
op == LIR_cmovi ||
op == LIR_cmovd;
}
inline bool isCmpIOpcode(LOpcode op) {
return LIR_eqi <= op && op <= LIR_geui;
}
inline bool isCmpSIOpcode(LOpcode op) {
return LIR_eqi <= op && op <= LIR_gei;
}
inline bool isCmpUIOpcode(LOpcode op) {
return LIR_eqi == op || (LIR_ltui <= op && op <= LIR_geui);
}
#ifdef NANOJIT_64BIT
inline bool isCmpQOpcode(LOpcode op) {
return LIR_eqq <= op && op <= LIR_geuq;
}
inline bool isCmpSQOpcode(LOpcode op) {
return LIR_eqq <= op && op <= LIR_geq;
}
inline bool isCmpUQOpcode(LOpcode op) {
return LIR_eqq == op || (LIR_ltuq <= op && op <= LIR_geuq);
}
#endif
inline bool isCmpDOpcode(LOpcode op) {
return LIR_eqd <= op && op <= LIR_ged;
}
inline bool isCmpFOpcode(LOpcode op) {
return LIR_eqf <= op && op <= LIR_gef;
}
inline bool isCmpF4Opcode(LOpcode op) {
return LIR_eqf4 == op;
}
inline bool isCmpOpcode(LOpcode op) {
return isCmpIOpcode(op) ||
#if defined NANOJIT_64BIT
isCmpQOpcode(op) ||
#endif
isCmpFOpcode(op) ||
isCmpF4Opcode(op)||
isCmpDOpcode(op);
}
inline LOpcode getCmpFOpcode(LOpcode op){
NanoAssert(isCmpDOpcode(op));
return LOpcode(op+6);
}
inline LOpcode getCmpDOpcode(LOpcode op){
NanoAssert(isCmpFOpcode(op));
return LOpcode(op-6);
}
inline LOpcode invertCondJmpOpcode(LOpcode op) {
NanoAssert(op == LIR_jt || op == LIR_jf);
return LOpcode(op ^ 1);
}
inline LOpcode invertCondGuardOpcode(LOpcode op) {
NanoAssert(op == LIR_xt || op == LIR_xf);
return LOpcode(op ^ 1);
}
inline LOpcode invertCmpOpcode(LOpcode op) {
NanoAssert(isCmpOpcode(op));
return LOpcode(op ^ 1);
}
inline LOpcode getCallOpcode(const CallInfo* ci) {
LOpcode op = LIR_callp;
switch (ci->returnType()) {
case ARGTYPE_V: op = LIR_callv; break;
case ARGTYPE_I:
case ARGTYPE_UI: op = LIR_calli; break;
#ifdef NANOJIT_64BIT
case ARGTYPE_Q: op = LIR_callq; break;
#endif
case ARGTYPE_F: op = LIR_callf; break;
case ARGTYPE_F4: op = LIR_callf4;break;
case ARGTYPE_D: op = LIR_calld; break;
default: NanoAssert(0); break;
}
return op;
}
LOpcode arithOpcodeD2I(LOpcode op);
#ifdef NANOJIT_64BIT
LOpcode cmpOpcodeI2Q(LOpcode op);
#endif
LOpcode cmpOpcodeD2I(LOpcode op);
LOpcode cmpOpcodeD2UI(LOpcode op);
// Array holding the 'repKind' field from LIRopcode.tbl.
extern const uint8_t repKinds[];
enum LTy {
LTy_V, // void: no value/no type
LTy_I, // int: 32-bit integer
#ifdef NANOJIT_64BIT
LTy_Q, // quad: 64-bit integer
#endif
LTy_D, // double: 64-bit float
LTy_F, // float: 32-bit float
LTy_F4, // float4: 128bit, four 32-bit floats
LTy_P = PTR_SIZE(LTy_I, LTy_Q) // word-sized integer
};
// Array holding the 'retType' field from LIRopcode.tbl.
extern const LTy retTypes[];
// Array holding the size in bytes of each LIns from LIRopcode.tbl.
extern const uint8_t insSizes[];
//-----------------------------------------------------------------------
// Low-level instructions. This is a bit complicated, because we have a
// variable-width representation to minimise space usage.
//
// - Instruction size is always an integral multiple of word size.
//
// - Every instruction has at least one word, holding the opcode and the
// reservation info ("SharedFields"). That word is in class LIns.
//
// - Beyond that, most instructions have 1, 2 or 3 extra words. These
// extra words are in classes LInsOp1, LInsOp2, etc (collectively called
// "LInsXYZ" in what follows). Each LInsXYZ class also contains an LIns,
// accessible by the 'ins' member, which holds the LIns data.
//
// - LIR is written forward, but read backwards. When reading backwards,
// in order to find the opcode, it must be in a predictable place in the
// LInsXYZ isn't affected by instruction width. Therefore, the LIns
// word (which contains the opcode) is always the *last* word in an
// instruction.
//
// - Each instruction is created by casting pre-allocated bytes from a
// LirBuffer to the LInsXYZ type. Therefore there are no constructors
// for LIns or LInsXYZ.
//
// - The standard handle for an instruction is a LIns*. This actually
// points to the LIns word, ie. to the final word in the instruction.
// This is a bit odd, but it allows the instruction's opcode to be
// easily accessed. Once you've looked at the opcode and know what kind
// of instruction it is, if you want to access any of the other words,
// you need to use toLInsXYZ(), which takes the LIns* and gives you an
// LInsXYZ*, ie. the pointer to the actual start of the instruction's
// bytes. From there you can access the instruction-specific extra
// words.
//
// - However, from outside class LIns, LInsXYZ isn't visible, nor is
// toLInsXYZ() -- from outside LIns, all LIR instructions are handled
// via LIns pointers and get/set methods are used for all LIns/LInsXYZ
// accesses. In fact, all data members in LInsXYZ are private and can
// only be accessed by LIns, which is a friend class. The only thing
// anyone outside LIns can do with a LInsXYZ is call getLIns().
//
// - An example Op2 instruction and the likely pointers to it (each line
// represents a word, and pointers to a line point to the start of the
// word on that line):
//
// [ oprnd_2 <-- LInsOp2* insOp2 == toLInsOp2(ins)
// oprnd_1
// opcode + resv ] <-- LIns* ins
//
// - LIR_skip instructions are used to link code chunks. If the first
// instruction on a chunk isn't a LIR_start, it will be a skip, and the
// skip's operand will point to the last LIns on the preceding chunk.
// LInsSk has the same layout as LInsOp1, but we represent it as a
// different class because there are some places where we treat
// skips specially and so having it separate seems like a good idea.
//
// - Various things about the size and layout of LIns and LInsXYZ are
// statically checked in staticSanityCheck(). In particular, this is
// worthwhile because there's nothing that guarantees that all the
// LInsXYZ classes have a size that is a multiple of word size (but in
// practice all sane compilers use a layout that results in this). We
// also check that every LInsXYZ is word-aligned in
// LirBuffer::makeRoom(); this seems sensible to avoid potential
// slowdowns due to misalignment. It relies on chunks themselves being
// word-aligned, which is extremely likely.
//
// - There is an enum, LInsRepKind, with one member for each of the
// LInsXYZ kinds. Each opcode is categorised with its LInsRepKind value
// in LIRopcode.tbl, and this is used in various places.
//-----------------------------------------------------------------------
enum LInsRepKind {
// LRK_XYZ corresponds to class LInsXYZ.
LRK_Op0,
LRK_Op1,
LRK_Op1b,
LRK_Op2,
LRK_Op3,
LRK_Op4,
LRK_Ld,
LRK_St,
LRK_Sk,
LRK_C,
LRK_P,
LRK_IorF,
LRK_QorD,
LRK_F4,
LRK_Jtbl,
LRK_Safe,
LRK_None // this one is used for unused opcode numbers
};
class LInsOp0;
class LInsOp1;
class LInsOp1b;
class LInsOp2;
class LInsOp3;
class LInsOp4;
class LInsLd;
class LInsSt;
class LInsSk;
class LInsC;
class LInsP;
class LInsIorF;
class LInsQorD;
class LInsJtbl;
class LInsF4;
class LInsSafe;
class LIns
{
private:
// SharedFields: fields shared by all LIns kinds.
//
// The .inReg, .regnum, .inAr and .arIndex fields form a "reservation"
// that is used temporarily during assembly to record information
// relating to register allocation. See class RegAlloc for more
// details. Note: all combinations of .inReg/.inAr are possible, ie.
// 0/0, 0/1, 1/0, 1/1.
//
// The .isResultLive field is only used for instructions that return
// results. It indicates if the result is live. It's set (if
// appropriate) and used only during the codegen pass.
//
// For literals (immX family), we record whether a value is 'tainted', that
// is, potentially controllable by an untrusted agent in a manner deemed to
// be exploitable, e.g., by a JIT-spray attack. The taint bit is propagated
// through all machine-independent optimizations, allowing the back end to
// obfuscate ("blind") tainted values that would be inserted in the instruction
// stream. Nanojit doesn't really know anything about which values are
// tainted, and relies on the generator of the LIR code to set the the taint
// status of each literal correctly when generating a LIR_immX instruction.
//
// At present, the maximum mumber of stack frame slots is 8k (on SPARC,
// 4k on most platforms), so 13 bits should be enough for the arIndex.
// Since we don't support SPARC anymore, we could steal yet another bit.
struct SharedFields {
uint32_t inReg:1; // if 1, 'reg' is active
uint32_t regnum:7;
uint32_t inAr:1; // if 1, 'arIndex' is active
uint32_t isResultLive:1; // if 1, the instruction's result is live
uint32_t isTainted:1; // if 1, immX constant value is user-controlled
uint32_t arIndex:13; // index into stack frame; displ is -4*arIndex
uint32_t opcode:8; // instruction's opcode; actually a LOpcode - but since
// there is no reliable way to enforce an enum's
// underlying type to be unsigned on all compilers, we
// store it explicitly as uint8_t rather than LOpcode:8
};
union {
SharedFields sharedFields;
// Force sizeof(LIns)==8 and 8-byte alignment on 64-bit machines.
// This is necessary because sizeof(SharedFields)==4 and we want all
// instances of LIns to be pointer-aligned.
void* wholeWord;
};
inline void initSharedFields(LOpcode opcode)
{
NanoAssert(((int)opcode)>=0 && opcode<=255);
// We must zero .inReg, .inAR and .isResultLive, but zeroing the
// whole word is easier. Then we set the opcode.
wholeWord = 0;
sharedFields.opcode = (uint8_t)opcode;
}
// LIns-to-LInsXYZ converters.
inline LInsOp0* toLInsOp0() const;
inline LInsOp1* toLInsOp1() const;
inline LInsOp1b* toLInsOp1b() const;
inline LInsOp2* toLInsOp2() const;
inline LInsOp3* toLInsOp3() const;
inline LInsOp4* toLInsOp4() const;
inline LInsLd* toLInsLd() const;
inline LInsSt* toLInsSt() const;
inline LInsSk* toLInsSk() const;
inline LInsC* toLInsC() const;
inline LInsP* toLInsP() const;
inline LInsIorF* toLInsIorF() const;
inline LInsQorD* toLInsQorD() const;
inline LInsJtbl* toLInsJtbl() const;
inline LInsF4* toLInsF4() const;
inline LInsSafe* toLInsSafe() const;
void staticSanityCheck();
public:
// LIns initializers.
inline void initLInsOp0(LOpcode opcode);
inline void initLInsOp1(LOpcode opcode, LIns* oprnd1);
inline void initLInsOp1b(LOpcode opcode, LIns* oprnd1, uint8_t mask);
inline void initLInsOp2(LOpcode opcode, LIns* oprnd1, LIns* oprnd2);
inline void initLInsOp3(LOpcode opcode, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3);
inline void initLInsOp4(LOpcode opcode, LIns* oprnd1, LIns* oprnd2, LIns* oprnd3, LIns* oprnd4);
inline void initLInsLd(LOpcode opcode, LIns* val, int32_t d, AccSet accSet, LoadQual loadQual);
inline void initLInsSt(LOpcode opcode, LIns* val, LIns* base, int32_t d, AccSet accSet);
inline void initLInsSk(LIns* prevLIns);
// Nb: args[] must be allocated and initialised before being passed in;
// initLInsC() just copies the pointer into the LInsC.
inline void initLInsC(LOpcode opcode, LIns** args, const CallInfo* ci);
inline void initLInsP(int32_t arg, int32_t kind);
inline void initLInsIorF(LOpcode opcode, int32_t immIorF);
inline void initLInsQorD(LOpcode opcode, uint64_t immQorD);
inline void initLInsJtbl(LIns* index, uint32_t size, LIns** table);
inline void initLInsF4(LOpcode opcode, const float4_t& immF4);
inline void initLInsSafe(LOpcode opcode, void* payload);
LOpcode opcode() const {
NanoAssert(sharedFields.opcode< LIR_sentinel);
return (LOpcode) sharedFields.opcode;
}
// Generally, void instructions (statements) are always live and
// non-void instructions (expressions) are live if used by another
// live instruction. But there are some trickier cases.
// Any non-void instruction can be marked isResultLive=1 even
// when it is unreachable, e.g. due to an always-taken branch.
// The assembler marks it live if it sees any uses, regardless of
// whether those uses are in reachable code or not.
bool isLive() const {
return isV() ||
sharedFields.isResultLive ||
(isCall() && !callInfo()->_isPure) || // impure calls are always live
isop(LIR_paramp); // LIR_paramp is always live
}
void setResultLive() {
NanoAssert(!isV());
sharedFields.isResultLive = 1;
}
// XXX: old reservation manipulating functions. See bug 538924.
// Replacement strategy:
// - deprecated_markAsClear() --> clearReg() and/or clearArIndex()
// - deprecated_hasKnownReg() --> isInReg()
// - deprecated_getReg() --> getReg() after checking isInReg()
//
void deprecated_markAsClear() {
sharedFields.inReg = 0;
sharedFields.inAr = 0;
}
bool deprecated_hasKnownReg() {
NanoAssert(isExtant());
return isInReg();
}
Register deprecated_getReg() {
NanoAssert(isExtant());
if (isInReg()) {
Register r = { sharedFields.regnum };
return r;
} else {
return deprecated_UnknownReg;
}
}
uint32_t deprecated_getArIndex() {
NanoAssert(isExtant());
return ( isInAr() ? sharedFields.arIndex : 0 );
}
// Reservation manipulation.
//
// "Extant" mean "in existence, still existing, surviving". In other
// words, has the value been computed explicitly (not folded into
// something else) and is it still available (in a register or spill
// slot) for use?
bool isExtant() {
return isInReg() || isInAr();
}
bool isInReg() {
return sharedFields.inReg;
}
bool isInRegMask(RegisterMask allow) {
return isInReg() && (rmask(getReg()) & allow);
}
Register getReg() {
NanoAssert(isInReg());
Register r = { sharedFields.regnum };
return r;
}
void setReg(Register r) {
sharedFields.inReg = 1;
sharedFields.regnum = REGNUM(r);
}
void clearReg() {
sharedFields.inReg = 0;
}
bool isInAr() {
return sharedFields.inAr;
}
uint32_t getArIndex() {
NanoAssert(isInAr());
return sharedFields.arIndex;
}
void setArIndex(uint32_t arIndex) {
sharedFields.inAr = 1;
sharedFields.arIndex = arIndex;
}
void clearArIndex() {
sharedFields.inAr = 0;
}
// Constant blinding.
// The flag is ignored on non-immediate instructions,
// as well as some immediate instructions that do not
// traffic in user-controlled constants.
bool isTainted() const {
return sharedFields.isTainted;
}
void setTainted(bool tainted) {
sharedFields.isTainted = tainted;
}
// For various instruction kinds.
inline LIns* oprnd1() const;
inline LIns* oprnd2() const;
inline LIns* oprnd3() const;
inline LIns* oprnd4() const;
// For branches.
inline LIns* getTarget() const;
inline void setTarget(LIns* label);
// For guards.
inline GuardRecord* record() const;
// For loads.
inline LoadQual loadQual() const;
// For loads/stores.
inline int32_t disp() const;
inline MiniAccSet miniAccSet() const;
inline AccSet accSet() const;
// For LInsSk.
inline LIns* prevLIns() const;
// For LInsP.
inline uint8_t paramArg() const;
inline uint8_t paramKind() const;
// For LInsIorF.
inline int32_t immI() const;
inline float immF() const;
inline int32_t immFasI() const;
// For safepoints.
inline void* safePayload() const;
// For LInsQorD.
#ifdef NANOJIT_64BIT
inline int32_t immQlo() const;
inline uint64_t immQ() const;
#endif
inline int32_t immDlo() const;
inline int32_t immDhi() const;
inline double immD() const;
inline uint64_t immDasQ() const;
// For LInsF4.
inline float4_t immF4() const;
// For LIR_allocp.
inline int32_t size() const;
inline void setSize(int32_t nbytes);
// For LInsC.
inline LIns* arg(uint32_t i) const; // right-to-left-order: arg(0) is rightmost
inline uint32_t argc() const;
inline LIns* callArgN(uint32_t n) const;
inline const CallInfo* callInfo() const;
// For LIR_jtbl
inline uint32_t getTableSize() const;
inline LIns* getTarget(uint32_t index) const;
inline void setTarget(uint32_t index, LIns* label) const;
// For LInsOp1b.
inline uint8_t mask() const;
// isLInsXYZ() returns true if the instruction has the LInsXYZ form.
// Note that there is some overlap with other predicates, eg.
// isStore()==isLInsSt(), isCall()==isLInsC(), but that's ok; these
// ones are used mostly to check that opcodes are appropriate for
// instruction layouts, the others are used for non-debugging
// purposes.
bool isLInsOp0() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Op0 == repKinds[opcode()];
}
bool isLInsOp1() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Op1 == repKinds[opcode()];
}
bool isLInsOp1b() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Op1b == repKinds[opcode()];
}
bool isLInsOp2() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Op2 == repKinds[opcode()];
}
bool isLInsOp3() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Op3 == repKinds[opcode()];
}
bool isLInsOp4() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Op4 == repKinds[opcode()];
}
bool isLInsLd() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Ld == repKinds[opcode()];
}
bool isLInsSt() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_St == repKinds[opcode()];
}
bool isLInsSk() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Sk == repKinds[opcode()];
}
bool isLInsC() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_C == repKinds[opcode()];
}
bool isLInsP() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_P == repKinds[opcode()];
}
bool isLInsIorF() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_IorF == repKinds[opcode()];
}
bool isLInsSafe() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Safe == repKinds[opcode()];
}
bool isLInsQorD() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_QorD == repKinds[opcode()];
}
bool isLInsJtbl() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_Jtbl == repKinds[opcode()];
}
bool isLInsF4() const {
NanoAssert(LRK_None != repKinds[opcode()]);
return LRK_F4 == repKinds[opcode()];
}
// LIns predicates.
bool isop(LOpcode o) const {
return opcode() == o;
}
bool isRet() const {
return isRetOpcode(opcode());
}
bool isCmp() const {
return isCmpOpcode(opcode());
}