• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef V8_WASM_BASELINE_LIFTOFF_REGISTER_H_
6 #define V8_WASM_BASELINE_LIFTOFF_REGISTER_H_
7 
8 #include <iosfwd>
9 #include <memory>
10 
11 #include "src/base/bits.h"
12 #include "src/wasm/baseline/liftoff-assembler-defs.h"
13 #include "src/wasm/wasm-opcodes.h"
14 
15 namespace v8 {
16 namespace internal {
17 namespace wasm {
18 
19 static constexpr bool kNeedI64RegPair = kSystemPointerSize == 4;
20 static constexpr bool kNeedS128RegPair = kFPAliasing == AliasingKind::kCombine;
21 
22 enum RegClass : uint8_t {
23   kGpReg,
24   kFpReg,
25   kGpRegPair = kFpReg + 1 + (kNeedS128RegPair && !kNeedI64RegPair),
26   kFpRegPair = kFpReg + 1 + kNeedI64RegPair,
27   kNoReg = kFpRegPair + kNeedS128RegPair,
28   // +------------------+-------------------------------+
29   // |                  |        kNeedI64RegPair        |
30   // +------------------+---------------+---------------+
31   // | kNeedS128RegPair |     true      |    false      |
32   // +------------------+---------------+---------------+
33   // |             true | 0,1,2,3,4 (a) | 0,1,3,2,3     |
34   // |            false | 0,1,2,3,3 (b) | 0,1,2,2,2 (c) |
35   // +------------------+---------------+---------------+
36   // (a) arm
37   // (b) ia32
38   // (c) x64, arm64
39 };
40 
41 static_assert(kNeedI64RegPair == (kGpRegPair != kNoReg),
42               "kGpRegPair equals kNoReg if unused");
43 static_assert(kNeedS128RegPair == (kFpRegPair != kNoReg),
44               "kFpRegPair equals kNoReg if unused");
45 
46 enum RegPairHalf : uint8_t { kLowWord = 0, kHighWord = 1 };
47 
needs_gp_reg_pair(ValueKind kind)48 static inline constexpr bool needs_gp_reg_pair(ValueKind kind) {
49   return kNeedI64RegPair && kind == kI64;
50 }
51 
needs_fp_reg_pair(ValueKind kind)52 static inline constexpr bool needs_fp_reg_pair(ValueKind kind) {
53   return kNeedS128RegPair && kind == kS128;
54 }
55 
reg_class_for(ValueKind kind)56 static inline constexpr RegClass reg_class_for(ValueKind kind) {
57   switch (kind) {
58     case kF32:
59     case kF64:
60       return kFpReg;
61     case kI8:
62     case kI16:
63     case kI32:
64       return kGpReg;
65     case kI64:
66       return kNeedI64RegPair ? kGpRegPair : kGpReg;
67     case kS128:
68       return kNeedS128RegPair ? kFpRegPair : kFpReg;
69     case kRef:
70     case kOptRef:
71     case kRtt:
72       return kGpReg;
73     default:
74       return kNoReg;  // unsupported kind
75   }
76 }
77 
78 // Description of LiftoffRegister code encoding.
79 // This example uses the ARM architecture, which as of writing has:
80 // - 9 GP registers, requiring 4 bits
81 // - 13 FP regitsters, requiring 5 bits
82 // - kNeedI64RegPair is true
83 // - kNeedS128RegPair is true
84 // - thus, kBitsPerRegPair is 2 + 2 * 4 = 10
85 // - storage_t is uint16_t
86 // The table below illustrates how each RegClass is encoded, with brackets
87 // surrounding the bits which encode the register number.
88 //
89 // +----------------+------------------+
90 // | RegClass       | Example          |
91 // +----------------+------------------+
92 // | kGpReg (1)     | [00 0000   0000] |
93 // | kFpReg (2)     | [00 0000   1001] |
94 // | kGpRegPair (3) | 01 [0000] [0001] |
95 // | kFpRegPair (4) | 10  000[0  0010] |
96 // +----------------+------------------+
97 //
98 // gp and fp registers are encoded in the same index space, which means that
99 // code has to check for kGpRegPair and kFpRegPair before it can treat the code
100 // as a register code.
101 // (1) [0 .. kMaxGpRegCode] encodes gp registers
102 // (2) [kMaxGpRegCode + 1 .. kMaxGpRegCode + kMaxFpRegCode] encodes fp
103 // registers, so in this example, 1001 is really fp register 0.
104 // (3) The second top bit is set for kGpRegPair, and the two gp registers are
105 // stuffed side by side in code. Note that this is not the second top bit of
106 // storage_t, since storage_t is larger than the number of meaningful bits we
107 // need for the encoding.
108 // (4) The top bit is set for kFpRegPair, and the fp register is stuffed into
109 // the bottom part of the code. Unlike (2), this is the fp register code itself
110 // (not sharing index space with gp), so in this example, it is fp register 2.
111 
112 // Maximum code of a gp cache register.
113 static constexpr int kMaxGpRegCode = kLiftoffAssemblerGpCacheRegs.last().code();
114 // Maximum code of an fp cache register.
115 static constexpr int kMaxFpRegCode = kLiftoffAssemblerFpCacheRegs.last().code();
116 static constexpr int kAfterMaxLiftoffGpRegCode = kMaxGpRegCode + 1;
117 static constexpr int kAfterMaxLiftoffFpRegCode =
118     kAfterMaxLiftoffGpRegCode + kMaxFpRegCode + 1;
119 static constexpr int kAfterMaxLiftoffRegCode = kAfterMaxLiftoffFpRegCode;
120 static constexpr int kBitsPerLiftoffRegCode =
121     32 - base::bits::CountLeadingZeros<uint32_t>(kAfterMaxLiftoffRegCode - 1);
122 static constexpr int kBitsPerGpRegCode =
123     32 - base::bits::CountLeadingZeros<uint32_t>(kMaxGpRegCode);
124 static constexpr int kBitsPerFpRegCode =
125     32 - base::bits::CountLeadingZeros<uint32_t>(kMaxFpRegCode);
126 // GpRegPair requires 1 extra bit, S128RegPair also needs an extra bit.
127 static constexpr int kBitsPerRegPair =
128     (kNeedS128RegPair ? 2 : 1) + 2 * kBitsPerGpRegCode;
129 
130 static_assert(2 * kBitsPerGpRegCode >= kBitsPerFpRegCode,
131               "encoding for gp pair and fp pair collides");
132 
133 class LiftoffRegister {
134   static constexpr int needed_bits =
135       std::max(kNeedI64RegPair || kNeedS128RegPair ? kBitsPerRegPair : 0,
136                kBitsPerLiftoffRegCode);
137   using storage_t = std::conditional<
138       needed_bits <= 8, uint8_t,
139       std::conditional<needed_bits <= 16, uint16_t, uint32_t>::type>::type;
140 
141   static_assert(8 * sizeof(storage_t) >= needed_bits,
142                 "chosen type is big enough");
143   // Check for smallest required data type being chosen.
144   // Special case for uint8_t as there are no smaller types.
145   static_assert((8 * sizeof(storage_t) < 2 * needed_bits) ||
146                     (sizeof(storage_t) == sizeof(uint8_t)),
147                 "chosen type is small enough");
148 
149  public:
LiftoffRegister(Register reg)150   constexpr explicit LiftoffRegister(Register reg)
151       : LiftoffRegister(reg.code()) {
152     DCHECK(kLiftoffAssemblerGpCacheRegs.has(reg));
153     DCHECK_EQ(reg, gp());
154   }
LiftoffRegister(DoubleRegister reg)155   constexpr explicit LiftoffRegister(DoubleRegister reg)
156       : LiftoffRegister(kAfterMaxLiftoffGpRegCode + reg.code()) {
157     DCHECK(kLiftoffAssemblerFpCacheRegs.has(reg));
158     DCHECK_EQ(reg, fp());
159   }
160 
from_liftoff_code(int code)161   static LiftoffRegister from_liftoff_code(int code) {
162     LiftoffRegister reg{static_cast<storage_t>(code)};
163     // Check that the code is correct by round-tripping through the
164     // reg-class-specific constructor.
165     DCHECK(
166         (reg.is_gp() && code == LiftoffRegister{reg.gp()}.liftoff_code()) ||
167         (reg.is_fp() && code == LiftoffRegister{reg.fp()}.liftoff_code()) ||
168         (reg.is_gp_pair() &&
169          code == ForPair(reg.low_gp(), reg.high_gp()).liftoff_code()) ||
170         (reg.is_fp_pair() && code == ForFpPair(reg.low_fp()).liftoff_code()));
171     return reg;
172   }
173 
from_code(RegClass rc,int code)174   static LiftoffRegister from_code(RegClass rc, int code) {
175     switch (rc) {
176       case kGpReg:
177         return LiftoffRegister(Register::from_code(code));
178       case kFpReg:
179         return LiftoffRegister(DoubleRegister::from_code(code));
180       default:
181         UNREACHABLE();
182     }
183   }
184 
185   // Shifts the register code depending on the type before converting to a
186   // LiftoffRegister.
from_external_code(RegClass rc,ValueKind kind,int code)187   static LiftoffRegister from_external_code(RegClass rc, ValueKind kind,
188                                             int code) {
189     if (kFPAliasing == AliasingKind::kCombine && kind == kF32) {
190       // Liftoff assumes a one-to-one mapping between float registers and
191       // double registers, and so does not distinguish between f32 and f64
192       // registers. The f32 register code must therefore be halved in order
193       // to pass the f64 code to Liftoff.
194       DCHECK_EQ(0, code % 2);
195       return LiftoffRegister::from_code(rc, code >> 1);
196     }
197     if (kNeedS128RegPair && kind == kS128) {
198       // Similarly for double registers and SIMD registers, the SIMD code
199       // needs to be doubled to pass the f64 code to Liftoff.
200       return LiftoffRegister::ForFpPair(DoubleRegister::from_code(code << 1));
201     }
202     return LiftoffRegister::from_code(rc, code);
203   }
204 
ForPair(Register low,Register high)205   static LiftoffRegister ForPair(Register low, Register high) {
206     DCHECK(kNeedI64RegPair);
207     DCHECK_NE(low, high);
208     storage_t combined_code = low.code() | high.code() << kBitsPerGpRegCode |
209                               1 << (2 * kBitsPerGpRegCode);
210     return LiftoffRegister(combined_code);
211   }
212 
ForFpPair(DoubleRegister low)213   static LiftoffRegister ForFpPair(DoubleRegister low) {
214     DCHECK(kNeedS128RegPair);
215     DCHECK_EQ(0, low.code() % 2);
216     storage_t combined_code = low.code() | 2 << (2 * kBitsPerGpRegCode);
217     return LiftoffRegister(combined_code);
218   }
219 
is_pair()220   constexpr bool is_pair() const {
221     return (kNeedI64RegPair || kNeedS128RegPair) &&
222            (code_ & (3 << (2 * kBitsPerGpRegCode)));
223   }
224 
is_gp_pair()225   constexpr bool is_gp_pair() const {
226     return kNeedI64RegPair && (code_ & (1 << (2 * kBitsPerGpRegCode))) != 0;
227   }
is_fp_pair()228   constexpr bool is_fp_pair() const {
229     return kNeedS128RegPair && (code_ & (2 << (2 * kBitsPerGpRegCode))) != 0;
230   }
is_gp()231   constexpr bool is_gp() const { return code_ < kAfterMaxLiftoffGpRegCode; }
is_fp()232   constexpr bool is_fp() const {
233     return code_ >= kAfterMaxLiftoffGpRegCode &&
234            code_ < kAfterMaxLiftoffFpRegCode;
235   }
236 
low()237   LiftoffRegister low() const {
238     // Common case for most archs where only gp pair supported.
239     if (!kNeedS128RegPair) return LiftoffRegister(low_gp());
240     return is_gp_pair() ? LiftoffRegister(low_gp()) : LiftoffRegister(low_fp());
241   }
242 
high()243   LiftoffRegister high() const {
244     // Common case for most archs where only gp pair supported.
245     if (!kNeedS128RegPair) return LiftoffRegister(high_gp());
246     return is_gp_pair() ? LiftoffRegister(high_gp())
247                         : LiftoffRegister(high_fp());
248   }
249 
low_gp()250   Register low_gp() const {
251     DCHECK(is_gp_pair());
252     static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
253     return Register::from_code(code_ & kCodeMask);
254   }
255 
high_gp()256   Register high_gp() const {
257     DCHECK(is_gp_pair());
258     static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
259     return Register::from_code((code_ >> kBitsPerGpRegCode) & kCodeMask);
260   }
261 
low_fp()262   DoubleRegister low_fp() const {
263     DCHECK(is_fp_pair());
264     static constexpr storage_t kCodeMask = (1 << kBitsPerFpRegCode) - 1;
265     return DoubleRegister::from_code(code_ & kCodeMask);
266   }
267 
high_fp()268   DoubleRegister high_fp() const {
269     DCHECK(is_fp_pair());
270     static constexpr storage_t kCodeMask = (1 << kBitsPerFpRegCode) - 1;
271     return DoubleRegister::from_code((code_ & kCodeMask) + 1);
272   }
273 
gp()274   constexpr Register gp() const {
275     DCHECK(is_gp());
276     return Register::from_code(code_);
277   }
278 
fp()279   constexpr DoubleRegister fp() const {
280     DCHECK(is_fp());
281     return DoubleRegister::from_code(code_ - kAfterMaxLiftoffGpRegCode);
282   }
283 
liftoff_code()284   constexpr int liftoff_code() const {
285     STATIC_ASSERT(sizeof(int) >= sizeof(storage_t));
286     return static_cast<int>(code_);
287   }
288 
reg_class()289   constexpr RegClass reg_class() const {
290     return is_fp_pair() ? kFpRegPair
291                         : is_gp_pair() ? kGpRegPair : is_gp() ? kGpReg : kFpReg;
292   }
293 
294   bool operator==(const LiftoffRegister other) const {
295     DCHECK_EQ(is_gp_pair(), other.is_gp_pair());
296     DCHECK_EQ(is_fp_pair(), other.is_fp_pair());
297     return code_ == other.code_;
298   }
299   bool operator!=(const LiftoffRegister other) const {
300     DCHECK_EQ(is_gp_pair(), other.is_gp_pair());
301     DCHECK_EQ(is_fp_pair(), other.is_fp_pair());
302     return code_ != other.code_;
303   }
overlaps(const LiftoffRegister other)304   bool overlaps(const LiftoffRegister other) const {
305     if (is_pair()) return low().overlaps(other) || high().overlaps(other);
306     if (other.is_pair()) return *this == other.low() || *this == other.high();
307     return *this == other;
308   }
309 
310  private:
LiftoffRegister(storage_t code)311   explicit constexpr LiftoffRegister(storage_t code) : code_(code) {}
312 
313   storage_t code_;
314 };
315 ASSERT_TRIVIALLY_COPYABLE(LiftoffRegister);
316 
317 inline std::ostream& operator<<(std::ostream& os, LiftoffRegister reg) {
318   if (reg.is_gp_pair()) {
319     return os << "<" << reg.low_gp() << "+" << reg.high_gp() << ">";
320   } else if (reg.is_fp_pair()) {
321     return os << "<" << reg.low_fp() << "+" << reg.high_fp() << ">";
322   } else if (reg.is_gp()) {
323     return os << reg.gp();
324   } else {
325     return os << reg.fp();
326   }
327 }
328 
329 class LiftoffRegList {
330  public:
331   class Iterator;
332 
333   static constexpr bool use_u16 = kAfterMaxLiftoffRegCode <= 16;
334   static constexpr bool use_u32 = !use_u16 && kAfterMaxLiftoffRegCode <= 32;
335   using storage_t = std::conditional<
336       use_u16, uint16_t,
337       std::conditional<use_u32, uint32_t, uint64_t>::type>::type;
338 
339   static constexpr storage_t kGpMask =
340       storage_t{kLiftoffAssemblerGpCacheRegs.bits()};
341   static constexpr storage_t kFpMask =
342       storage_t{kLiftoffAssemblerFpCacheRegs.bits()}
343       << kAfterMaxLiftoffGpRegCode;
344   // Sets all even numbered fp registers.
345   static constexpr uint64_t kEvenFpSetMask = uint64_t{0x5555555555555555}
346                                              << kAfterMaxLiftoffGpRegCode;
347 
348   constexpr LiftoffRegList() = default;
349 
350   // Allow to construct LiftoffRegList from a number of
351   // {Register|DoubleRegister|LiftoffRegister}.
352   template <
353       typename... Regs,
354       typename = std::enable_if_t<std::conjunction_v<std::disjunction<
355           std::is_same<Register, Regs>, std::is_same<DoubleRegister, Regs>,
356           std::is_same<LiftoffRegister, Regs>>...>>>
LiftoffRegList(Regs...regs)357   constexpr LiftoffRegList(Regs... regs) {
358     (..., set(regs));
359   }
360 
set(Register reg)361   constexpr Register set(Register reg) {
362     return set(LiftoffRegister(reg)).gp();
363   }
set(DoubleRegister reg)364   constexpr DoubleRegister set(DoubleRegister reg) {
365     return set(LiftoffRegister(reg)).fp();
366   }
367 
set(LiftoffRegister reg)368   constexpr LiftoffRegister set(LiftoffRegister reg) {
369     if (reg.is_pair()) {
370       regs_ |= storage_t{1} << reg.low().liftoff_code();
371       regs_ |= storage_t{1} << reg.high().liftoff_code();
372     } else {
373       regs_ |= storage_t{1} << reg.liftoff_code();
374     }
375     return reg;
376   }
377 
clear(LiftoffRegister reg)378   constexpr LiftoffRegister clear(LiftoffRegister reg) {
379     if (reg.is_pair()) {
380       regs_ &= ~(storage_t{1} << reg.low().liftoff_code());
381       regs_ &= ~(storage_t{1} << reg.high().liftoff_code());
382     } else {
383       regs_ &= ~(storage_t{1} << reg.liftoff_code());
384     }
385     return reg;
386   }
clear(Register reg)387   constexpr Register clear(Register reg) {
388     return clear(LiftoffRegister{reg}).gp();
389   }
clear(DoubleRegister reg)390   constexpr DoubleRegister clear(DoubleRegister reg) {
391     return clear(LiftoffRegister{reg}).fp();
392   }
393 
has(LiftoffRegister reg)394   bool has(LiftoffRegister reg) const {
395     if (reg.is_pair()) {
396       DCHECK_EQ(has(reg.low()), has(reg.high()));
397       reg = reg.low();
398     }
399     return (regs_ & (storage_t{1} << reg.liftoff_code())) != 0;
400   }
has(Register reg)401   bool has(Register reg) const { return has(LiftoffRegister{reg}); }
has(DoubleRegister reg)402   bool has(DoubleRegister reg) const { return has(LiftoffRegister{reg}); }
403 
is_empty()404   constexpr bool is_empty() const { return regs_ == 0; }
405 
GetNumRegsSet()406   constexpr unsigned GetNumRegsSet() const {
407     return base::bits::CountPopulation(regs_);
408   }
409 
410   constexpr LiftoffRegList operator&(const LiftoffRegList other) const {
411     return LiftoffRegList(regs_ & other.regs_);
412   }
413 
414   constexpr LiftoffRegList operator|(const LiftoffRegList other) const {
415     return LiftoffRegList(regs_ | other.regs_);
416   }
417 
GetAdjacentFpRegsSet()418   constexpr LiftoffRegList GetAdjacentFpRegsSet() const {
419     // And regs_ with a right shifted version of itself, so reg[i] is set only
420     // if reg[i+1] is set. We only care about the even fp registers.
421     storage_t available = (regs_ >> 1) & regs_ & kEvenFpSetMask;
422     return LiftoffRegList(available);
423   }
424 
HasAdjacentFpRegsSet()425   constexpr bool HasAdjacentFpRegsSet() const {
426     return !GetAdjacentFpRegsSet().is_empty();
427   }
428 
429   constexpr bool operator==(const LiftoffRegList other) const {
430     return regs_ == other.regs_;
431   }
432   constexpr bool operator!=(const LiftoffRegList other) const {
433     return regs_ != other.regs_;
434   }
435 
GetFirstRegSet()436   LiftoffRegister GetFirstRegSet() const {
437     DCHECK(!is_empty());
438     int first_code = base::bits::CountTrailingZeros(regs_);
439     return LiftoffRegister::from_liftoff_code(first_code);
440   }
441 
GetLastRegSet()442   LiftoffRegister GetLastRegSet() const {
443     DCHECK(!is_empty());
444     int last_code =
445         8 * sizeof(regs_) - 1 - base::bits::CountLeadingZeros(regs_);
446     return LiftoffRegister::from_liftoff_code(last_code);
447   }
448 
MaskOut(const LiftoffRegList mask)449   LiftoffRegList MaskOut(const LiftoffRegList mask) const {
450     // Masking out is guaranteed to return a correct reg list, hence no checks
451     // needed.
452     return FromBits(regs_ & ~mask.regs_);
453   }
454 
GetGpList()455   RegList GetGpList() { return RegList::FromBits(regs_ & kGpMask); }
GetFpList()456   DoubleRegList GetFpList() {
457     return DoubleRegList::FromBits((regs_ & kFpMask) >>
458                                    kAfterMaxLiftoffGpRegCode);
459   }
460 
461   inline Iterator begin() const;
462   inline Iterator end() const;
463 
FromBits(storage_t bits)464   static LiftoffRegList FromBits(storage_t bits) {
465     DCHECK_EQ(bits, bits & (kGpMask | kFpMask));
466     return LiftoffRegList(bits);
467   }
468 
469   template <storage_t bits>
FromBits()470   static constexpr LiftoffRegList FromBits() {
471     static_assert(bits == (bits & (kGpMask | kFpMask)), "illegal reg list");
472     return LiftoffRegList(bits);
473   }
474 
475  private:
476   // Unchecked constructor. Only use for valid bits.
LiftoffRegList(storage_t bits)477   explicit constexpr LiftoffRegList(storage_t bits) : regs_(bits) {}
478 
479   storage_t regs_ = 0;
480 };
481 ASSERT_TRIVIALLY_COPYABLE(LiftoffRegList);
482 
483 static constexpr LiftoffRegList kGpCacheRegList =
484     LiftoffRegList::FromBits<LiftoffRegList::kGpMask>();
485 static constexpr LiftoffRegList kFpCacheRegList =
486     LiftoffRegList::FromBits<LiftoffRegList::kFpMask>();
487 
488 class LiftoffRegList::Iterator {
489  public:
490   LiftoffRegister operator*() { return remaining_.GetFirstRegSet(); }
491   Iterator& operator++() {
492     remaining_.clear(remaining_.GetFirstRegSet());
493     return *this;
494   }
495   bool operator==(Iterator other) { return remaining_ == other.remaining_; }
496   bool operator!=(Iterator other) { return remaining_ != other.remaining_; }
497 
498  private:
Iterator(LiftoffRegList remaining)499   explicit Iterator(LiftoffRegList remaining) : remaining_(remaining) {}
500   friend class LiftoffRegList;
501 
502   LiftoffRegList remaining_;
503 };
504 
begin()505 LiftoffRegList::Iterator LiftoffRegList::begin() const {
506   return Iterator{*this};
507 }
end()508 LiftoffRegList::Iterator LiftoffRegList::end() const {
509   return Iterator{LiftoffRegList{}};
510 }
511 
GetCacheRegList(RegClass rc)512 static constexpr LiftoffRegList GetCacheRegList(RegClass rc) {
513   return rc == kFpReg ? kFpCacheRegList : kGpCacheRegList;
514 }
515 
516 inline std::ostream& operator<<(std::ostream& os, LiftoffRegList reglist) {
517   os << "{";
518   for (bool first = true; !reglist.is_empty(); first = false) {
519     LiftoffRegister reg = reglist.GetFirstRegSet();
520     reglist.clear(reg);
521     os << (first ? "" : ", ") << reg;
522   }
523   return os << "}";
524 }
525 
526 }  // namespace wasm
527 }  // namespace internal
528 }  // namespace v8
529 
530 #endif  // V8_WASM_BASELINE_LIFTOFF_REGISTER_H_
531