• 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 = kPointerSize == 4;
20 
21 enum RegClass : uint8_t {
22   kGpReg,
23   kFpReg,
24   // {kGpRegPair} equals {kNoReg} if {kNeedI64RegPair} is false.
25   kGpRegPair,
26   kNoReg = kGpRegPair + kNeedI64RegPair
27 };
28 
29 enum RegPairHalf : uint8_t { kLowWord, kHighWord };
30 
needs_reg_pair(ValueType type)31 static inline constexpr bool needs_reg_pair(ValueType type) {
32   return kNeedI64RegPair && type == kWasmI64;
33 }
34 
35 // TODO(clemensh): Use a switch once we require C++14 support.
reg_class_for(ValueType type)36 static inline constexpr RegClass reg_class_for(ValueType type) {
37   return needs_reg_pair(type)  // i64 on 32 bit
38              ? kGpRegPair
39              : type == kWasmI32 || type == kWasmI64  // int types
40                    ? kGpReg
41                    : type == kWasmF32 || type == kWasmF64  // float types
42                          ? kFpReg
43                          : kNoReg;  // other (unsupported) types
44 }
45 
46 // Maximum code of a gp cache register.
47 static constexpr int kMaxGpRegCode =
48     8 * sizeof(kLiftoffAssemblerGpCacheRegs) -
49     base::bits::CountLeadingZeros(kLiftoffAssemblerGpCacheRegs);
50 // Maximum code of an fp cache register.
51 static constexpr int kMaxFpRegCode =
52     8 * sizeof(kLiftoffAssemblerFpCacheRegs) -
53     base::bits::CountLeadingZeros(kLiftoffAssemblerFpCacheRegs);
54 // LiftoffRegister encodes both gp and fp in a unified index space.
55 // [0 .. kMaxGpRegCode] encodes gp registers,
56 // [kMaxGpRegCode+1 .. kMaxGpRegCode + kMaxFpRegCode] encodes fp registers.
57 // I64 values on 32 bit platforms are stored in two registers, both encoded in
58 // the same LiftoffRegister value.
59 static constexpr int kAfterMaxLiftoffGpRegCode = kMaxGpRegCode + 1;
60 static constexpr int kAfterMaxLiftoffFpRegCode =
61     kAfterMaxLiftoffGpRegCode + kMaxFpRegCode + 1;
62 static constexpr int kAfterMaxLiftoffRegCode = kAfterMaxLiftoffFpRegCode;
63 static constexpr int kBitsPerLiftoffRegCode =
64     32 - base::bits::CountLeadingZeros<uint32_t>(kAfterMaxLiftoffRegCode - 1);
65 static constexpr int kBitsPerGpRegCode =
66     32 - base::bits::CountLeadingZeros<uint32_t>(kMaxGpRegCode);
67 static constexpr int kBitsPerGpRegPair = 1 + 2 * kBitsPerGpRegCode;
68 
69 class LiftoffRegister {
70   static constexpr int needed_bits =
71       Max(kNeedI64RegPair ? kBitsPerGpRegPair : 0, kBitsPerLiftoffRegCode);
72   using storage_t = std::conditional<
73       needed_bits <= 8, uint8_t,
74       std::conditional<needed_bits <= 16, uint16_t, uint32_t>::type>::type;
75   static_assert(8 * sizeof(storage_t) >= needed_bits &&
76                     8 * sizeof(storage_t) < 2 * needed_bits,
77                 "right type has been chosen");
78 
79  public:
LiftoffRegister(Register reg)80   explicit LiftoffRegister(Register reg) : LiftoffRegister(reg.code()) {
81     DCHECK_EQ(reg, gp());
82   }
LiftoffRegister(DoubleRegister reg)83   explicit LiftoffRegister(DoubleRegister reg)
84       : LiftoffRegister(kAfterMaxLiftoffGpRegCode + reg.code()) {
85     DCHECK_EQ(reg, fp());
86   }
87 
from_liftoff_code(int code)88   static LiftoffRegister from_liftoff_code(int code) {
89     DCHECK_LE(0, code);
90     DCHECK_GT(kAfterMaxLiftoffRegCode, code);
91     DCHECK_EQ(code, static_cast<storage_t>(code));
92     return LiftoffRegister(code);
93   }
94 
from_code(RegClass rc,int code)95   static LiftoffRegister from_code(RegClass rc, int code) {
96     switch (rc) {
97       case kGpReg:
98         return LiftoffRegister(Register::from_code(code));
99       case kFpReg:
100         return LiftoffRegister(DoubleRegister::from_code(code));
101       default:
102         UNREACHABLE();
103     }
104   }
105 
ForPair(Register low,Register high)106   static LiftoffRegister ForPair(Register low, Register high) {
107     DCHECK(kNeedI64RegPair);
108     DCHECK_NE(low, high);
109     storage_t combined_code = low.code() | high.code() << kBitsPerGpRegCode |
110                               1 << (2 * kBitsPerGpRegCode);
111     return LiftoffRegister(combined_code);
112   }
113 
is_pair()114   constexpr bool is_pair() const {
115     return kNeedI64RegPair && (code_ & (1 << (2 * kBitsPerGpRegCode))) != 0;
116   }
is_gp()117   constexpr bool is_gp() const { return code_ < kAfterMaxLiftoffGpRegCode; }
is_fp()118   constexpr bool is_fp() const {
119     return code_ >= kAfterMaxLiftoffGpRegCode &&
120            code_ < kAfterMaxLiftoffFpRegCode;
121   }
122 
low()123   LiftoffRegister low() const { return LiftoffRegister(low_gp()); }
124 
high()125   LiftoffRegister high() const { return LiftoffRegister(high_gp()); }
126 
low_gp()127   Register low_gp() const {
128     DCHECK(is_pair());
129     static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
130     return Register::from_code(code_ & kCodeMask);
131   }
132 
high_gp()133   Register high_gp() const {
134     DCHECK(is_pair());
135     static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
136     return Register::from_code((code_ >> kBitsPerGpRegCode) & kCodeMask);
137   }
138 
gp()139   Register gp() const {
140     DCHECK(is_gp());
141     return Register::from_code(code_);
142   }
143 
fp()144   DoubleRegister fp() const {
145     DCHECK(is_fp());
146     return DoubleRegister::from_code(code_ - kAfterMaxLiftoffGpRegCode);
147   }
148 
liftoff_code()149   uint32_t liftoff_code() const {
150     DCHECK(is_gp() || is_fp());
151     return code_;
152   }
153 
reg_class()154   RegClass reg_class() const {
155     return is_pair() ? kGpRegPair : is_gp() ? kGpReg : kFpReg;
156   }
157 
158   bool operator==(const LiftoffRegister other) const {
159     DCHECK_EQ(is_pair(), other.is_pair());
160     return code_ == other.code_;
161   }
162   bool operator!=(const LiftoffRegister other) const {
163     DCHECK_EQ(is_pair(), other.is_pair());
164     return code_ != other.code_;
165   }
overlaps(const LiftoffRegister other)166   bool overlaps(const LiftoffRegister other) const {
167     if (is_pair()) return low().overlaps(other) || high().overlaps(other);
168     if (other.is_pair()) return *this == other.low() || *this == other.high();
169     return *this == other;
170   }
171 
172  private:
173   storage_t code_;
174 
LiftoffRegister(storage_t code)175   explicit constexpr LiftoffRegister(storage_t code) : code_(code) {}
176 };
177 ASSERT_TRIVIALLY_COPYABLE(LiftoffRegister);
178 
179 inline std::ostream& operator<<(std::ostream& os, LiftoffRegister reg) {
180   if (reg.is_pair()) {
181     return os << "<gp" << reg.low_gp().code() << "+" << reg.high_gp().code()
182               << ">";
183   } else if (reg.is_gp()) {
184     return os << "gp" << reg.gp().code();
185   } else {
186     return os << "fp" << reg.fp().code();
187   }
188 }
189 
190 class LiftoffRegList {
191  public:
192   static constexpr bool use_u16 = kAfterMaxLiftoffRegCode <= 16;
193   static constexpr bool use_u32 = !use_u16 && kAfterMaxLiftoffRegCode <= 32;
194   using storage_t = std::conditional<
195       use_u16, uint16_t,
196       std::conditional<use_u32, uint32_t, uint64_t>::type>::type;
197 
198   static constexpr storage_t kGpMask = storage_t{kLiftoffAssemblerGpCacheRegs};
199   static constexpr storage_t kFpMask = storage_t{kLiftoffAssemblerFpCacheRegs}
200                                        << kAfterMaxLiftoffGpRegCode;
201 
202   constexpr LiftoffRegList() = default;
203 
set(Register reg)204   Register set(Register reg) { return set(LiftoffRegister(reg)).gp(); }
set(DoubleRegister reg)205   DoubleRegister set(DoubleRegister reg) {
206     return set(LiftoffRegister(reg)).fp();
207   }
208 
set(LiftoffRegister reg)209   LiftoffRegister set(LiftoffRegister reg) {
210     if (reg.is_pair()) {
211       regs_ |= storage_t{1} << reg.low().liftoff_code();
212       regs_ |= storage_t{1} << reg.high().liftoff_code();
213     } else {
214       regs_ |= storage_t{1} << reg.liftoff_code();
215     }
216     return reg;
217   }
218 
clear(LiftoffRegister reg)219   LiftoffRegister clear(LiftoffRegister reg) {
220     if (reg.is_pair()) {
221       regs_ &= ~(storage_t{1} << reg.low().liftoff_code());
222       regs_ &= ~(storage_t{1} << reg.high().liftoff_code());
223     } else {
224       regs_ &= ~(storage_t{1} << reg.liftoff_code());
225     }
226     return reg;
227   }
228 
has(LiftoffRegister reg)229   bool has(LiftoffRegister reg) const {
230     if (reg.is_pair()) {
231       DCHECK_EQ(has(reg.low()), has(reg.high()));
232       reg = reg.low();
233     }
234     return (regs_ & (storage_t{1} << reg.liftoff_code())) != 0;
235   }
has(Register reg)236   bool has(Register reg) const { return has(LiftoffRegister(reg)); }
has(DoubleRegister reg)237   bool has(DoubleRegister reg) const { return has(LiftoffRegister(reg)); }
238 
is_empty()239   constexpr bool is_empty() const { return regs_ == 0; }
240 
GetNumRegsSet()241   constexpr unsigned GetNumRegsSet() const {
242     return base::bits::CountPopulation(regs_);
243   }
244 
245   constexpr LiftoffRegList operator&(const LiftoffRegList other) const {
246     return LiftoffRegList(regs_ & other.regs_);
247   }
248 
249   constexpr LiftoffRegList operator~() const {
250     return LiftoffRegList(~regs_ & (kGpMask | kFpMask));
251   }
252 
253   constexpr bool operator==(const LiftoffRegList other) const {
254     return regs_ == other.regs_;
255   }
256   constexpr bool operator!=(const LiftoffRegList other) const {
257     return regs_ != other.regs_;
258   }
259 
GetFirstRegSet()260   LiftoffRegister GetFirstRegSet() const {
261     DCHECK(!is_empty());
262     unsigned first_code = base::bits::CountTrailingZeros(regs_);
263     return LiftoffRegister::from_liftoff_code(first_code);
264   }
265 
GetLastRegSet()266   LiftoffRegister GetLastRegSet() const {
267     DCHECK(!is_empty());
268     unsigned last_code =
269         8 * sizeof(regs_) - 1 - base::bits::CountLeadingZeros(regs_);
270     return LiftoffRegister::from_liftoff_code(last_code);
271   }
272 
MaskOut(const LiftoffRegList mask)273   LiftoffRegList MaskOut(const LiftoffRegList mask) const {
274     // Masking out is guaranteed to return a correct reg list, hence no checks
275     // needed.
276     return FromBits(regs_ & ~mask.regs_);
277   }
278 
FromBits(storage_t bits)279   static LiftoffRegList FromBits(storage_t bits) {
280     DCHECK_EQ(bits, bits & (kGpMask | kFpMask));
281     return LiftoffRegList(bits);
282   }
283 
284   template <storage_t bits>
FromBits()285   static constexpr LiftoffRegList FromBits() {
286     static_assert(bits == (bits & (kGpMask | kFpMask)), "illegal reg list");
287     return LiftoffRegList(bits);
288   }
289 
290   template <typename... Regs>
ForRegs(Regs...regs)291   static LiftoffRegList ForRegs(Regs... regs) {
292     LiftoffRegList list;
293     for (LiftoffRegister reg : {LiftoffRegister(regs)...}) list.set(reg);
294     return list;
295   }
296 
GetGpList()297   RegList GetGpList() { return regs_ & kGpMask; }
GetFpList()298   RegList GetFpList() { return (regs_ & kFpMask) >> kAfterMaxLiftoffGpRegCode; }
299 
300  private:
301   storage_t regs_ = 0;
302 
303   // Unchecked constructor. Only use for valid bits.
LiftoffRegList(storage_t bits)304   explicit constexpr LiftoffRegList(storage_t bits) : regs_(bits) {}
305 };
306 ASSERT_TRIVIALLY_COPYABLE(LiftoffRegList);
307 
308 static constexpr LiftoffRegList kGpCacheRegList =
309     LiftoffRegList::FromBits<LiftoffRegList::kGpMask>();
310 static constexpr LiftoffRegList kFpCacheRegList =
311     LiftoffRegList::FromBits<LiftoffRegList::kFpMask>();
312 
GetCacheRegList(RegClass rc)313 static constexpr LiftoffRegList GetCacheRegList(RegClass rc) {
314   return rc == kGpReg ? kGpCacheRegList : kFpCacheRegList;
315 }
316 
317 inline std::ostream& operator<<(std::ostream& os, LiftoffRegList reglist) {
318   os << "{";
319   for (bool first = true; !reglist.is_empty(); first = false) {
320     LiftoffRegister reg = reglist.GetFirstRegSet();
321     reglist.clear(reg);
322     os << (first ? "" : ", ") << reg;
323   }
324   return os << "}";
325 }
326 
327 }  // namespace wasm
328 }  // namespace internal
329 }  // namespace v8
330 
331 #endif  // V8_WASM_BASELINE_LIFTOFF_REGISTER_H_
332