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