[Liftoff] Support i64 parameters and returns

Add support for retrieving i64 parameters and returning them from a
liftoff function.

R=ahaas@chromium.org

Bug: v8:6600
Change-Id: I407b6e3cde6967bacc59d8c3a54e0d5798164d18
Reviewed-on: https://chromium-review.googlesource.com/909215
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51214}
This commit is contained in:
Clemens Hammacher 2018-02-09 15:39:35 +01:00 committed by Commit Bot
parent f118d952d2
commit 720068036e
3 changed files with 84 additions and 63 deletions

View File

@ -347,6 +347,13 @@ void LiftoffAssembler::Spill(uint32_t index, WasmValue value) {
case kWasmI32:
mov(dst, Immediate(value.to_i32()));
break;
case kWasmI64: {
int32_t low_word = value.to_i64();
int32_t high_word = value.to_i64() >> 32;
mov(dst, Immediate(low_word));
mov(liftoff::GetHalfStackSlot(2 * index + 1), Immediate(high_word));
break;
}
case kWasmF32:
mov(dst, Immediate(value.to_f32_boxed().get_bits()));
break;

View File

@ -208,7 +208,7 @@ class StackTransferRecipe {
DCHECK_EQ(kWasmI64, src.type());
switch (src.loc()) {
case VarState::kStack:
LoadI64HalfStackSlot(dst, 2 * index + half == kLowWord ? 0 : 1);
LoadI64HalfStackSlot(dst, 2 * index + (half == kLowWord ? 0 : 1));
break;
case VarState::kRegister: {
LiftoffRegister src_half =
@ -566,15 +566,22 @@ void LiftoffAssembler::PrepareCall(wasm::FunctionSig* sig,
void LiftoffAssembler::FinishCall(wasm::FunctionSig* sig,
compiler::CallDescriptor* call_desc) {
size_t return_count = call_desc->ReturnCount();
DCHECK_EQ(return_count, sig->return_count());
const size_t return_count = sig->return_count();
if (return_count != 0) {
DCHECK_EQ(1, return_count);
compiler::LinkageLocation return_loc = call_desc->GetReturnLocation(0);
int return_reg_code = return_loc.AsRegister();
ValueType return_type = sig->GetReturn(0);
LiftoffRegister return_reg =
LiftoffRegister::from_code(reg_class_for(return_type), return_reg_code);
const bool need_pair = kNeedI64RegPair && return_type == kWasmI64;
DCHECK_EQ(need_pair ? 2 : 1, call_desc->ReturnCount());
RegClass rc = need_pair ? kGpReg : reg_class_for(return_type);
LiftoffRegister return_reg = LiftoffRegister::from_code(
rc, call_desc->GetReturnLocation(0).AsRegister());
DCHECK(GetCacheRegList(rc).has(return_reg));
if (need_pair) {
LiftoffRegister high_reg = LiftoffRegister::from_code(
rc, call_desc->GetReturnLocation(1).AsRegister());
DCHECK(GetCacheRegList(rc).has(high_reg));
return_reg = LiftoffRegister::ForPair(return_reg, high_reg);
}
DCHECK(!cache_state_.is_used(return_reg));
PushRegister(return_type, return_reg);
}

View File

@ -70,10 +70,14 @@ wasm::WasmValue WasmPtrValue(void* ptr) {
return WasmPtrValue(reinterpret_cast<uintptr_t>(ptr));
}
compiler::CallDescriptor* GetLoweredCallDescriptor(
Zone* zone, compiler::CallDescriptor* call_desc) {
return kPointerSize == 4 ? compiler::GetI32WasmCallDescriptor(zone, call_desc)
: call_desc;
}
constexpr ValueType kTypesArr_ilf[] = {kWasmI32, kWasmI64, kWasmF32};
constexpr ValueType kTypesArr_if[] = {kWasmI32, kWasmF32};
constexpr Vector<const ValueType> kTypes_ilf = ArrayVector(kTypesArr_ilf);
constexpr Vector<const ValueType> kTypes_if = ArrayVector(kTypesArr_if);
class LiftoffCompiler {
public:
@ -128,7 +132,7 @@ class LiftoffCompiler {
protected_instructions,
Zone* compilation_zone, std::unique_ptr<Zone>* codegen_zone)
: asm_(liftoff_asm),
call_desc_(call_desc),
call_desc_(GetLoweredCallDescriptor(compilation_zone, call_desc)),
env_(env),
min_size_(uint64_t{env_->module->initial_pages} * wasm::kWasmPageSize),
max_size_(uint64_t{env_->module->has_maximum_pages
@ -204,43 +208,48 @@ class LiftoffCompiler {
}
}
void ProcessParameter(uint32_t param_idx, uint32_t input_location) {
ValueType type = __ local_type(param_idx);
RegClass rc = reg_class_for(type);
compiler::LinkageLocation param_loc =
call_desc_->GetInputLocation(input_location);
if (param_loc.IsRegister()) {
DCHECK(!param_loc.IsAnyRegister());
int reg_code = param_loc.AsRegister();
RegList cache_regs = rc == kGpReg ? kLiftoffAssemblerGpCacheRegs
: kLiftoffAssemblerFpCacheRegs;
if (cache_regs & (1 << reg_code)) {
// This is a cache register, just use it.
LiftoffRegister reg =
rc == kGpReg ? LiftoffRegister(Register::from_code(reg_code))
: LiftoffRegister(DoubleRegister::from_code(reg_code));
__ PushRegister(type, reg);
return;
// Returns the number of inputs processed (1 or 2).
uint32_t ProcessParameter(ValueType type, uint32_t input_idx) {
const int num_lowered_params = 1 + (kNeedI64RegPair && type == kWasmI64);
// Initialize to anything, will be set in the loop and used afterwards.
LiftoffRegister reg = LiftoffRegister::from_code(kGpReg, 0);
RegClass rc = num_lowered_params == 1 ? reg_class_for(type) : kGpReg;
LiftoffRegList pinned;
for (int pair_idx = 0; pair_idx < num_lowered_params; ++pair_idx) {
compiler::LinkageLocation param_loc =
call_desc_->GetInputLocation(input_idx + pair_idx);
// Initialize to anything, will be set in both arms of the if.
LiftoffRegister in_reg = LiftoffRegister::from_code(kGpReg, 0);
if (param_loc.IsRegister()) {
DCHECK(!param_loc.IsAnyRegister());
int reg_code = param_loc.AsRegister();
RegList cache_regs = rc == kGpReg ? kLiftoffAssemblerGpCacheRegs
: kLiftoffAssemblerFpCacheRegs;
if (cache_regs & (1 << reg_code)) {
// This is a cache register, just use it.
in_reg = LiftoffRegister::from_code(rc, reg_code);
} else {
// Move to a cache register (spill one if necessary).
// Note that we cannot create a {LiftoffRegister} for reg_code, since
// {LiftoffRegister} can only store cache regs.
LiftoffRegister in_reg = __ GetUnusedRegister(rc, pinned);
if (rc == kGpReg) {
__ Move(in_reg.gp(), Register::from_code(reg_code), type);
} else {
__ Move(in_reg.fp(), DoubleRegister::from_code(reg_code), type);
}
}
} else if (param_loc.IsCallerFrameSlot()) {
in_reg = __ GetUnusedRegister(rc, pinned);
ValueType lowered_type = num_lowered_params == 1 ? type : kWasmI32;
__ LoadCallerFrameSlot(in_reg, -param_loc.AsCallerFrameSlot(),
lowered_type);
}
// Move to a cache register.
// Note that we cannot create a {LiftoffRegister} for reg_code, since
// {LiftoffRegister} can only store cache regs.
LiftoffRegister cache_reg = __ GetUnusedRegister(rc);
if (rc == kGpReg) {
__ Move(cache_reg.gp(), Register::from_code(reg_code), type);
} else {
__ Move(cache_reg.fp(), DoubleRegister::from_code(reg_code), type);
}
__ PushRegister(type, cache_reg);
return;
reg = pair_idx == 0 ? in_reg : LiftoffRegister::ForPair(reg, in_reg);
pinned.set(reg);
}
if (param_loc.IsCallerFrameSlot()) {
LiftoffRegister tmp_reg = __ GetUnusedRegister(rc);
__ LoadCallerFrameSlot(tmp_reg, -param_loc.AsCallerFrameSlot(), type);
__ PushRegister(type, tmp_reg);
return;
}
UNREACHABLE();
__ PushRegister(type, reg);
return num_lowered_params;
}
void StackCheck(wasm::WasmCodePosition position) {
@ -262,9 +271,9 @@ class LiftoffCompiler {
__ ReserveStackSpace(__ GetTotalFrameSlotCount());
// Parameter 0 is the wasm context.
uint32_t num_params =
static_cast<uint32_t>(call_desc_->ParameterCount()) - 1;
static_cast<uint32_t>(decoder->sig_->parameter_count());
for (uint32_t i = 0; i < __ num_locals(); ++i) {
if (!CheckSupportedType(decoder, kTypes_if, __ local_type(i), "param"))
if (!CheckSupportedType(decoder, kTypes_ilf, __ local_type(i), "param"))
return;
}
// Input 0 is the call target, the context is at 1.
@ -276,20 +285,25 @@ class LiftoffCompiler {
DCHECK(!context_loc.IsAnyRegister());
Register context_reg = Register::from_code(context_loc.AsRegister());
__ SpillContext(context_reg);
uint32_t param_idx = 0;
for (; param_idx < num_params; ++param_idx) {
constexpr int kFirstActualParameterIndex = kContextParameterIndex + 1;
ProcessParameter(param_idx, param_idx + kFirstActualParameterIndex);
// Input 0 is the code target, 1 is the context. First parameter at 2.
uint32_t input_idx = kContextParameterIndex + 1;
for (uint32_t param_idx = 0; param_idx < num_params; ++param_idx) {
input_idx += ProcessParameter(__ local_type(param_idx), input_idx);
}
DCHECK_EQ(input_idx, call_desc_->InputCount());
// Set to a gp register, to mark this uninitialized.
LiftoffRegister zero_double_reg(Register::from_code<0>());
DCHECK(zero_double_reg.is_gp());
for (; param_idx < __ num_locals(); ++param_idx) {
for (uint32_t param_idx = num_params; param_idx < __ num_locals();
++param_idx) {
ValueType type = decoder->GetLocalType(param_idx);
switch (type) {
case kWasmI32:
__ cache_state()->stack_state.emplace_back(kWasmI32, uint32_t{0});
break;
case kWasmI64:
__ cache_state()->stack_state.emplace_back(kWasmI64, uint32_t{0});
break;
case kWasmF32:
if (zero_double_reg.is_gp()) {
// Note: This might spill one of the registers used to hold
@ -309,7 +323,6 @@ class LiftoffCompiler {
// is never a position of any instruction in the function.
StackCheck(0);
DCHECK_EQ(__ num_locals(), param_idx);
DCHECK_EQ(__ num_locals(), __ cache_state()->stack_height());
CheckStackSizeLimit(decoder);
}
@ -1027,16 +1040,13 @@ class LiftoffCompiler {
if (operand.sig->return_count() > 1)
return unsupported(decoder, "multi-return");
if (operand.sig->return_count() == 1 &&
!CheckSupportedType(decoder, kTypes_if, operand.sig->GetReturn(0),
!CheckSupportedType(decoder, kTypes_ilf, operand.sig->GetReturn(0),
"return"))
return;
compiler::CallDescriptor* call_desc =
compiler::GetWasmCallDescriptor(compilation_zone_, operand.sig);
if (kPointerSize == 4) {
call_desc =
compiler::GetI32WasmCallDescriptor(compilation_zone_, call_desc);
}
call_desc = GetLoweredCallDescriptor(compilation_zone_, call_desc);
uint32_t max_used_spill_slot = 0;
__ PrepareCall(operand.sig, call_desc, &max_used_spill_slot);
@ -1071,7 +1081,7 @@ class LiftoffCompiler {
if (operand.sig->return_count() > 1)
return unsupported(decoder, "multi-return");
if (operand.sig->return_count() == 1 &&
!CheckSupportedType(decoder, kTypes_if, operand.sig->GetReturn(0),
!CheckSupportedType(decoder, kTypes_ilf, operand.sig->GetReturn(0),
"return"))
return;
@ -1164,10 +1174,7 @@ class LiftoffCompiler {
compiler::CallDescriptor* call_desc =
compiler::GetWasmCallDescriptor(compilation_zone_, operand.sig);
if (kPointerSize == 4) {
call_desc =
compiler::GetI32WasmCallDescriptor(compilation_zone_, call_desc);
}
call_desc = GetLoweredCallDescriptor(compilation_zone_, call_desc);
uint32_t max_used_spill_slot = 0;
__ CallIndirect(operand.sig, call_desc, scratch.gp(), &max_used_spill_slot);