summaryrefslogtreecommitdiff
path: root/deps/v8/src/codegen/arm64/register-arm64.h
blob: 2bdf0ceea03cda0b8540825cd3641a47229ce114 (plain)
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
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_CODEGEN_ARM64_REGISTER_ARM64_H_
#define V8_CODEGEN_ARM64_REGISTER_ARM64_H_

#include "src/codegen/arm64/utils-arm64.h"
#include "src/codegen/register.h"
#include "src/codegen/reglist.h"
#include "src/common/globals.h"

namespace v8 {
namespace internal {

// -----------------------------------------------------------------------------
// Registers.
// clang-format off
#define GENERAL_REGISTER_CODE_LIST(R)                     \
  R(0)  R(1)  R(2)  R(3)  R(4)  R(5)  R(6)  R(7)          \
  R(8)  R(9)  R(10) R(11) R(12) R(13) R(14) R(15)         \
  R(16) R(17) R(18) R(19) R(20) R(21) R(22) R(23)         \
  R(24) R(25) R(26) R(27) R(28) R(29) R(30) R(31)

#define GENERAL_REGISTERS(R)                              \
  R(x0)  R(x1)  R(x2)  R(x3)  R(x4)  R(x5)  R(x6)  R(x7)  \
  R(x8)  R(x9)  R(x10) R(x11) R(x12) R(x13) R(x14) R(x15) \
  R(x16) R(x17) R(x18) R(x19) R(x20) R(x21) R(x22) R(x23) \
  R(x24) R(x25) R(x26) R(x27) R(x28) R(x29) R(x30) R(x31)

// x18 is the platform register and is reserved for the use of platform ABIs.
// It is known to be reserved by the OS at least on Windows and iOS.
#define ALLOCATABLE_GENERAL_REGISTERS(R)                  \
  R(x0)  R(x1)  R(x2)  R(x3)  R(x4)  R(x5)  R(x6)  R(x7)  \
  R(x8)  R(x9)  R(x10) R(x11) R(x12) R(x13) R(x14) R(x15) \
         R(x19) R(x20) R(x21) R(x22) R(x23) R(x24) R(x25) \
  R(x27) R(x28)

#define FLOAT_REGISTERS(V)                                \
  V(s0)  V(s1)  V(s2)  V(s3)  V(s4)  V(s5)  V(s6)  V(s7)  \
  V(s8)  V(s9)  V(s10) V(s11) V(s12) V(s13) V(s14) V(s15) \
  V(s16) V(s17) V(s18) V(s19) V(s20) V(s21) V(s22) V(s23) \
  V(s24) V(s25) V(s26) V(s27) V(s28) V(s29) V(s30) V(s31)

#define DOUBLE_REGISTERS(R)                               \
  R(d0)  R(d1)  R(d2)  R(d3)  R(d4)  R(d5)  R(d6)  R(d7)  \
  R(d8)  R(d9)  R(d10) R(d11) R(d12) R(d13) R(d14) R(d15) \
  R(d16) R(d17) R(d18) R(d19) R(d20) R(d21) R(d22) R(d23) \
  R(d24) R(d25) R(d26) R(d27) R(d28) R(d29) R(d30) R(d31)

#define SIMD128_REGISTERS(V)                              \
  V(q0)  V(q1)  V(q2)  V(q3)  V(q4)  V(q5)  V(q6)  V(q7)  \
  V(q8)  V(q9)  V(q10) V(q11) V(q12) V(q13) V(q14) V(q15) \
  V(q16) V(q17) V(q18) V(q19) V(q20) V(q21) V(q22) V(q23) \
  V(q24) V(q25) V(q26) V(q27) V(q28) V(q29) V(q30) V(q31)

#define VECTOR_REGISTERS(V)                               \
  V(v0)  V(v1)  V(v2)  V(v3)  V(v4)  V(v5)  V(v6)  V(v7)  \
  V(v8)  V(v9)  V(v10) V(v11) V(v12) V(v13) V(v14) V(v15) \
  V(v16) V(v17) V(v18) V(v19) V(v20) V(v21) V(v22) V(v23) \
  V(v24) V(v25) V(v26) V(v27) V(v28) V(v29) V(v30) V(v31)

// Register d29 could be allocated, but we keep an even length list here, in
// order to make stack alignment easier for save and restore.
#define ALLOCATABLE_DOUBLE_REGISTERS(R)                   \
  R(d0)  R(d1)  R(d2)  R(d3)  R(d4)  R(d5)  R(d6)  R(d7)  \
  R(d8)  R(d9)  R(d10) R(d11) R(d12) R(d13) R(d14) R(d16) \
  R(d17) R(d18) R(d19) R(d20) R(d21) R(d22) R(d23) R(d24) \
  R(d25) R(d26) R(d27) R(d28)
// clang-format on

constexpr int kRegListSizeInBits = sizeof(RegList) * kBitsPerByte;

const int kNumRegs = kNumberOfRegisters;
// Registers x0-x17 are caller-saved.
const int kNumJSCallerSaved = 18;
const RegList kJSCallerSaved = 0x3ffff;

// Number of registers for which space is reserved in safepoints. Must be a
// multiple of eight.
// TODO(all): Refine this number.
const int kNumSafepointRegisters = 32;

// Define the list of registers actually saved at safepoints.
// Note that the number of saved registers may be smaller than the reserved
// space, i.e. kNumSafepointSavedRegisters <= kNumSafepointRegisters.
#define kSafepointSavedRegisters CPURegList::GetSafepointSavedRegisters().list()
#define kNumSafepointSavedRegisters \
  CPURegList::GetSafepointSavedRegisters().Count()

// Some CPURegister methods can return Register and VRegister types, so we
// need to declare them in advance.
class Register;
class VRegister;

enum RegisterCode {
#define REGISTER_CODE(R) kRegCode_##R,
  GENERAL_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
      kRegAfterLast
};

class CPURegister : public RegisterBase<CPURegister, kRegAfterLast> {
 public:
  enum RegisterType { kRegister, kVRegister, kNoRegister };

  static constexpr CPURegister no_reg() {
    return CPURegister{kCode_no_reg, 0, kNoRegister};
  }

  template <int code, int size, RegisterType type>
  static constexpr CPURegister Create() {
    static_assert(IsValid(code, size, type), "Cannot create invalid registers");
    return CPURegister{code, size, type};
  }

  static CPURegister Create(int code, int size, RegisterType type) {
    DCHECK(IsValid(code, size, type));
    return CPURegister{code, size, type};
  }

  RegisterType type() const { return reg_type_; }
  int SizeInBits() const {
    DCHECK(IsValid());
    return reg_size_;
  }
  int SizeInBytes() const {
    DCHECK(IsValid());
    DCHECK_EQ(SizeInBits() % 8, 0);
    return reg_size_ / 8;
  }
  bool Is8Bits() const {
    DCHECK(IsValid());
    return reg_size_ == 8;
  }
  bool Is16Bits() const {
    DCHECK(IsValid());
    return reg_size_ == 16;
  }
  bool Is32Bits() const {
    DCHECK(IsValid());
    return reg_size_ == 32;
  }
  bool Is64Bits() const {
    DCHECK(IsValid());
    return reg_size_ == 64;
  }
  bool Is128Bits() const {
    DCHECK(IsValid());
    return reg_size_ == 128;
  }
  bool IsValid() const { return reg_type_ != kNoRegister; }
  bool IsNone() const { return reg_type_ == kNoRegister; }
  constexpr bool Is(const CPURegister& other) const {
    return Aliases(other) && (reg_size_ == other.reg_size_);
  }
  constexpr bool Aliases(const CPURegister& other) const {
    return (reg_code_ == other.reg_code_) && (reg_type_ == other.reg_type_);
  }

  constexpr bool operator==(const CPURegister& other) const {
    return Is(other);
  }

  constexpr bool operator!=(const CPURegister& other) const {
    return !(*this == other);
  }

  bool IsZero() const;
  bool IsSP() const;

  bool IsRegister() const { return reg_type_ == kRegister; }
  bool IsVRegister() const { return reg_type_ == kVRegister; }

  bool IsFPRegister() const { return IsS() || IsD(); }

  bool IsW() const { return IsRegister() && Is32Bits(); }
  bool IsX() const { return IsRegister() && Is64Bits(); }

  // These assertions ensure that the size and type of the register are as
  // described. They do not consider the number of lanes that make up a vector.
  // So, for example, Is8B() implies IsD(), and Is1D() implies IsD, but IsD()
  // does not imply Is1D() or Is8B().
  // Check the number of lanes, ie. the format of the vector, using methods such
  // as Is8B(), Is1D(), etc. in the VRegister class.
  bool IsV() const { return IsVRegister(); }
  bool IsB() const { return IsV() && Is8Bits(); }
  bool IsH() const { return IsV() && Is16Bits(); }
  bool IsS() const { return IsV() && Is32Bits(); }
  bool IsD() const { return IsV() && Is64Bits(); }
  bool IsQ() const { return IsV() && Is128Bits(); }

  Register Reg() const;
  VRegister VReg() const;

  Register X() const;
  Register W() const;
  VRegister V() const;
  VRegister B() const;
  VRegister H() const;
  VRegister D() const;
  VRegister S() const;
  VRegister Q() const;

  bool IsSameSizeAndType(const CPURegister& other) const;

  bool is(const CPURegister& other) const { return Is(other); }
  bool is_valid() const { return IsValid(); }

 protected:
  int reg_size_;
  RegisterType reg_type_;

#if defined(V8_OS_WIN) && !defined(__clang__)
  // MSVC has problem to parse template base class as friend class.
  friend RegisterBase;
#else
  friend class RegisterBase;
#endif

  constexpr CPURegister(int code, int size, RegisterType type)
      : RegisterBase(code), reg_size_(size), reg_type_(type) {}

  static constexpr bool IsValidRegister(int code, int size) {
    return (size == kWRegSizeInBits || size == kXRegSizeInBits) &&
           (code < kNumberOfRegisters || code == kSPRegInternalCode);
  }

  static constexpr bool IsValidVRegister(int code, int size) {
    return (size == kBRegSizeInBits || size == kHRegSizeInBits ||
            size == kSRegSizeInBits || size == kDRegSizeInBits ||
            size == kQRegSizeInBits) &&
           code < kNumberOfVRegisters;
  }

  static constexpr bool IsValid(int code, int size, RegisterType type) {
    return (type == kRegister && IsValidRegister(code, size)) ||
           (type == kVRegister && IsValidVRegister(code, size));
  }

  static constexpr bool IsNone(int code, int size, RegisterType type) {
    return type == kNoRegister && code == 0 && size == 0;
  }
};

ASSERT_TRIVIALLY_COPYABLE(CPURegister);

class Register : public CPURegister {
 public:
  static constexpr Register no_reg() { return Register(CPURegister::no_reg()); }

  template <int code, int size>
  static constexpr Register Create() {
    return Register(CPURegister::Create<code, size, CPURegister::kRegister>());
  }

  static Register Create(int code, int size) {
    return Register(CPURegister::Create(code, size, CPURegister::kRegister));
  }

  static Register XRegFromCode(unsigned code);
  static Register WRegFromCode(unsigned code);

  static Register from_code(int code) {
    // Always return an X register.
    return Register::Create(code, kXRegSizeInBits);
  }

  template <int code>
  static Register from_code() {
    // Always return an X register.
    return Register::Create<code, kXRegSizeInBits>();
  }

  static const char* GetSpecialRegisterName(int code) {
    return (code == kSPRegInternalCode) ? "sp" : "UNKNOWN";
  }

 private:
  constexpr explicit Register(const CPURegister& r) : CPURegister(r) {}
};

ASSERT_TRIVIALLY_COPYABLE(Register);

constexpr bool kPadArguments = true;
constexpr bool kSimpleFPAliasing = true;
constexpr bool kSimdMaskRegisters = false;

enum DoubleRegisterCode {
#define REGISTER_CODE(R) kDoubleCode_##R,
  DOUBLE_REGISTERS(REGISTER_CODE)
#undef REGISTER_CODE
      kDoubleAfterLast
};

// Functions for handling NEON vector format information.
enum VectorFormat {
  kFormatUndefined = 0xffffffff,
  kFormat8B = NEON_8B,
  kFormat16B = NEON_16B,
  kFormat4H = NEON_4H,
  kFormat8H = NEON_8H,
  kFormat2S = NEON_2S,
  kFormat4S = NEON_4S,
  kFormat1D = NEON_1D,
  kFormat2D = NEON_2D,

  // Scalar formats. We add the scalar bit to distinguish between scalar and
  // vector enumerations; the bit is always set in the encoding of scalar ops
  // and always clear for vector ops. Although kFormatD and kFormat1D appear
  // to be the same, their meaning is subtly different. The first is a scalar
  // operation, the second a vector operation that only affects one lane.
  kFormatB = NEON_B | NEONScalar,
  kFormatH = NEON_H | NEONScalar,
  kFormatS = NEON_S | NEONScalar,
  kFormatD = NEON_D | NEONScalar
};

VectorFormat VectorFormatHalfWidth(VectorFormat vform);
VectorFormat VectorFormatDoubleWidth(VectorFormat vform);
VectorFormat VectorFormatDoubleLanes(VectorFormat vform);
VectorFormat VectorFormatHalfLanes(VectorFormat vform);
VectorFormat ScalarFormatFromLaneSize(int lanesize);
VectorFormat VectorFormatHalfWidthDoubleLanes(VectorFormat vform);
VectorFormat VectorFormatFillQ(VectorFormat vform);
VectorFormat ScalarFormatFromFormat(VectorFormat vform);
V8_EXPORT_PRIVATE unsigned RegisterSizeInBitsFromFormat(VectorFormat vform);
unsigned RegisterSizeInBytesFromFormat(VectorFormat vform);
int LaneSizeInBytesFromFormat(VectorFormat vform);
unsigned LaneSizeInBitsFromFormat(VectorFormat vform);
int LaneSizeInBytesLog2FromFormat(VectorFormat vform);
V8_EXPORT_PRIVATE int LaneCountFromFormat(VectorFormat vform);
int MaxLaneCountFromFormat(VectorFormat vform);
V8_EXPORT_PRIVATE bool IsVectorFormat(VectorFormat vform);
int64_t MaxIntFromFormat(VectorFormat vform);
int64_t MinIntFromFormat(VectorFormat vform);
uint64_t MaxUintFromFormat(VectorFormat vform);

class VRegister : public CPURegister {
 public:
  static constexpr VRegister no_reg() {
    return VRegister(CPURegister::no_reg(), 0);
  }

  template <int code, int size, int lane_count = 1>
  static constexpr VRegister Create() {
    static_assert(IsValidLaneCount(lane_count), "Invalid lane count");
    return VRegister(CPURegister::Create<code, size, kVRegister>(), lane_count);
  }

  static VRegister Create(int code, int size, int lane_count = 1) {
    DCHECK(IsValidLaneCount(lane_count));
    return VRegister(CPURegister::Create(code, size, CPURegister::kVRegister),
                     lane_count);
  }

  static VRegister Create(int reg_code, VectorFormat format) {
    int reg_size = RegisterSizeInBitsFromFormat(format);
    int reg_count = IsVectorFormat(format) ? LaneCountFromFormat(format) : 1;
    return VRegister::Create(reg_code, reg_size, reg_count);
  }

  static VRegister BRegFromCode(unsigned code);
  static VRegister HRegFromCode(unsigned code);
  static VRegister SRegFromCode(unsigned code);
  static VRegister DRegFromCode(unsigned code);
  static VRegister QRegFromCode(unsigned code);
  static VRegister VRegFromCode(unsigned code);

  VRegister V8B() const {
    return VRegister::Create(code(), kDRegSizeInBits, 8);
  }
  VRegister V16B() const {
    return VRegister::Create(code(), kQRegSizeInBits, 16);
  }
  VRegister V4H() const {
    return VRegister::Create(code(), kDRegSizeInBits, 4);
  }
  VRegister V8H() const {
    return VRegister::Create(code(), kQRegSizeInBits, 8);
  }
  VRegister V2S() const {
    return VRegister::Create(code(), kDRegSizeInBits, 2);
  }
  VRegister V4S() const {
    return VRegister::Create(code(), kQRegSizeInBits, 4);
  }
  VRegister V2D() const {
    return VRegister::Create(code(), kQRegSizeInBits, 2);
  }
  VRegister V1D() const {
    return VRegister::Create(code(), kDRegSizeInBits, 1);
  }

  bool Is8B() const { return (Is64Bits() && (lane_count_ == 8)); }
  bool Is16B() const { return (Is128Bits() && (lane_count_ == 16)); }
  bool Is4H() const { return (Is64Bits() && (lane_count_ == 4)); }
  bool Is8H() const { return (Is128Bits() && (lane_count_ == 8)); }
  bool Is2S() const { return (Is64Bits() && (lane_count_ == 2)); }
  bool Is4S() const { return (Is128Bits() && (lane_count_ == 4)); }
  bool Is1D() const { return (Is64Bits() && (lane_count_ == 1)); }
  bool Is2D() const { return (Is128Bits() && (lane_count_ == 2)); }

  // For consistency, we assert the number of lanes of these scalar registers,
  // even though there are no vectors of equivalent total size with which they
  // could alias.
  bool Is1B() const {
    DCHECK(!(Is8Bits() && IsVector()));
    return Is8Bits();
  }
  bool Is1H() const {
    DCHECK(!(Is16Bits() && IsVector()));
    return Is16Bits();
  }
  bool Is1S() const {
    DCHECK(!(Is32Bits() && IsVector()));
    return Is32Bits();
  }

  bool IsLaneSizeB() const { return LaneSizeInBits() == kBRegSizeInBits; }
  bool IsLaneSizeH() const { return LaneSizeInBits() == kHRegSizeInBits; }
  bool IsLaneSizeS() const { return LaneSizeInBits() == kSRegSizeInBits; }
  bool IsLaneSizeD() const { return LaneSizeInBits() == kDRegSizeInBits; }

  bool IsScalar() const { return lane_count_ == 1; }
  bool IsVector() const { return lane_count_ > 1; }

  bool IsSameFormat(const VRegister& other) const {
    return (reg_size_ == other.reg_size_) && (lane_count_ == other.lane_count_);
  }

  int LaneCount() const { return lane_count_; }

  unsigned LaneSizeInBytes() const { return SizeInBytes() / lane_count_; }

  unsigned LaneSizeInBits() const { return LaneSizeInBytes() * 8; }

  static constexpr int kMaxNumRegisters = kNumberOfVRegisters;
  STATIC_ASSERT(kMaxNumRegisters == kDoubleAfterLast);

  static VRegister from_code(int code) {
    // Always return a D register.
    return VRegister::Create(code, kDRegSizeInBits);
  }

 private:
  int lane_count_;

  constexpr explicit VRegister(const CPURegister& r, int lane_count)
      : CPURegister(r), lane_count_(lane_count) {}

  static constexpr bool IsValidLaneCount(int lane_count) {
    return base::bits::IsPowerOfTwo(lane_count) && lane_count <= 16;
  }
};

ASSERT_TRIVIALLY_COPYABLE(VRegister);

// No*Reg is used to indicate an unused argument, or an error case. Note that
// these all compare equal (using the Is() method). The Register and VRegister
// variants are provided for convenience.
constexpr Register NoReg = Register::no_reg();
constexpr VRegister NoVReg = VRegister::no_reg();
constexpr CPURegister NoCPUReg = CPURegister::no_reg();
constexpr Register no_reg = NoReg;
constexpr VRegister no_dreg = NoVReg;

#define DEFINE_REGISTER(register_class, name, ...) \
  constexpr register_class name = register_class::Create<__VA_ARGS__>()
#define ALIAS_REGISTER(register_class, alias, name) \
  constexpr register_class alias = name

#define DEFINE_REGISTERS(N)                            \
  DEFINE_REGISTER(Register, w##N, N, kWRegSizeInBits); \
  DEFINE_REGISTER(Register, x##N, N, kXRegSizeInBits);
GENERAL_REGISTER_CODE_LIST(DEFINE_REGISTERS)
#undef DEFINE_REGISTERS

DEFINE_REGISTER(Register, wsp, kSPRegInternalCode, kWRegSizeInBits);
DEFINE_REGISTER(Register, sp, kSPRegInternalCode, kXRegSizeInBits);

#define DEFINE_VREGISTERS(N)                            \
  DEFINE_REGISTER(VRegister, b##N, N, kBRegSizeInBits); \
  DEFINE_REGISTER(VRegister, h##N, N, kHRegSizeInBits); \
  DEFINE_REGISTER(VRegister, s##N, N, kSRegSizeInBits); \
  DEFINE_REGISTER(VRegister, d##N, N, kDRegSizeInBits); \
  DEFINE_REGISTER(VRegister, q##N, N, kQRegSizeInBits); \
  DEFINE_REGISTER(VRegister, v##N, N, kQRegSizeInBits);
GENERAL_REGISTER_CODE_LIST(DEFINE_VREGISTERS)
#undef DEFINE_VREGISTERS

#undef DEFINE_REGISTER

// Registers aliases.
ALIAS_REGISTER(VRegister, v8_, v8);  // Avoid conflicts with namespace v8.
ALIAS_REGISTER(Register, ip0, x16);
ALIAS_REGISTER(Register, ip1, x17);
ALIAS_REGISTER(Register, wip0, w16);
ALIAS_REGISTER(Register, wip1, w17);
// Root register.
ALIAS_REGISTER(Register, kRootRegister, x26);
ALIAS_REGISTER(Register, rr, x26);
// Context pointer register.
ALIAS_REGISTER(Register, cp, x27);
ALIAS_REGISTER(Register, fp, x29);
ALIAS_REGISTER(Register, lr, x30);
ALIAS_REGISTER(Register, xzr, x31);
ALIAS_REGISTER(Register, wzr, w31);

// Register used for padding stack slots.
ALIAS_REGISTER(Register, padreg, x31);

// Keeps the 0 double value.
ALIAS_REGISTER(VRegister, fp_zero, d15);
// MacroAssembler fixed V Registers.
ALIAS_REGISTER(VRegister, fp_fixed1, d28);
ALIAS_REGISTER(VRegister, fp_fixed2, d29);

// MacroAssembler scratch V registers.
ALIAS_REGISTER(VRegister, fp_scratch, d30);
ALIAS_REGISTER(VRegister, fp_scratch1, d30);
ALIAS_REGISTER(VRegister, fp_scratch2, d31);

#undef ALIAS_REGISTER

// AreAliased returns true if any of the named registers overlap. Arguments set
// to NoReg are ignored. The system stack pointer may be specified.
bool AreAliased(const CPURegister& reg1, const CPURegister& reg2,
                const CPURegister& reg3 = NoReg,
                const CPURegister& reg4 = NoReg,
                const CPURegister& reg5 = NoReg,
                const CPURegister& reg6 = NoReg,
                const CPURegister& reg7 = NoReg,
                const CPURegister& reg8 = NoReg);

// AreSameSizeAndType returns true if all of the specified registers have the
// same size, and are of the same type. The system stack pointer may be
// specified. Arguments set to NoReg are ignored, as are any subsequent
// arguments. At least one argument (reg1) must be valid (not NoCPUReg).
V8_EXPORT_PRIVATE bool AreSameSizeAndType(
    const CPURegister& reg1, const CPURegister& reg2 = NoCPUReg,
    const CPURegister& reg3 = NoCPUReg, const CPURegister& reg4 = NoCPUReg,
    const CPURegister& reg5 = NoCPUReg, const CPURegister& reg6 = NoCPUReg,
    const CPURegister& reg7 = NoCPUReg, const CPURegister& reg8 = NoCPUReg);

// AreSameFormat returns true if all of the specified VRegisters have the same
// vector format. Arguments set to NoVReg are ignored, as are any subsequent
// arguments. At least one argument (reg1) must be valid (not NoVReg).
bool AreSameFormat(const VRegister& reg1, const VRegister& reg2,
                   const VRegister& reg3 = NoVReg,
                   const VRegister& reg4 = NoVReg);

// AreConsecutive returns true if all of the specified VRegisters are
// consecutive in the register file. Arguments may be set to NoVReg, and if so,
// subsequent arguments must also be NoVReg. At least one argument (reg1) must
// be valid (not NoVReg).
V8_EXPORT_PRIVATE bool AreConsecutive(const VRegister& reg1,
                                      const VRegister& reg2,
                                      const VRegister& reg3 = NoVReg,
                                      const VRegister& reg4 = NoVReg);

using FloatRegister = VRegister;
using DoubleRegister = VRegister;
using Simd128Register = VRegister;

// -----------------------------------------------------------------------------
// Lists of registers.
class V8_EXPORT_PRIVATE CPURegList {
 public:
  template <typename... CPURegisters>
  explicit CPURegList(CPURegister reg0, CPURegisters... regs)
      : list_(CPURegister::ListOf(reg0, regs...)),
        size_(reg0.SizeInBits()),
        type_(reg0.type()) {
    DCHECK(AreSameSizeAndType(reg0, regs...));
    DCHECK(IsValid());
  }

  CPURegList(CPURegister::RegisterType type, int size, RegList list)
      : list_(list), size_(size), type_(type) {
    DCHECK(IsValid());
  }

  CPURegList(CPURegister::RegisterType type, int size, int first_reg,
             int last_reg)
      : size_(size), type_(type) {
    DCHECK(
        ((type == CPURegister::kRegister) && (last_reg < kNumberOfRegisters)) ||
        ((type == CPURegister::kVRegister) &&
         (last_reg < kNumberOfVRegisters)));
    DCHECK(last_reg >= first_reg);
    list_ = (1ULL << (last_reg + 1)) - 1;
    list_ &= ~((1ULL << first_reg) - 1);
    DCHECK(IsValid());
  }

  CPURegister::RegisterType type() const {
    return type_;
  }

  RegList list() const {
    return list_;
  }

  inline void set_list(RegList new_list) {
    list_ = new_list;
    DCHECK(IsValid());
  }

  // Combine another CPURegList into this one. Registers that already exist in
  // this list are left unchanged. The type and size of the registers in the
  // 'other' list must match those in this list.
  void Combine(const CPURegList& other);

  // Remove every register in the other CPURegList from this one. Registers that
  // do not exist in this list are ignored. The type of the registers in the
  // 'other' list must match those in this list.
  void Remove(const CPURegList& other);

  // Variants of Combine and Remove which take CPURegisters.
  void Combine(const CPURegister& other);
  void Remove(const CPURegister& other1, const CPURegister& other2 = NoCPUReg,
              const CPURegister& other3 = NoCPUReg,
              const CPURegister& other4 = NoCPUReg);

  // Variants of Combine and Remove which take a single register by its code;
  // the type and size of the register is inferred from this list.
  void Combine(int code);
  void Remove(int code);

  // Remove all callee-saved registers from the list. This can be useful when
  // preparing registers for an AAPCS64 function call, for example.
  void RemoveCalleeSaved();

  // Align the list to 16 bytes.
  void Align();

  CPURegister PopLowestIndex();
  CPURegister PopHighestIndex();

  // AAPCS64 callee-saved registers.
  static CPURegList GetCalleeSaved(int size = kXRegSizeInBits);
  static CPURegList GetCalleeSavedV(int size = kDRegSizeInBits);

  // AAPCS64 caller-saved registers. Note that this includes lr.
  // TODO(all): Determine how we handle d8-d15 being callee-saved, but the top
  // 64-bits being caller-saved.
  static CPURegList GetCallerSaved(int size = kXRegSizeInBits);
  static CPURegList GetCallerSavedV(int size = kDRegSizeInBits);

  // Registers saved as safepoints.
  static CPURegList GetSafepointSavedRegisters();

  bool IsEmpty() const {
    return list_ == 0;
  }

  bool IncludesAliasOf(const CPURegister& other1,
                       const CPURegister& other2 = NoCPUReg,
                       const CPURegister& other3 = NoCPUReg,
                       const CPURegister& other4 = NoCPUReg) const {
    RegList list = 0;
    if (!other1.IsNone() && (other1.type() == type_)) list |= other1.bit();
    if (!other2.IsNone() && (other2.type() == type_)) list |= other2.bit();
    if (!other3.IsNone() && (other3.type() == type_)) list |= other3.bit();
    if (!other4.IsNone() && (other4.type() == type_)) list |= other4.bit();
    return (list_ & list) != 0;
  }

  int Count() const {
    return CountSetBits(list_, kRegListSizeInBits);
  }

  int RegisterSizeInBits() const {
    return size_;
  }

  int RegisterSizeInBytes() const {
    int size_in_bits = RegisterSizeInBits();
    DCHECK_EQ(size_in_bits % kBitsPerByte, 0);
    return size_in_bits / kBitsPerByte;
  }

  int TotalSizeInBytes() const {
    return RegisterSizeInBytes() * Count();
  }

 private:
  RegList list_;
  int size_;
  CPURegister::RegisterType type_;

  bool IsValid() const {
    constexpr RegList kValidRegisters{0x8000000ffffffff};
    constexpr RegList kValidVRegisters{0x0000000ffffffff};
    switch (type_) {
      case CPURegister::kRegister:
        return (list_ & kValidRegisters) == list_;
      case CPURegister::kVRegister:
        return (list_ & kValidVRegisters) == list_;
      case CPURegister::kNoRegister:
        return list_ == 0;
      default:
        UNREACHABLE();
    }
  }
};

// AAPCS64 callee-saved registers.
#define kCalleeSaved CPURegList::GetCalleeSaved()
#define kCalleeSavedV CPURegList::GetCalleeSavedV()

// AAPCS64 caller-saved registers. Note that this includes lr.
#define kCallerSaved CPURegList::GetCallerSaved()
#define kCallerSavedV CPURegList::GetCallerSavedV()

// Define a {RegisterName} method for {Register} and {VRegister}.
DEFINE_REGISTER_NAMES(Register, GENERAL_REGISTERS)
DEFINE_REGISTER_NAMES(VRegister, VECTOR_REGISTERS)

// Give alias names to registers for calling conventions.
constexpr Register kReturnRegister0 = x0;
constexpr Register kReturnRegister1 = x1;
constexpr Register kReturnRegister2 = x2;
constexpr Register kJSFunctionRegister = x1;
constexpr Register kContextRegister = cp;
constexpr Register kAllocateSizeRegister = x1;

constexpr Register kSpeculationPoisonRegister = x23;

constexpr Register kInterpreterAccumulatorRegister = x0;
constexpr Register kInterpreterBytecodeOffsetRegister = x19;
constexpr Register kInterpreterBytecodeArrayRegister = x20;
constexpr Register kInterpreterDispatchTableRegister = x21;

constexpr Register kJavaScriptCallArgCountRegister = x0;
constexpr Register kJavaScriptCallCodeStartRegister = x2;
constexpr Register kJavaScriptCallTargetRegister = kJSFunctionRegister;
constexpr Register kJavaScriptCallNewTargetRegister = x3;
constexpr Register kJavaScriptCallExtraArg1Register = x2;

constexpr Register kOffHeapTrampolineRegister = ip0;
constexpr Register kRuntimeCallFunctionRegister = x1;
constexpr Register kRuntimeCallArgCountRegister = x0;
constexpr Register kRuntimeCallArgvRegister = x11;
constexpr Register kWasmInstanceRegister = x7;
constexpr Register kWasmCompileLazyFuncIndexRegister = x8;

}  // namespace internal
}  // namespace v8

#endif  // V8_CODEGEN_ARM64_REGISTER_ARM64_H_