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 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. 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: 80 explicit LiftoffRegister(Register reg) : LiftoffRegister(reg.code()) { 81 DCHECK_EQ(reg, gp()); 82 } 83 explicit LiftoffRegister(DoubleRegister reg) 84 : LiftoffRegister(kAfterMaxLiftoffGpRegCode + reg.code()) { 85 DCHECK_EQ(reg, fp()); 86 } 87 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 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 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 114 constexpr bool is_pair() const { 115 return kNeedI64RegPair && (code_ & (1 << (2 * kBitsPerGpRegCode))) != 0; 116 } 117 constexpr bool is_gp() const { return code_ < kAfterMaxLiftoffGpRegCode; } 118 constexpr bool is_fp() const { 119 return code_ >= kAfterMaxLiftoffGpRegCode && 120 code_ < kAfterMaxLiftoffFpRegCode; 121 } 122 123 LiftoffRegister low() const { return LiftoffRegister(low_gp()); } 124 125 LiftoffRegister high() const { return LiftoffRegister(high_gp()); } 126 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 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 139 Register gp() const { 140 DCHECK(is_gp()); 141 return Register::from_code(code_); 142 } 143 144 DoubleRegister fp() const { 145 DCHECK(is_fp()); 146 return DoubleRegister::from_code(code_ - kAfterMaxLiftoffGpRegCode); 147 } 148 149 uint32_t liftoff_code() const { 150 DCHECK(is_gp() || is_fp()); 151 return code_; 152 } 153 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 } 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 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 204 Register set(Register reg) { return set(LiftoffRegister(reg)).gp(); } 205 DoubleRegister set(DoubleRegister reg) { 206 return set(LiftoffRegister(reg)).fp(); 207 } 208 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 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 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 } 236 bool has(Register reg) const { return has(LiftoffRegister(reg)); } 237 bool has(DoubleRegister reg) const { return has(LiftoffRegister(reg)); } 238 239 constexpr bool is_empty() const { return regs_ == 0; } 240 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 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 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 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 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> 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> 291 static LiftoffRegList ForRegs(Regs... regs) { 292 LiftoffRegList list; 293 for (LiftoffRegister reg : {LiftoffRegister(regs)...}) list.set(reg); 294 return list; 295 } 296 297 RegList GetGpList() { return regs_ & kGpMask; } 298 RegList GetFpList() { return (regs_ & kFpMask) >> kAfterMaxLiftoffGpRegCode; } 299 300 private: 301 storage_t regs_ = 0; 302 303 // Unchecked constructor. Only use for valid 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 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