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 {
}
void inc_used(LiftoffRegister reg) {
if (reg.is_pair()) {
inc_used(reg.low());
inc_used(reg.high());
return;
}
used_registers.set(reg);
DCHECK_GT(kMaxInt, register_use_count[reg.liftoff_code()]);
++register_use_count[reg.liftoff_code()];
}
// Returns whether this was the last use.
bool dec_used(LiftoffRegister reg) {
void dec_used(LiftoffRegister reg) {
DCHECK(is_used(reg));
if (reg.is_pair()) {
dec_used(reg.low());
dec_used(reg.high());
return;
}
int code = reg.liftoff_code();
DCHECK_LT(0, register_use_count[code]);
if (--register_use_count[code] != 0) return false;
used_registers.clear(reg);
return true;
if (--register_use_count[code] == 0) used_registers.clear(reg);
}
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);
DCHECK_EQ(used, register_use_count[reg.liftoff_code()] != 0);
return used;
......@@ -237,7 +249,12 @@ class LiftoffAssembler : public TurboAssembler {
// Get an unused register for class {rc}, potentially spilling to free one.
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);
return GetUnusedRegister(candidates, pinned);
}
......
......@@ -202,8 +202,7 @@ class LiftoffCompiler {
LiftoffRegister reg =
rc == kGpReg ? LiftoffRegister(Register::from_code(reg_code))
: LiftoffRegister(DoubleRegister::from_code(reg_code));
LiftoffRegList cache_regs =
rc == kGpReg ? kGpCacheRegList : kFpCacheRegList;
LiftoffRegList cache_regs = GetCacheRegList(rc);
if (cache_regs.has(reg)) {
// This is a cache register, just use it.
__ PushRegister(type, reg);
......
......@@ -16,24 +16,27 @@ namespace v8 {
namespace internal {
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.
static inline constexpr RegClass reg_class_for(ValueType type) {
return type == kWasmI32 || type == kWasmI64 // int types
? kGpReg
: type == kWasmF32 || type == kWasmF64 // float types
? kFpReg
: kNoReg; // other (unsupported) types
return kNeedI64RegPair && type == kWasmI64 // i64 on 32 bit
? kGpRegPair
: type == kWasmI32 || type == kWasmI64 // int types
? kGpReg
: 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.
static constexpr int kMaxGpRegCode =
8 * sizeof(kLiftoffAssemblerGpCacheRegs) -
......@@ -45,14 +48,27 @@ static constexpr int kMaxFpRegCode =
// LiftoffRegister encodes both gp and fp in a unified index space.
// [0 .. kMaxGpRegCode] encodes gp 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 kAfterMaxLiftoffFpRegCode =
kAfterMaxLiftoffGpRegCode + kMaxFpRegCode + 1;
static constexpr int kAfterMaxLiftoffRegCode = kAfterMaxLiftoffFpRegCode;
static_assert(kAfterMaxLiftoffRegCode < 256,
"liftoff register codes can be stored in one uint8_t");
static constexpr int kBitsPerLiftoffRegCode =
32 - base::bits::CountLeadingZeros<uint32_t>(kAfterMaxLiftoffRegCode - 1);
static constexpr int kBitsPerGpRegCode =
32 - base::bits::CountLeadingZeros<uint32_t>(kMaxGpRegCode);
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:
explicit LiftoffRegister(Register reg) : LiftoffRegister(reg.code()) {
DCHECK_EQ(reg, gp());
......@@ -65,6 +81,7 @@ class LiftoffRegister {
static LiftoffRegister from_liftoff_code(int code) {
DCHECK_LE(0, code);
DCHECK_GT(kAfterMaxLiftoffRegCode, code);
DCHECK_EQ(code, static_cast<storage_t>(code));
return LiftoffRegister(code);
}
......@@ -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_fp() const {
return code_ >= kAfterMaxLiftoffGpRegCode &&
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 {
DCHECK(is_gp());
return Register::from_code(code_);
......@@ -95,31 +140,46 @@ class LiftoffRegister {
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 {
DCHECK(is_gp() || is_fp());
return is_gp() ? kGpReg : kFpReg;
return is_pair() ? kGpRegPair : is_gp() ? kGpReg : kFpReg;
}
bool operator==(const LiftoffRegister other) const {
DCHECK_EQ(is_pair(), other.is_pair());
return code_ == other.code_;
}
bool operator!=(const LiftoffRegister other) const {
DCHECK_EQ(is_pair(), other.is_pair());
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:
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),
"LiftoffRegister can efficiently be passed by value");
inline std::ostream& operator<<(std::ostream& os, LiftoffRegister reg) {
return reg.is_gp() ? os << "gp" << reg.gp().code()
: os << "fp" << reg.fp().code();
if (reg.is_pair()) {
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 {
......@@ -142,16 +202,30 @@ class LiftoffRegList {
}
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;
}
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;
}
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;
}
......
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