Commit edfcba04 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] Introduce builtin for grow_memory

This CL adds a builtin which receives an int32 argument and returns the
int32 result. Internally, it checks whether the argument is in the
valid smi range, then calls the runtime function with the smi argument
and converts the return value back from smi to int32.
This saves a lot of code in the wasm TF builder and in Liftoff.

R=mstarzinger@chromium.org

Bug: v8:6600
Change-Id: Icddcb020eae74c767a75090feb4939275432c007
Reviewed-on: https://chromium-review.googlesource.com/1107711
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53946}
parent 7da6adcb
......@@ -1205,8 +1205,9 @@ namespace internal {
TFC(WasmAllocateHeapNumber, AllocateHeapNumber, 1) \
TFC(WasmArgumentsAdaptor, ArgumentAdaptor, 1) \
TFC(WasmCallJavaScript, CallTrampoline, 1) \
TFC(WasmToNumber, TypeConversion, 1) \
TFC(WasmGrowMemory, WasmGrowMemory, 1) \
TFS(WasmStackGuard) \
TFC(WasmToNumber, TypeConversion, 1) \
TFS(ThrowWasmTrapUnreachable) \
TFS(ThrowWasmTrapMemOutOfBounds) \
TFS(ThrowWasmTrapDivByZero) \
......
......@@ -21,8 +21,7 @@ class WasmBuiltinsAssembler : public CodeStubAssembler {
}
TNode<Code> LoadBuiltinFromFrame(Builtins::Name id) {
TNode<Object> instance = UncheckedCast<Object>(
LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset));
TNode<Object> instance = LoadInstanceFromFrame();
TNode<IntPtrT> roots = UncheckedCast<IntPtrT>(
Load(MachineType::Pointer(), instance,
IntPtrConstant(WasmInstanceObject::kRootsArrayAddressOffset -
......@@ -32,6 +31,22 @@ class WasmBuiltinsAssembler : public CodeStubAssembler {
IntPtrConstant(Heap::roots_to_builtins_offset() + id * kPointerSize)));
return target;
}
TNode<Object> LoadInstanceFromFrame() {
return UncheckedCast<Object>(
LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset));
}
TNode<Code> LoadCEntryFromInstance(TNode<Object> instance) {
return UncheckedCast<Code>(
Load(MachineType::AnyTagged(), instance,
IntPtrConstant(WasmInstanceObject::kCEntryStubOffset -
kHeapObjectTag)));
}
TNode<Code> LoadCEntryFromFrame() {
return LoadCEntryFromInstance(LoadInstanceFromFrame());
}
};
TF_BUILTIN(WasmAllocateHeapNumber, WasmBuiltinsAssembler) {
......@@ -67,27 +82,40 @@ TF_BUILTIN(WasmToNumber, WasmBuiltinsAssembler) {
TailCallStub(TypeConversionDescriptor(), target, context, argument);
}
TF_BUILTIN(WasmStackGuard, CodeStubAssembler) {
TNode<Object> instance = UncheckedCast<Object>(
LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset));
TNode<Code> centry = UncheckedCast<Code>(Load(
MachineType::AnyTagged(), instance,
IntPtrConstant(WasmInstanceObject::kCEntryStubOffset - kHeapObjectTag)));
TF_BUILTIN(WasmStackGuard, WasmBuiltinsAssembler) {
TNode<Code> centry = LoadCEntryFromFrame();
TailCallRuntimeWithCEntry(Runtime::kWasmStackGuard, centry,
NoContextConstant());
}
#define DECLARE_ENUM(name) \
TF_BUILTIN(ThrowWasm##name, CodeStubAssembler) { \
TNode<Object> instance = UncheckedCast<Object>( \
LoadFromParentFrame(WasmCompiledFrameConstants::kWasmInstanceOffset)); \
TNode<Code> centry = UncheckedCast<Code>( \
Load(MachineType::AnyTagged(), instance, \
IntPtrConstant(WasmInstanceObject::kCEntryStubOffset - \
kHeapObjectTag))); \
int message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \
TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, \
NoContextConstant(), SmiConstant(message_id)); \
TF_BUILTIN(WasmGrowMemory, WasmBuiltinsAssembler) {
TNode<Int32T> num_pages =
UncheckedCast<Int32T>(Parameter(Descriptor::kNumPages));
Label num_pages_out_of_range(this, Label::kDeferred);
TNode<BoolT> num_pages_fits_in_smi =
IsValidPositiveSmi(ChangeInt32ToIntPtr(num_pages));
GotoIfNot(num_pages_fits_in_smi, &num_pages_out_of_range);
TNode<Smi> num_pages_smi = SmiFromInt32(num_pages);
TNode<Object> instance = LoadInstanceFromFrame();
TNode<Code> centry = LoadCEntryFromInstance(instance);
TNode<Smi> ret_smi = UncheckedCast<Smi>(
CallRuntimeWithCEntry(Runtime::kWasmGrowMemory, centry,
NoContextConstant(), instance, num_pages_smi));
TNode<Int32T> ret = SmiToInt32(ret_smi);
ReturnRaw(ret);
BIND(&num_pages_out_of_range);
ReturnRaw(Int32Constant(-1));
}
#define DECLARE_ENUM(name) \
TF_BUILTIN(ThrowWasm##name, WasmBuiltinsAssembler) { \
TNode<Code> centry = LoadCEntryFromFrame(); \
int message_id = wasm::WasmOpcodes::TrapReasonToMessageId(wasm::k##name); \
TailCallRuntimeWithCEntry(Runtime::kThrowWasmError, centry, \
NoContextConstant(), SmiConstant(message_id)); \
}
FOREACH_WASM_TRAPREASON(DECLARE_ENUM)
#undef DECLARE_ENUM
......
......@@ -409,6 +409,10 @@ void CodeAssembler::ReturnIf(Node* condition, Node* value) {
Bind(&if_continue);
}
void CodeAssembler::ReturnRaw(Node* value) {
return raw_assembler()->Return(value);
}
void CodeAssembler::DebugAbort(Node* message) {
raw_assembler()->DebugAbort(message);
}
......@@ -1072,16 +1076,22 @@ class NodeArray {
TNode<Object> CodeAssembler::CallRuntimeImpl(
Runtime::FunctionId function, TNode<Object> context,
std::initializer_list<TNode<Object>> args) {
int result_size = Runtime::FunctionForId(function)->result_size;
TNode<Code> centry =
HeapConstant(CodeFactory::RuntimeCEntry(isolate(), result_size));
return CallRuntimeWithCEntryImpl(function, centry, context, args);
}
TNode<Object> CodeAssembler::CallRuntimeWithCEntryImpl(
Runtime::FunctionId function, TNode<Code> centry, TNode<Object> context,
std::initializer_list<TNode<Object>> args) {
constexpr size_t kMaxNumArgs = 6;
DCHECK_GE(kMaxNumArgs, args.size());
int argc = static_cast<int>(args.size());
auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
zone(), function, argc, Operator::kNoProperties,
CallDescriptor::kNoFlags);
int return_count = static_cast<int>(call_descriptor->ReturnCount());
Node* centry =
HeapConstant(CodeFactory::RuntimeCEntry(isolate(), return_count));
Node* ref = ExternalConstant(ExternalReference::Create(function));
Node* arity = Int32Constant(argc);
......
......@@ -749,6 +749,8 @@ class V8_EXPORT_PRIVATE CodeAssembler {
void ReturnIf(Node* condition, Node* value);
void ReturnRaw(Node* value);
void DebugAbort(Node* message);
void DebugBreak();
void Unreachable();
......@@ -987,6 +989,14 @@ class V8_EXPORT_PRIVATE CodeAssembler {
{implicit_cast<SloppyTNode<Object>>(args)...});
}
template <class... TArgs>
TNode<Object> CallRuntimeWithCEntry(Runtime::FunctionId function,
TNode<Code> centry,
SloppyTNode<Object> context,
TArgs... args) {
return CallRuntimeWithCEntryImpl(function, centry, context, {args...});
}
template <class... TArgs>
void TailCallRuntime(Runtime::FunctionId function,
SloppyTNode<Object> context, TArgs... args) {
......@@ -1007,7 +1017,7 @@ class V8_EXPORT_PRIVATE CodeAssembler {
void TailCallRuntimeWithCEntry(Runtime::FunctionId function,
TNode<Code> centry, TNode<Object> context,
TArgs... args) {
int argc = static_cast<int>(sizeof...(args));
int argc = sizeof...(args);
TNode<Int32T> arity = Int32Constant(argc);
return TailCallRuntimeWithCEntryImpl(
function, arity, centry, context,
......@@ -1193,6 +1203,10 @@ class V8_EXPORT_PRIVATE CodeAssembler {
TNode<Object> context,
std::initializer_list<TNode<Object>> args);
TNode<Object> CallRuntimeWithCEntryImpl(
Runtime::FunctionId function, TNode<Code> centry, TNode<Object> context,
std::initializer_list<TNode<Object>> args);
void TailCallRuntimeImpl(Runtime::FunctionId function, TNode<Int32T> arity,
TNode<Object> context,
std::initializer_list<TNode<Object>> args);
......
......@@ -2009,28 +2009,26 @@ Node* WasmGraphBuilder::BuildCcallConvertFloat(Node* input,
Node* WasmGraphBuilder::GrowMemory(Node* input) {
SetNeedsStackCheck();
Diamond check_input_range(
graph(), mcgraph()->common(),
graph()->NewNode(mcgraph()->machine()->Uint32LessThanOrEqual(), input,
mcgraph()->Uint32Constant(FLAG_wasm_max_mem_pages)),
BranchHint::kTrue);
check_input_range.Chain(*control_);
Node* parameters[] = {BuildChangeUint31ToSmi(input)};
Node* old_effect = *effect_;
*control_ = check_input_range.if_true;
Node* call = BuildCallToRuntime(Runtime::kWasmGrowMemory, parameters,
arraysize(parameters));
Node* result = BuildChangeSmiToInt32(call);
WasmGrowMemoryDescriptor interface_descriptor;
auto call_descriptor = Linkage::GetStubCallDescriptor(
mcgraph()->zone(), // zone
interface_descriptor, // descriptor
interface_descriptor.GetStackParameterCount(), // stack parameter count
CallDescriptor::kNoFlags, // flags
Operator::kNoProperties, // properties
Linkage::kNoContext, // context specification
StubCallMode::kCallWasmRuntimeStub); // stub call mode
// A direct call to a wasm runtime stub defined in this module.
// Just encode the stub index. This will be patched at relocation.
Node* call_target = mcgraph()->RelocatableIntPtrConstant(
wasm::WasmCode::kWasmGrowMemory, RelocInfo::WASM_STUB_CALL);
Node* call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor),
call_target, input, *effect_, *control_);
result = check_input_range.Phi(MachineRepresentation::kWord32, result,
mcgraph()->Int32Constant(-1));
*effect_ = graph()->NewNode(mcgraph()->common()->EffectPhi(2), *effect_,
old_effect, check_input_range.merge);
*control_ = check_input_range.merge;
return result;
*effect_ = call;
*control_ = call;
return call;
}
uint32_t WasmGraphBuilder::GetExceptionEncodedSize(
......
......@@ -460,14 +460,16 @@ StackFrame::Type StackFrame::ComputeType(const StackFrameIteratorBase* iterator,
iterator->isolate()->wasm_engine()->code_manager()->LookupCode(pc);
if (wasm_code != nullptr) {
switch (wasm_code->kind()) {
case wasm::WasmCode::kInterpreterEntry:
return WASM_INTERPRETER_ENTRY;
case wasm::WasmCode::kFunction:
return WASM_COMPILED;
case wasm::WasmCode::kLazyStub:
return WASM_COMPILE_LAZY;
case wasm::WasmCode::kWasmToJsWrapper:
return WASM_TO_JS;
case wasm::WasmCode::kLazyStub:
return WASM_COMPILE_LAZY;
case wasm::WasmCode::kRuntimeStub:
return STUB;
case wasm::WasmCode::kInterpreterEntry:
return WASM_INTERPRETER_ENTRY;
default:
UNREACHABLE();
}
......
......@@ -269,8 +269,10 @@ void ArrayNArgumentsConstructorDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void WasmGrowMemoryDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
DefaultInitializePlatformSpecific(data, kParameterCount);
}
} // namespace internal
} // namespace v8
......@@ -70,6 +70,7 @@ namespace internal {
V(ResumeGenerator) \
V(FrameDropperTrampoline) \
V(RunMicrotasks) \
V(WasmGrowMemory) \
BUILTIN_LIST_TFS(V)
class V8_EXPORT_PRIVATE CallInterfaceDescriptorData {
......@@ -948,6 +949,14 @@ class RunMicrotasksDescriptor final : public CallInterfaceDescriptor {
DECLARE_DEFAULT_DESCRIPTOR(RunMicrotasksDescriptor, CallInterfaceDescriptor)
};
class WasmGrowMemoryDescriptor final : public CallInterfaceDescriptor {
public:
DEFINE_RESULT_AND_PARAMETERS_NO_CONTEXT(1 /* result size */, kNumPages)
DEFINE_RESULT_AND_PARAMETER_TYPES(MachineType::Int32() /* result */,
MachineType::Int32() /* kNumPages */)
DECLARE_DESCRIPTOR(WasmGrowMemoryDescriptor, CallInterfaceDescriptor)
};
#define DEFINE_TFS_BUILTIN_DESCRIPTOR(Name, ...) \
class Name##Descriptor : public CallInterfaceDescriptor { \
public: \
......
......@@ -67,10 +67,11 @@ class ClearThreadInWasmScope {
RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_UINT32_ARG_CHECKED(delta_pages, 0);
Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
// {delta_pages} is checked to be a positive smi in the WasmGrowMemory builtin
// which calls this runtime function.
CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
// This runtime function is always being called from wasm code.
ClearThreadInWasmScope flag_scope(true);
......
......@@ -576,7 +576,7 @@ namespace internal {
F(WasmExceptionGetElement, 1, 1) \
F(WasmExceptionSetElement, 2, 1) \
F(WasmGetExceptionRuntimeId, 0, 1) \
F(WasmGrowMemory, 1, 1) \
F(WasmGrowMemory, 2, 1) \
F(WasmRunInterpreter, 2, 1) \
F(WasmStackGuard, 0, 1) \
F(WasmThrow, 0, 1) \
......
......@@ -1590,39 +1590,6 @@ class LiftoffCompiler {
__ PushRegister(kWasmI32, mem_size);
}
void Int32ToSmi(LiftoffRegister dst, Register src, Register scratch) {
constexpr int kTotalSmiShift = kSmiTagSize + kSmiShiftSize;
// TODO(clemensh): Shift by immediate directly.
if (SmiValuesAre32Bits()) {
__ LoadConstant(LiftoffRegister(scratch),
WasmValue(int64_t{kTotalSmiShift}));
__ emit_i64_shl(dst, LiftoffRegister(src), scratch);
} else {
DCHECK(SmiValuesAre31Bits());
__ LoadConstant(LiftoffRegister(scratch),
WasmValue(int32_t{kTotalSmiShift}));
__ emit_i32_shl(dst.gp(), src, scratch);
if (kPointerSize == kInt64Size) {
__ emit_i32_to_intptr(dst.gp(), dst.gp());
}
}
}
void SmiToInt32(Register dst, LiftoffRegister src, Register scratch) {
constexpr int kTotalSmiShift = kSmiTagSize + kSmiShiftSize;
// TODO(clemensh): Shift by immediate directly.
if (SmiValuesAre32Bits()) {
__ LoadConstant(LiftoffRegister(scratch),
WasmValue(int64_t{kTotalSmiShift}));
__ emit_i64_shr(LiftoffRegister(dst), src, scratch);
} else {
DCHECK(SmiValuesAre31Bits());
__ LoadConstant(LiftoffRegister(scratch),
WasmValue(int32_t{kTotalSmiShift}));
__ emit_i32_sar(dst, src.gp(), scratch);
}
}
void GrowMemory(Decoder* decoder, const Value& value, Value* result_val) {
// Pop the input, then spill all cache registers to make the runtime call.
LiftoffRegList pinned;
......@@ -1635,28 +1602,23 @@ class LiftoffCompiler {
"complex code here otherwise)");
LiftoffRegister result = pinned.set(LiftoffRegister(kGpReturnReg));
LiftoffRegister tmp_const =
pinned.set(__ cache_state()->unused_register(kGpReg, pinned));
Label done;
Label do_runtime_call;
// TODO(clemensh): Compare to immediate directly.
__ LoadConstant(tmp_const, WasmValue(uint32_t{FLAG_wasm_max_mem_pages}));
__ emit_cond_jump(kUnsignedLessEqual, &do_runtime_call, kWasmI32,
input.gp(), tmp_const.gp());
__ LoadConstant(result, WasmValue(int32_t{-1}));
__ emit_jump(&done);
// TODO(clemensh): Introduce new builtin for smi-conversion, runtime call,
// and conversion back. Use in TF and here.
__ bind(&do_runtime_call);
LiftoffRegister input_smi = input;
Int32ToSmi(input_smi, input.gp(), tmp_const.gp());
Register args[] = {input_smi.gp()};
GenerateRuntimeCall(Runtime::kWasmGrowMemory, arraysize(args), args);
SmiToInt32(result.gp(), result, tmp_const.gp());
__ bind(&done);
WasmGrowMemoryDescriptor descriptor;
DCHECK_EQ(0, descriptor.GetStackParameterCount());
DCHECK_EQ(1, descriptor.GetRegisterParameterCount());
DCHECK_EQ(ValueTypes::MachineTypeFor(kWasmI32),
descriptor.GetParameterType(0));
Register param_reg = descriptor.GetRegisterParameter(0);
if (input.gp() != param_reg) __ Move(param_reg, input.gp(), kWasmI32);
__ CallRuntimeStub(wasm::WasmCode::kWasmGrowMemory);
safepoint_table_builder_.DefineSafepoint(asm_, Safepoint::kSimple, 0,
Safepoint::kNoLazyDeopt);
if (kReturnRegister0 != result.gp()) {
__ Move(result.gp(), kReturnRegister0, kWasmI32);
}
__ PushRegister(kWasmI32, result);
}
......
......@@ -36,6 +36,7 @@ struct WasmModule;
V(WasmAllocateHeapNumber) \
V(WasmArgumentsAdaptor) \
V(WasmCallJavaScript) \
V(WasmGrowMemory) \
V(WasmStackGuard) \
V(WasmToNumber) \
V(DoubleToI)
......
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