Commit 160f6009 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[Liftoff] Add support for i64 register pairs

In order to support i64 values on 32 bit platforms, we extend the
{LiftoffRegister} class to support storing pairs of GP registers on
those platforms. On 64 bit platforms, this refactoring should cause no
slowdown, as the compiler can statically determine that
{LiftoffRegister::is_pair()} is always false.

R=titzer@chromium.org

Bug: v8:6600
Change-Id: Ie9966d599271b8779959b1809ab4e129b68b80d7
Reviewed-on: https://chromium-review.googlesource.com/890261Reviewed-by: 's avatarBen Titzer <titzer@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#50986}
parent d161bc3c
...@@ -140,22 +140,34 @@ class LiftoffAssembler : public TurboAssembler { ...@@ -140,22 +140,34 @@ class LiftoffAssembler : public TurboAssembler {
} }
void inc_used(LiftoffRegister reg) { void inc_used(LiftoffRegister reg) {
if (reg.is_pair()) {
inc_used(reg.low());
inc_used(reg.high());
return;
}
used_registers.set(reg); used_registers.set(reg);
DCHECK_GT(kMaxInt, register_use_count[reg.liftoff_code()]); DCHECK_GT(kMaxInt, register_use_count[reg.liftoff_code()]);
++register_use_count[reg.liftoff_code()]; ++register_use_count[reg.liftoff_code()];
} }
// Returns whether this was the last use. // Returns whether this was the last use.
bool dec_used(LiftoffRegister reg) { void dec_used(LiftoffRegister reg) {
DCHECK(is_used(reg)); DCHECK(is_used(reg));
if (reg.is_pair()) {
dec_used(reg.low());
dec_used(reg.high());
return;
}
int code = reg.liftoff_code(); int code = reg.liftoff_code();
DCHECK_LT(0, register_use_count[code]); DCHECK_LT(0, register_use_count[code]);
if (--register_use_count[code] != 0) return false; if (--register_use_count[code] == 0) used_registers.clear(reg);
used_registers.clear(reg);
return true;
} }
bool is_used(LiftoffRegister reg) const { bool is_used(LiftoffRegister reg) const {
if (reg.is_pair()) {
DCHECK_EQ(is_used(reg.low()), is_used(reg.high()));
reg = reg.low();
}
bool used = used_registers.has(reg); bool used = used_registers.has(reg);
DCHECK_EQ(used, register_use_count[reg.liftoff_code()] != 0); DCHECK_EQ(used, register_use_count[reg.liftoff_code()] != 0);
return used; return used;
...@@ -237,7 +249,12 @@ class LiftoffAssembler : public TurboAssembler { ...@@ -237,7 +249,12 @@ class LiftoffAssembler : public TurboAssembler {
// Get an unused register for class {rc}, potentially spilling to free one. // Get an unused register for class {rc}, potentially spilling to free one.
LiftoffRegister GetUnusedRegister(RegClass rc, LiftoffRegList pinned = {}) { LiftoffRegister GetUnusedRegister(RegClass rc, LiftoffRegList pinned = {}) {
DCHECK(rc == kGpReg || rc == kFpReg); if (kNeedI64RegPair && rc == kGpRegPair) {
LiftoffRegList candidates = kGpCacheRegList;
LiftoffRegister low = pinned.set(GetUnusedRegister(candidates, pinned));
LiftoffRegister high = GetUnusedRegister(candidates, pinned);
return LiftoffRegister::ForPair(low, high);
}
LiftoffRegList candidates = GetCacheRegList(rc); LiftoffRegList candidates = GetCacheRegList(rc);
return GetUnusedRegister(candidates, pinned); return GetUnusedRegister(candidates, pinned);
} }
......
...@@ -202,8 +202,7 @@ class LiftoffCompiler { ...@@ -202,8 +202,7 @@ class LiftoffCompiler {
LiftoffRegister reg = LiftoffRegister reg =
rc == kGpReg ? LiftoffRegister(Register::from_code(reg_code)) rc == kGpReg ? LiftoffRegister(Register::from_code(reg_code))
: LiftoffRegister(DoubleRegister::from_code(reg_code)); : LiftoffRegister(DoubleRegister::from_code(reg_code));
LiftoffRegList cache_regs = LiftoffRegList cache_regs = GetCacheRegList(rc);
rc == kGpReg ? kGpCacheRegList : kFpCacheRegList;
if (cache_regs.has(reg)) { if (cache_regs.has(reg)) {
// This is a cache register, just use it. // This is a cache register, just use it.
__ PushRegister(type, reg); __ PushRegister(type, reg);
......
...@@ -16,24 +16,27 @@ namespace v8 { ...@@ -16,24 +16,27 @@ namespace v8 {
namespace internal { namespace internal {
namespace wasm { namespace wasm {
enum RegClass { kNoReg, kGpReg, kFpReg }; static constexpr bool kNeedI64RegPair = kPointerSize == 4;
enum RegClass {
kGpReg,
kFpReg,
// {kGpRegPair} equals {kNoReg} if {kNeedI64RegPair} is false.
kGpRegPair,
kNoReg = kGpRegPair + kNeedI64RegPair
};
// TODO(clemensh): Use a switch once we require C++14 support. // TODO(clemensh): Use a switch once we require C++14 support.
static inline constexpr RegClass reg_class_for(ValueType type) { static inline constexpr RegClass reg_class_for(ValueType type) {
return type == kWasmI32 || type == kWasmI64 // int types return kNeedI64RegPair && type == kWasmI64 // i64 on 32 bit
? kGpReg ? kGpRegPair
: type == kWasmF32 || type == kWasmF64 // float types : type == kWasmI32 || type == kWasmI64 // int types
? kFpReg ? kGpReg
: kNoReg; // other (unsupported) types : type == kWasmF32 || type == kWasmF64 // float types
? kFpReg
: kNoReg; // other (unsupported) types
} }
// RegForClass<rc>: Register for rc==kGpReg, DoubleRegister for rc==kFpReg, void
// for all other values of rc.
template <RegClass rc>
using RegForClass = typename std::conditional<
rc == kGpReg, Register,
typename std::conditional<rc == kFpReg, DoubleRegister, void>::type>::type;
// Maximum code of a gp cache register. // Maximum code of a gp cache register.
static constexpr int kMaxGpRegCode = static constexpr int kMaxGpRegCode =
8 * sizeof(kLiftoffAssemblerGpCacheRegs) - 8 * sizeof(kLiftoffAssemblerGpCacheRegs) -
...@@ -45,14 +48,27 @@ static constexpr int kMaxFpRegCode = ...@@ -45,14 +48,27 @@ static constexpr int kMaxFpRegCode =
// LiftoffRegister encodes both gp and fp in a unified index space. // LiftoffRegister encodes both gp and fp in a unified index space.
// [0 .. kMaxGpRegCode] encodes gp registers, // [0 .. kMaxGpRegCode] encodes gp registers,
// [kMaxGpRegCode+1 .. kMaxGpRegCode + kMaxFpRegCode] encodes fp registers. // [kMaxGpRegCode+1 .. kMaxGpRegCode + kMaxFpRegCode] encodes fp registers.
// I64 values on 32 bit platforms are stored in two registers, both encoded in
// the same LiftoffRegister value.
static constexpr int kAfterMaxLiftoffGpRegCode = kMaxGpRegCode + 1; static constexpr int kAfterMaxLiftoffGpRegCode = kMaxGpRegCode + 1;
static constexpr int kAfterMaxLiftoffFpRegCode = static constexpr int kAfterMaxLiftoffFpRegCode =
kAfterMaxLiftoffGpRegCode + kMaxFpRegCode + 1; kAfterMaxLiftoffGpRegCode + kMaxFpRegCode + 1;
static constexpr int kAfterMaxLiftoffRegCode = kAfterMaxLiftoffFpRegCode; static constexpr int kAfterMaxLiftoffRegCode = kAfterMaxLiftoffFpRegCode;
static_assert(kAfterMaxLiftoffRegCode < 256, static constexpr int kBitsPerLiftoffRegCode =
"liftoff register codes can be stored in one uint8_t"); 32 - base::bits::CountLeadingZeros<uint32_t>(kAfterMaxLiftoffRegCode - 1);
static constexpr int kBitsPerGpRegCode =
32 - base::bits::CountLeadingZeros<uint32_t>(kMaxGpRegCode);
class LiftoffRegister { class LiftoffRegister {
static constexpr int needed_bits =
kNeedI64RegPair ? 1 + 2 * kBitsPerGpRegCode : kBitsPerLiftoffRegCode;
using storage_t = std::conditional<
needed_bits <= 8, uint8_t,
std::conditional<needed_bits <= 16, uint16_t, uint32_t>::type>::type;
static_assert(8 * sizeof(storage_t) >= needed_bits &&
8 * sizeof(storage_t) < 2 * needed_bits,
"right type has been chosen");
public: public:
explicit LiftoffRegister(Register reg) : LiftoffRegister(reg.code()) { explicit LiftoffRegister(Register reg) : LiftoffRegister(reg.code()) {
DCHECK_EQ(reg, gp()); DCHECK_EQ(reg, gp());
...@@ -65,6 +81,7 @@ class LiftoffRegister { ...@@ -65,6 +81,7 @@ class LiftoffRegister {
static LiftoffRegister from_liftoff_code(int code) { static LiftoffRegister from_liftoff_code(int code) {
DCHECK_LE(0, code); DCHECK_LE(0, code);
DCHECK_GT(kAfterMaxLiftoffRegCode, code); DCHECK_GT(kAfterMaxLiftoffRegCode, code);
DCHECK_EQ(code, static_cast<storage_t>(code));
return LiftoffRegister(code); return LiftoffRegister(code);
} }
...@@ -79,12 +96,40 @@ class LiftoffRegister { ...@@ -79,12 +96,40 @@ class LiftoffRegister {
} }
} }
static LiftoffRegister ForPair(LiftoffRegister reg1, LiftoffRegister reg2) {
DCHECK(kNeedI64RegPair);
DCHECK_NE(reg1, reg2);
storage_t combined_code = reg1.liftoff_code() |
reg2.liftoff_code() << kBitsPerGpRegCode |
1 << (2 * kBitsPerGpRegCode);
return LiftoffRegister(combined_code);
}
constexpr bool is_pair() const {
return kNeedI64RegPair && (code_ & (1 << (2 * kBitsPerGpRegCode))) != 0;
}
constexpr bool is_gp() const { return code_ < kAfterMaxLiftoffGpRegCode; } constexpr bool is_gp() const { return code_ < kAfterMaxLiftoffGpRegCode; }
constexpr bool is_fp() const { constexpr bool is_fp() const {
return code_ >= kAfterMaxLiftoffGpRegCode && return code_ >= kAfterMaxLiftoffGpRegCode &&
code_ < kAfterMaxLiftoffFpRegCode; code_ < kAfterMaxLiftoffFpRegCode;
} }
LiftoffRegister low() const { return LiftoffRegister(low_gp()); }
LiftoffRegister high() const { return LiftoffRegister(high_gp()); }
Register low_gp() const {
DCHECK(is_pair());
static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
return Register::from_code(code_ & kCodeMask);
}
Register high_gp() const {
DCHECK(is_pair());
static constexpr storage_t kCodeMask = (1 << kBitsPerGpRegCode) - 1;
return Register::from_code((code_ >> kBitsPerGpRegCode) & kCodeMask);
}
Register gp() const { Register gp() const {
DCHECK(is_gp()); DCHECK(is_gp());
return Register::from_code(code_); return Register::from_code(code_);
...@@ -95,31 +140,46 @@ class LiftoffRegister { ...@@ -95,31 +140,46 @@ class LiftoffRegister {
return DoubleRegister::from_code(code_ - kAfterMaxLiftoffGpRegCode); return DoubleRegister::from_code(code_ - kAfterMaxLiftoffGpRegCode);
} }
int liftoff_code() const { return code_; } uint32_t liftoff_code() const {
DCHECK(is_gp() || is_fp());
return code_;
}
RegClass reg_class() const { RegClass reg_class() const {
DCHECK(is_gp() || is_fp()); return is_pair() ? kGpRegPair : is_gp() ? kGpReg : kFpReg;
return is_gp() ? kGpReg : kFpReg;
} }
bool operator==(const LiftoffRegister other) const { bool operator==(const LiftoffRegister other) const {
DCHECK_EQ(is_pair(), other.is_pair());
return code_ == other.code_; return code_ == other.code_;
} }
bool operator!=(const LiftoffRegister other) const { bool operator!=(const LiftoffRegister other) const {
DCHECK_EQ(is_pair(), other.is_pair());
return code_ != other.code_; return code_ != other.code_;
} }
bool overlaps(const LiftoffRegister other) const {
if (is_pair()) return low().overlaps(other) || high().overlaps(other);
if (other.is_pair()) return *this == other.low() || *this == other.high();
return *this == other;
}
private: private:
uint8_t code_; storage_t code_;
explicit constexpr LiftoffRegister(uint8_t code) : code_(code) {} explicit constexpr LiftoffRegister(storage_t code) : code_(code) {}
}; };
static_assert(IS_TRIVIALLY_COPYABLE(LiftoffRegister), static_assert(IS_TRIVIALLY_COPYABLE(LiftoffRegister),
"LiftoffRegister can efficiently be passed by value"); "LiftoffRegister can efficiently be passed by value");
inline std::ostream& operator<<(std::ostream& os, LiftoffRegister reg) { inline std::ostream& operator<<(std::ostream& os, LiftoffRegister reg) {
return reg.is_gp() ? os << "gp" << reg.gp().code() if (reg.is_pair()) {
: os << "fp" << reg.fp().code(); return os << "<gp" << reg.low_gp().code() << "+" << reg.high_gp().code()
<< ">";
} else if (reg.is_gp()) {
return os << "gp" << reg.gp().code();
} else {
return os << "fp" << reg.fp().code();
}
} }
class LiftoffRegList { class LiftoffRegList {
...@@ -142,16 +202,30 @@ class LiftoffRegList { ...@@ -142,16 +202,30 @@ class LiftoffRegList {
} }
LiftoffRegister set(LiftoffRegister reg) { LiftoffRegister set(LiftoffRegister reg) {
regs_ |= storage_t{1} << reg.liftoff_code(); if (reg.is_pair()) {
regs_ |= storage_t{1} << reg.low().liftoff_code();
regs_ |= storage_t{1} << reg.high().liftoff_code();
} else {
regs_ |= storage_t{1} << reg.liftoff_code();
}
return reg; return reg;
} }
LiftoffRegister clear(LiftoffRegister reg) { LiftoffRegister clear(LiftoffRegister reg) {
regs_ &= ~(storage_t{1} << reg.liftoff_code()); if (reg.is_pair()) {
regs_ &= ~(storage_t{1} << reg.low().liftoff_code());
regs_ &= ~(storage_t{1} << reg.high().liftoff_code());
} else {
regs_ &= ~(storage_t{1} << reg.liftoff_code());
}
return reg; return reg;
} }
bool has(LiftoffRegister reg) const { bool has(LiftoffRegister reg) const {
if (reg.is_pair()) {
DCHECK_EQ(has(reg.low()), has(reg.high()));
reg = reg.low();
}
return (regs_ & (storage_t{1} << reg.liftoff_code())) != 0; return (regs_ & (storage_t{1} << reg.liftoff_code())) != 0;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment