Home | History | Annotate | Download | only in baseline
      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