v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
liftoff-register.h
Go to the documentation of this file.
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"
14
15namespace v8 {
16namespace internal {
17namespace wasm {
18
19static constexpr bool kNeedI64RegPair = kSystemPointerSize == 4;
21
22enum RegClass : uint8_t {
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
41static_assert(kNeedI64RegPair == (kGpRegPair != kNoReg),
42 "kGpRegPair equals kNoReg if unused");
43static_assert(kNeedS128RegPair == (kFpRegPair != kNoReg),
44 "kFpRegPair equals kNoReg if unused");
45
46enum RegPairHalf : uint8_t { kLowWord = 0, kHighWord = 1 };
47
48static inline constexpr bool needs_gp_reg_pair(ValueKind kind) {
49 return kNeedI64RegPair && kind == kI64;
50}
51
52static inline constexpr bool needs_fp_reg_pair(ValueKind kind) {
53 return kNeedS128RegPair && kind == kS128;
54}
55
56static inline constexpr RegClass reg_class_for(ValueKind kind) {
57 // Statically generate an array that we use for lookup at runtime.
58 constexpr size_t kNumValueKinds = static_cast<size_t>(kTop);
59 constexpr auto kRegClasses =
61 switch (kind) {
62 case kF16:
63 case kF32:
64 case kF64:
65 return kFpReg;
66 case kI8:
67 case kI16:
68 case kI32:
69 return kGpReg;
70 case kI64:
72 case kS128:
74 case kRef:
75 case kRefNull:
76 return kGpReg;
77 case kVoid:
78 return kNoReg; // unsupported kind
79 }
81 });
82 V8_ASSUME(kind < kNumValueKinds);
83 RegClass rc = kRegClasses[kind];
84 V8_ASSUME(rc != kNoReg);
85 return rc;
86}
87
88// Description of LiftoffRegister code encoding.
89// This example uses the ARM architecture, which as of writing has:
90// - 9 GP registers, requiring 4 bits
91// - 13 FP registers, requiring 5 bits
92// - kNeedI64RegPair is true
93// - kNeedS128RegPair is true
94// - thus, kBitsPerRegPair is 2 + 2 * 4 = 10
95// - storage_t is uint16_t
96// The table below illustrates how each RegClass is encoded, with brackets
97// surrounding the bits which encode the register number.
98//
99// +----------------+------------------+
100// | RegClass | Example |
101// +----------------+------------------+
102// | kGpReg (1) | [00 0000 0000] |
103// | kFpReg (2) | [00 0000 1001] |
104// | kGpRegPair (3) | 01 [0000] [0001] |
105// | kFpRegPair (4) | 10 000[0 0010] |
106// +----------------+------------------+
107//
108// gp and fp registers are encoded in the same index space, which means that
109// code has to check for kGpRegPair and kFpRegPair before it can treat the code
110// as a register code.
111// (1) [0 .. kMaxGpRegCode] encodes gp registers
112// (2) [kMaxGpRegCode + 1 .. kMaxGpRegCode + kMaxFpRegCode] encodes fp
113// registers, so in this example, 1001 is really fp register 0.
114// (3) The second top bit is set for kGpRegPair, and the two gp registers are
115// stuffed side by side in code. Note that this is not the second top bit of
116// storage_t, since storage_t is larger than the number of meaningful bits we
117// need for the encoding.
118// (4) The top bit is set for kFpRegPair, and the fp register is stuffed into
119// the bottom part of the code. Unlike (2), this is the fp register code itself
120// (not sharing index space with gp), so in this example, it is fp register 2.
121
122// Maximum code of a gp cache register.
124// Maximum code of an fp cache register.
126static constexpr int kAfterMaxLiftoffGpRegCode = kMaxGpRegCode + 1;
127static constexpr int kAfterMaxLiftoffFpRegCode =
130static constexpr int kBitsPerLiftoffRegCode =
132static constexpr int kBitsPerGpRegCode =
134static constexpr int kBitsPerFpRegCode =
136// GpRegPair requires 1 extra bit, S128RegPair also needs an extra bit.
137static constexpr int kBitsPerRegPair =
138 (kNeedS128RegPair ? 2 : 1) + 2 * kBitsPerGpRegCode;
139
140static_assert(2 * kBitsPerGpRegCode >= kBitsPerFpRegCode,
141 "encoding for gp pair and fp pair collides");
142
144 static constexpr int needed_bits =
147 using storage_t = std::conditional<
148 needed_bits <= 8, uint8_t,
149 std::conditional<needed_bits <= 16, uint16_t, uint32_t>::type>::type;
150
151 static_assert(8 * sizeof(storage_t) >= needed_bits,
152 "chosen type is big enough");
153 // Check for smallest required data type being chosen.
154 // Special case for uint8_t as there are no smaller types.
155 static_assert((8 * sizeof(storage_t) < 2 * needed_bits) ||
156 (sizeof(storage_t) == sizeof(uint8_t)),
157 "chosen type is small enough");
158
159 public:
160 constexpr explicit LiftoffRegister(Register reg)
161 : LiftoffRegister(reg.code()) {
163 DCHECK_EQ(reg, gp());
164 }
170
171#if defined(V8_TARGET_ARCH_IA32)
172 // IA32 needs a fixed xmm0 register as a LiftoffRegister, however, xmm0 is not
173 // an allocatable double register (see register-ia32.h). This constructor
174 // allows bypassing the DCHECK that the LiftoffRegister has to be allocatable.
175 static LiftoffRegister from_uncached(DoubleRegister reg) {
178 }
179#endif
180
182 LiftoffRegister reg{static_cast<storage_t>(code)};
183 // Check that the code is correct by round-tripping through the
184 // reg-class-specific constructor.
185 DCHECK(
186 (reg.is_gp() && code == LiftoffRegister{reg.gp()}.liftoff_code()) ||
187 (reg.is_fp() && code == LiftoffRegister{reg.fp()}.liftoff_code()) ||
188 (reg.is_gp_pair() &&
189 code == ForPair(reg.low_gp(), reg.high_gp()).liftoff_code()) ||
190 (reg.is_fp_pair() && code == ForFpPair(reg.low_fp()).liftoff_code()));
191 return reg;
192 }
193
194 static LiftoffRegister from_code(RegClass rc, int code) {
195 switch (rc) {
196 case kGpReg:
198 case kFpReg:
200 default:
201 UNREACHABLE();
202 }
203 }
204
205 // Shifts the register code depending on the type before converting to a
206 // LiftoffRegister.
208 int code) {
210 // Liftoff assumes a one-to-one mapping between float registers and
211 // double registers, and so does not distinguish between f32 and f64
212 // registers. The f32 register code must therefore be halved in order
213 // to pass the f64 code to Liftoff.
214 DCHECK_EQ(0, code % 2);
215 return LiftoffRegister::from_code(rc, code >> 1);
216 }
217 if (kNeedS128RegPair && kind == kS128) {
218 // Similarly for double registers and SIMD registers, the SIMD code
219 // needs to be doubled to pass the f64 code to Liftoff.
221 }
222 return LiftoffRegister::from_code(rc, code);
223 }
224
227 DCHECK_NE(low, high);
228 storage_t combined_code = low.code() | (high.code() << kBitsPerGpRegCode) |
229 (1 << (2 * kBitsPerGpRegCode));
230 return LiftoffRegister(combined_code);
231 }
232
235 DCHECK_EQ(0, low.code() % 2);
236 storage_t combined_code = low.code() | 2 << (2 * kBitsPerGpRegCode);
237 return LiftoffRegister(combined_code);
238 }
239
240 constexpr bool is_pair() const {
241 return (kNeedI64RegPair || kNeedS128RegPair) &&
242 (code_ & (3 << (2 * kBitsPerGpRegCode)));
243 }
244
245 constexpr bool is_gp_pair() const {
246 return kNeedI64RegPair && (code_ & (1 << (2 * kBitsPerGpRegCode))) != 0;
247 }
248 constexpr bool is_fp_pair() const {
249 return kNeedS128RegPair && (code_ & (2 << (2 * kBitsPerGpRegCode))) != 0;
250 }
251 constexpr bool is_gp() const { return code_ < kAfterMaxLiftoffGpRegCode; }
252 constexpr bool is_fp() const {
255 }
256
258 // Common case for most archs where only gp pair supported.
261 }
262
264 // Common case for most archs where only gp pair supported.
268 }
269
270 Register low_gp() const {
272 static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
273 return Register::from_code(code_ & kCodeMask);
274 }
275
278 static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
279 return Register::from_code((code_ >> kBitsPerGpRegCode) & kCodeMask);
280 }
281
284 static constexpr storage_t kCodeMask = (1 << kBitsPerFpRegCode) - 1;
285 return DoubleRegister::from_code(code_ & kCodeMask);
286 }
287
290 static constexpr storage_t kCodeMask = (1 << kBitsPerFpRegCode) - 1;
291 return DoubleRegister::from_code((code_ & kCodeMask) + 1);
292 }
293
294 constexpr Register gp() const {
295 DCHECK(is_gp());
297 }
298
303
304 constexpr int liftoff_code() const {
305 static_assert(sizeof(int) >= sizeof(storage_t));
306 return static_cast<int>(code_);
307 }
308
309 constexpr RegClass reg_class() const {
310 return is_fp_pair() ? kFpRegPair
312 }
313
314 bool operator==(const LiftoffRegister other) const {
315 DCHECK_EQ(is_gp_pair(), other.is_gp_pair());
316 DCHECK_EQ(is_fp_pair(), other.is_fp_pair());
317 return code_ == other.code_;
318 }
319 bool overlaps(const LiftoffRegister other) const {
320 if (is_pair()) return low().overlaps(other) || high().overlaps(other);
321 if (other.is_pair()) return *this == other.low() || *this == other.high();
322 return *this == other;
323 }
324
325 private:
326 explicit constexpr LiftoffRegister(storage_t code) : code_(code) {}
327
329};
331
332inline std::ostream& operator<<(std::ostream& os, LiftoffRegister reg) {
333 if (reg.is_gp_pair()) {
334 return os << "<" << reg.low_gp() << "+" << reg.high_gp() << ">";
335 } else if (reg.is_fp_pair()) {
336 return os << "<" << reg.low_fp() << "+" << reg.high_fp() << ">";
337 } else if (reg.is_gp()) {
338 return os << reg.gp();
339 } else {
340 return os << reg.fp();
341 }
342}
343
345 public:
346 class Iterator;
347
348 static constexpr bool use_u16 = kAfterMaxLiftoffRegCode <= 16;
349 static constexpr bool use_u32 = !use_u16 && kAfterMaxLiftoffRegCode <= 32;
350 using storage_t = std::conditional<
351 use_u16, uint16_t,
352 std::conditional<use_u32, uint32_t, uint64_t>::type>::type;
353
359 // Sets all even numbered fp registers.
360 static constexpr uint64_t kEvenFpSetMask = uint64_t{0x5555555555555555}
362 static constexpr uint64_t kOddFpSetMask = uint64_t{0xAAAAAAAAAAAAAAAA}
364
365 constexpr LiftoffRegList() = default;
366
367 // Allow to construct LiftoffRegList from a number of
368 // {Register|DoubleRegister|LiftoffRegister}.
369 template <typename... Regs>
370 constexpr explicit LiftoffRegList(Regs... regs)
371 requires(
372 std::conjunction_v<std::disjunction<
373 std::is_same<Register, Regs>, std::is_same<DoubleRegister, Regs>,
374 std::is_same<LiftoffRegister, Regs>>...>)
375 {
376 (..., set(regs));
377 }
378
380 return set(LiftoffRegister(reg)).gp();
381 }
383 return set(LiftoffRegister(reg)).fp();
384 }
385
387 if (reg.is_pair()) {
388 regs_ |= storage_t{1} << reg.low().liftoff_code();
389 regs_ |= storage_t{1} << reg.high().liftoff_code();
390 } else {
391 regs_ |= storage_t{1} << reg.liftoff_code();
392 }
393 return reg;
394 }
395
397 if (reg.is_pair()) {
398 regs_ &= ~(storage_t{1} << reg.low().liftoff_code());
399 regs_ &= ~(storage_t{1} << reg.high().liftoff_code());
400 } else {
401 regs_ &= ~(storage_t{1} << reg.liftoff_code());
402 }
403 return reg;
404 }
406 return clear(LiftoffRegister{reg}).gp();
407 }
409 return clear(LiftoffRegister{reg}).fp();
410 }
411
412 bool has(LiftoffRegister reg) const {
413 if (reg.is_pair()) {
414 DCHECK_EQ(has(reg.low()), has(reg.high()));
415 reg = reg.low();
416 }
417 return (regs_ & (storage_t{1} << reg.liftoff_code())) != 0;
418 }
419 bool has(Register reg) const { return has(LiftoffRegister{reg}); }
420 bool has(DoubleRegister reg) const { return has(LiftoffRegister{reg}); }
421
422 constexpr bool is_empty() const { return regs_ == 0; }
423
424 constexpr unsigned GetNumRegsSet() const {
426 }
427
428 constexpr LiftoffRegList operator&(const LiftoffRegList other) const {
429 return LiftoffRegList(regs_ & other.regs_);
430 }
431
432 constexpr LiftoffRegList& operator&=(const LiftoffRegList other) {
433 regs_ &= other.regs_;
434 return *this;
435 }
436
437 constexpr LiftoffRegList operator|(const LiftoffRegList other) const {
438 return LiftoffRegList(regs_ | other.regs_);
439 }
440
441 constexpr LiftoffRegList& operator|=(const LiftoffRegList other) {
442 regs_ |= other.regs_;
443 return *this;
444 }
445
447 // And regs_ with a right shifted version of itself, so reg[i] is set only
448 // if reg[i+1] is set. We only care about the even fp registers.
449 storage_t available = (regs_ >> 1) & regs_ & kEvenFpSetMask;
450 return LiftoffRegList(available);
451 }
452
453 constexpr bool HasAdjacentFpRegsSet() const {
454 return !GetAdjacentFpRegsSet().is_empty();
455 }
456
457 // Returns a list where if any part of an adjacent pair of FP regs was set,
458 // both are set in the result. For example, [1, 4] is turned into [0, 1, 4, 5]
459 // because (0, 1) and (4, 5) are adjacent pairs.
461 storage_t odd_regs = regs_ & kOddFpSetMask;
462 storage_t even_regs = regs_ & kEvenFpSetMask;
463 return FromBits(regs_ | ((odd_regs >> 1) & kFpMask) |
464 ((even_regs << 1) & kFpMask));
465 }
466
467 constexpr bool operator==(const LiftoffRegList&) const = default;
468
470 V8_ASSUME(regs_ != 0);
471 int first_code = base::bits::CountTrailingZeros(regs_);
472 return LiftoffRegister::from_liftoff_code(first_code);
473 }
474
476 V8_ASSUME(regs_ != 0);
477 int last_code =
478 8 * sizeof(regs_) - 1 - base::bits::CountLeadingZeros(regs_);
479 return LiftoffRegister::from_liftoff_code(last_code);
480 }
481
483 // Masking out is guaranteed to return a correct reg list, hence no checks
484 // needed.
485 return FromBits(regs_ & ~mask.regs_);
486 }
487
493
494 inline Iterator begin() const;
495 inline Iterator end() const;
496
497 static constexpr LiftoffRegList FromBits(storage_t bits) {
498 DCHECK_EQ(bits, bits & (kGpMask | kFpMask));
499 return LiftoffRegList(bits);
500 }
501
502 template <storage_t bits>
503 static constexpr LiftoffRegList FromBits() {
504 static_assert(bits == (bits & (kGpMask | kFpMask)), "illegal reg list");
505 return LiftoffRegList{bits};
506 }
507
508#if DEBUG
509 void Print() const;
510#endif
511
512 private:
513 // Unchecked constructor. Only use for valid bits.
514 explicit constexpr LiftoffRegList(storage_t bits) : regs_(bits) {}
515
517};
519
524
526 public:
530 return *this;
531 }
532 bool operator==(const Iterator& other) const = default;
533
534 private:
535 explicit Iterator(LiftoffRegList remaining) : remaining_(remaining) {}
536 friend class LiftoffRegList;
537
539};
540
547
549 V8_ASSUME(rc == kFpReg || rc == kGpReg);
550 static_assert(kGpReg == 0 && kFpReg == 1);
551 constexpr LiftoffRegList kRegLists[2]{kGpCacheRegList, kFpCacheRegList};
552 return kRegLists[rc];
553}
554
555inline std::ostream& operator<<(std::ostream& os, LiftoffRegList reglist) {
556 os << "{";
557 for (bool first = true; !reglist.is_empty(); first = false) {
559 reglist.clear(reg);
560 os << (first ? "" : ", ") << reg;
561 }
562 return os << "}";
563}
564
565} // namespace wasm
566} // namespace internal
567} // namespace v8
568
569#endif // V8_WASM_BASELINE_LIFTOFF_REGISTER_H_
Builtins::Kind kind
Definition builtins.cc:40
constexpr RegisterT last() const
constexpr bool has(RegisterT reg) const
static constexpr RegListBase FromBits()
constexpr storage_t bits() const
static constexpr DwVfpRegister from_code(int8_t code)
constexpr int8_t code() const
static constexpr Register from_code(int code)
bool operator==(const Iterator &other) const =default
constexpr DoubleRegister set(DoubleRegister reg)
constexpr Register set(Register reg)
LiftoffRegister GetLastRegSet() const
constexpr DoubleRegister clear(DoubleRegister reg)
static constexpr uint64_t kEvenFpSetMask
static constexpr storage_t kFpMask
constexpr LiftoffRegister clear(LiftoffRegister reg)
constexpr bool operator==(const LiftoffRegList &) const =default
LiftoffRegList MaskOut(const LiftoffRegList mask) const
constexpr LiftoffRegList(storage_t bits)
constexpr bool HasAdjacentFpRegsSet() const
static constexpr LiftoffRegList FromBits(storage_t bits)
std::conditional< use_u16, uint16_t, std::conditional< use_u32, uint32_t, uint64_t >::type >::type storage_t
constexpr LiftoffRegList & operator|=(const LiftoffRegList other)
bool has(DoubleRegister reg) const
constexpr LiftoffRegister set(LiftoffRegister reg)
constexpr LiftoffRegList(Regs... regs)
static constexpr uint64_t kOddFpSetMask
constexpr LiftoffRegList()=default
bool has(LiftoffRegister reg) const
constexpr LiftoffRegList GetAdjacentFpRegsSet() const
constexpr unsigned GetNumRegsSet() const
constexpr LiftoffRegList & operator&=(const LiftoffRegList other)
LiftoffRegister GetFirstRegSet() const
static constexpr storage_t kGpMask
static constexpr LiftoffRegList FromBits()
constexpr LiftoffRegList SpreadSetBitsToAdjacentFpRegs() const
constexpr LiftoffRegList operator&(const LiftoffRegList other) const
constexpr LiftoffRegList operator|(const LiftoffRegList other) const
constexpr Register clear(Register reg)
bool operator==(const LiftoffRegister other) const
constexpr DoubleRegister fp() const
static LiftoffRegister from_liftoff_code(int code)
static LiftoffRegister ForFpPair(DoubleRegister low)
bool overlaps(const LiftoffRegister other) const
static LiftoffRegister from_code(RegClass rc, int code)
constexpr RegClass reg_class() const
static LiftoffRegister from_external_code(RegClass rc, ValueKind kind, int code)
static LiftoffRegister ForPair(Register low, Register high)
std::conditional< needed_bits<=8, uint8_t, std::conditional< needed_bits<=16, uint16_t, uint32_t >::type >::type storage_t
constexpr LiftoffRegister(storage_t code)
constexpr LiftoffRegister(Register reg)
constexpr LiftoffRegister(DoubleRegister reg)
Handle< Code > code
LiftoffRegister reg
uint32_t const mask
constexpr unsigned CountLeadingZeros(T value)
Definition bits.h:100
constexpr unsigned CountTrailingZeros(T value)
Definition bits.h:144
constexpr unsigned CountPopulation(T value)
Definition bits.h:26
constexpr auto make_array(Function f)
static constexpr RegClass reg_class_for(ValueKind kind)
static constexpr int kBitsPerLiftoffRegCode
static constexpr int kMaxFpRegCode
static constexpr bool needs_gp_reg_pair(ValueKind kind)
static constexpr int kBitsPerFpRegCode
static constexpr bool needs_fp_reg_pair(ValueKind kind)
constexpr DoubleRegList kLiftoffAssemblerFpCacheRegs
static constexpr int kAfterMaxLiftoffRegCode
constexpr RegList kLiftoffAssemblerGpCacheRegs
static constexpr bool kNeedS128RegPair
static constexpr int kMaxGpRegCode
static constexpr int kBitsPerGpRegCode
static constexpr LiftoffRegList GetCacheRegList(RegClass rc)
static constexpr LiftoffRegList kGpCacheRegList
std::ostream & operator<<(std::ostream &os, LiftoffVarState slot)
static constexpr LiftoffRegList kFpCacheRegList
static constexpr int kAfterMaxLiftoffFpRegCode
static constexpr int kAfterMaxLiftoffGpRegCode
static constexpr bool kNeedI64RegPair
static constexpr int kBitsPerRegPair
constexpr AliasingKind kFPAliasing
void Print(Tagged< Object > obj)
Definition objects.h:774
constexpr int kSystemPointerSize
Definition globals.h:410
Definition c-api.cc:87
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define CONSTEXPR_UNREACHABLE()
Definition logging.h:73
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define ASSERT_TRIVIALLY_COPYABLE(T)
Definition macros.h:267
#define V8_ASSUME
Definition v8config.h:533
wasm::ValueType type