Commit 7f7545cf authored by Ben L. Titzer's avatar Ben L. Titzer Committed by Commit Bot

[wasm] Introduce WasmImportCallKind enum

The WASM engine compiles per-import wrappers for callables imported
into a WASM instance that have one of a number of different shapes,
depending on the type of the imported function and whether there is
a signature match. This CL introduces an enum with a value for each
case in preparation for introducing a per-kind cache.

R=mstarzinger@chromium.org
CC=clemensh@chromium.org

Change-Id: If9b7355ff7c57a329c096f93f3624bc3d6c74e3f
Reviewed-on: https://chromium-review.googlesource.com/1243045
Commit-Queue: Ben Titzer <titzer@chromium.org>
Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#56210}
parent 9f7ec7d4
......@@ -2575,7 +2575,7 @@ Node* WasmGraphBuilder::BuildWasmCall(wasm::FunctionSig* sig, Node** args,
return call;
}
Node* WasmGraphBuilder::BuildImportWasmCall(wasm::FunctionSig* sig, Node** args,
Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
int func_index) {
......@@ -2596,7 +2596,7 @@ Node* WasmGraphBuilder::BuildImportWasmCall(wasm::FunctionSig* sig, Node** args,
untrusted_code_mitigations_ ? kRetpoline : kNoRetpoline);
}
Node* WasmGraphBuilder::BuildImportWasmCall(wasm::FunctionSig* sig, Node** args,
Node* WasmGraphBuilder::BuildImportCall(wasm::FunctionSig* sig, Node** args,
Node*** rets,
wasm::WasmCodePosition position,
Node* func_index) {
......@@ -2635,7 +2635,7 @@ Node* WasmGraphBuilder::CallDirect(uint32_t index, Node** args, Node*** rets,
if (env_ && index < env_->module->num_imported_functions) {
// Call to an imported function.
return BuildImportWasmCall(sig, args, rets, position, index);
return BuildImportCall(sig, args, rets, position, index);
}
// A direct call to a wasm function defined in this module.
......@@ -4461,8 +4461,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
// Load function index from {WasmExportedFunctionData}.
Node* function_index =
BuildLoadFunctionIndexFromExportedFunctionData(function_data);
BuildImportWasmCall(sig_, args, &rets, wasm::kNoCodePosition,
function_index);
BuildImportCall(sig_, args, &rets, wasm::kNoCodePosition, function_index);
} else {
// Call to a wasm function defined in this module.
// The call target is the jump table slot for that function.
......@@ -4486,9 +4485,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
Return(jsval);
}
bool BuildWasmToJSWrapper(Handle<JSReceiver> target, int index) {
DCHECK(target->IsCallable());
bool BuildWasmImportCallWrapper(WasmImportCallKind kind, int func_index,
int formal_param_count) {
int wasm_count = static_cast<int>(sig_->parameter_count());
// Build the start and the parameter nodes.
......@@ -4497,16 +4495,13 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
// Create the instance_node from the passed parameter.
instance_node_.set(Param(wasm::kWasmInstanceParameterIndex));
Node* callables_node = LOAD_INSTANCE_FIELD(ImportedFunctionCallables,
MachineType::TaggedPointer());
Node* callable_node = LOAD_FIXED_ARRAY_SLOT(callables_node, index);
Node* undefined_node =
LOAD_INSTANCE_FIELD(UndefinedValue, MachineType::TaggedPointer());
Node* native_context =
LOAD_INSTANCE_FIELD(NativeContext, MachineType::TaggedPointer());
if (!wasm::IsJSCompatibleSignature(sig_)) {
// Throw a TypeError.
if (kind == WasmImportCallKind::kRuntimeTypeError) {
// =======================================================================
// === Runtime TypeError =================================================
// =======================================================================
BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError,
native_context, nullptr, 0);
// We don't need to return a value here, as the runtime call will not
......@@ -4515,29 +4510,36 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
return false;
}
CallDescriptor* call_descriptor;
Node** args = Buffer(wasm_count + 9);
Node* call = nullptr;
BuildModifyThreadInWasmFlag(false);
Node* callables_node = LOAD_INSTANCE_FIELD(ImportedFunctionCallables,
MachineType::TaggedPointer());
Node* callable_node = LOAD_FIXED_ARRAY_SLOT(callables_node, func_index);
Node* undefined_node =
LOAD_INSTANCE_FIELD(UndefinedValue, MachineType::TaggedPointer());
if (target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(target);
Node* call = nullptr;
bool sloppy_receiver = true;
BuildModifyThreadInWasmFlag(false); // exiting WASM via call.
switch (kind) {
// =======================================================================
// === JS Functions with matching arity ==================================
// =======================================================================
case WasmImportCallKind::kJSFunctionArityMatch:
sloppy_receiver = false;
V8_FALLTHROUGH; // fallthru
case WasmImportCallKind::kJSFunctionArityMatchSloppy: {
Node** args = Buffer(wasm_count + 9);
int pos = 0;
Node* function_context = SetEffect(graph()->NewNode(
mcgraph()->machine()->Load(MachineType::TaggedPointer()),
callable_node,
mcgraph()->Int32Constant(
wasm::ObjectAccess::ContextOffsetInTaggedJSFunction()),
Effect(), Control()));
if (!IsClassConstructor(function->shared()->kind())) {
if (function->shared()->internal_formal_parameter_count() ==
wasm_count) {
int pos = 0;
args[pos++] = callable_node; // target callable.
// Receiver.
if (is_sloppy(function->shared()->language_mode()) &&
!function->shared()->native()) {
if (sloppy_receiver) {
Node* global_proxy = LOAD_FIXED_ARRAY_SLOT(
native_context, Context::GLOBAL_PROXY_INDEX);
args[pos++] = global_proxy;
......@@ -4545,7 +4547,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
args[pos++] = undefined_node;
}
call_descriptor = Linkage::GetJSCallDescriptor(
auto call_descriptor = Linkage::GetJSCallDescriptor(
graph()->zone(), false, wasm_count + 1, CallDescriptor::kNoFlags);
// Convert wasm numbers to JS values.
......@@ -4557,20 +4559,33 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
args[pos++] = Effect();
args[pos++] = Control();
call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor),
pos, args);
} else if (function->shared()->internal_formal_parameter_count() >= 0) {
call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), pos,
args);
break;
}
// =======================================================================
// === JS Functions with arguments adapter ===============================
// =======================================================================
case WasmImportCallKind::kJSFunctionArityMismatch:
sloppy_receiver = false;
V8_FALLTHROUGH; // fallthru
case WasmImportCallKind::kJSFunctionArityMismatchSloppy: {
Node** args = Buffer(wasm_count + 9);
int pos = 0;
args[pos++] = BuildLoadBuiltinFromInstance(
Builtins::kArgumentsAdaptorTrampoline);
Node* function_context = SetEffect(graph()->NewNode(
mcgraph()->machine()->Load(MachineType::TaggedPointer()),
callable_node,
mcgraph()->Int32Constant(
wasm::ObjectAccess::ContextOffsetInTaggedJSFunction()),
Effect(), Control()));
args[pos++] =
BuildLoadBuiltinFromInstance(Builtins::kArgumentsAdaptorTrampoline);
args[pos++] = callable_node; // target callable
args[pos++] = undefined_node; // new target
args[pos++] = mcgraph()->Int32Constant(wasm_count); // argument count
args[pos++] = mcgraph()->Int32Constant(
function->shared()->internal_formal_parameter_count());
args[pos++] = mcgraph()->Int32Constant(formal_param_count);
// Receiver.
if (is_sloppy(function->shared()->language_mode()) &&
!function->shared()->native()) {
if (sloppy_receiver) {
Node* global_proxy = LOAD_FIXED_ARRAY_SLOT(
native_context, Context::GLOBAL_PROXY_INDEX);
args[pos++] = global_proxy;
......@@ -4578,7 +4593,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
args[pos++] = undefined_node;
}
call_descriptor = Linkage::GetStubCallDescriptor(
auto call_descriptor = Linkage::GetStubCallDescriptor(
mcgraph()->zone(), ArgumentsAdaptorDescriptor{}, 1 + wasm_count,
CallDescriptor::kNoFlags, Operator::kNoProperties);
......@@ -4587,14 +4602,15 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
args[pos++] = function_context;
args[pos++] = Effect();
args[pos++] = Control();
call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor),
pos, args);
}
}
call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), pos,
args);
break;
}
// We cannot call the target directly, we have to use the Call builtin.
if (!call) {
// =======================================================================
// === General case of unknown callable ==================================
// =======================================================================
case WasmImportCallKind::kUseCallBuiltin: {
Node** args = Buffer(wasm_count + 9);
int pos = 0;
args[pos++] = mcgraph()->RelocatableIntPtrConstant(
wasm::WasmCode::kWasmCallJavaScript, RelocInfo::WASM_STUB_CALL);
......@@ -4602,7 +4618,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
args[pos++] = mcgraph()->Int32Constant(wasm_count); // argument count
args[pos++] = undefined_node; // receiver
call_descriptor = Linkage::GetStubCallDescriptor(
auto call_descriptor = Linkage::GetStubCallDescriptor(
graph()->zone(), CallTrampolineDescriptor{}, wasm_count + 1,
CallDescriptor::kNoFlags, Operator::kNoProperties,
StubCallMode::kCallWasmRuntimeStub);
......@@ -4611,17 +4627,22 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
pos = AddArgumentNodes(args, pos, wasm_count, sig_);
// The native_context is sufficient here, because all kind of callables
// which depend on the context provide their own context. The context here
// is only needed if the target is a constructor to throw a TypeError, if
// the target is a native function, or if the target is a callable
// JSObject, which can only be constructed by the runtime.
// which depend on the context provide their own context. The context
// here is only needed if the target is a constructor to throw a
// TypeError, if the target is a native function, or if the target is a
// callable JSObject, which can only be constructed by the runtime.
args[pos++] = native_context;
args[pos++] = Effect();
args[pos++] = Control();
call = graph()->NewNode(mcgraph()->common()->Call(call_descriptor), pos,
args);
break;
}
default:
UNREACHABLE();
}
DCHECK_NOT_NULL(call);
SetEffect(call);
SetSourcePosition(call, 0);
......@@ -4631,7 +4652,7 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
? mcgraph()->Int32Constant(0)
: FromJS(call, native_context, sig_->GetReturn());
BuildModifyThreadInWasmFlag(true);
BuildModifyThreadInWasmFlag(true); // reentering WASM upon return.
Return(val);
return true;
......@@ -4857,12 +4878,55 @@ MaybeHandle<Code> CompileJSToWasmWrapper(Isolate* isolate,
return code;
}
MaybeHandle<Code> CompileWasmToJSWrapper(
Isolate* isolate, Handle<JSReceiver> target, wasm::FunctionSig* sig,
uint32_t index, wasm::ModuleOrigin origin,
WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> target,
wasm::FunctionSig* expected_sig) {
if (WasmExportedFunction::IsWasmExportedFunction(*target)) {
auto imported_function = WasmExportedFunction::cast(*target);
wasm::FunctionSig* imported_sig =
imported_function->instance()
->module()
->functions[imported_function->function_index()]
.sig;
if (*imported_sig != *expected_sig) {
return WasmImportCallKind::kLinkError;
}
return WasmImportCallKind::kWasmToWasm;
}
// Assuming we are calling to JS, check whether this would be a runtime error.
if (!wasm::IsJSCompatibleSignature(expected_sig)) {
return WasmImportCallKind::kRuntimeTypeError;
}
// For JavaScript calls, determine whether the target has an arity match
// and whether it has a sloppy receiver.
if (target->IsJSFunction()) {
Handle<JSFunction> function = Handle<JSFunction>::cast(target);
if (IsClassConstructor(function->shared()->kind())) {
// Class constructor will throw anyway.
return WasmImportCallKind::kUseCallBuiltin;
}
bool sloppy = is_sloppy(function->shared()->language_mode()) &&
!function->shared()->native();
if (function->shared()->internal_formal_parameter_count() ==
expected_sig->parameter_count()) {
return sloppy ? WasmImportCallKind::kJSFunctionArityMatchSloppy
: WasmImportCallKind::kJSFunctionArityMatch;
}
return sloppy ? WasmImportCallKind::kJSFunctionArityMismatchSloppy
: WasmImportCallKind::kJSFunctionArityMismatch;
}
// Unknown case. Use the call builtin.
return WasmImportCallKind::kUseCallBuiltin;
}
MaybeHandle<Code> CompileWasmImportCallWrapper(
Isolate* isolate, WasmImportCallKind kind, Handle<JSReceiver> js_receiver,
wasm::FunctionSig* sig, uint32_t index, wasm::ModuleOrigin origin,
wasm::UseTrapHandler use_trap_handler) {
DCHECK_NE(WasmImportCallKind::kLinkError, kind);
DCHECK_NE(WasmImportCallKind::kWasmToWasm, kind);
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.wasm"),
"CompileWasmToJSWrapper");
"CompileWasmImportCallWrapper");
//----------------------------------------------------------------------------
// Create the Graph
//----------------------------------------------------------------------------
......@@ -4890,7 +4954,13 @@ MaybeHandle<Code> CompileWasmToJSWrapper(
StubCallMode::kCallWasmRuntimeStub);
builder.set_control_ptr(&control);
builder.set_effect_ptr(&effect);
builder.BuildWasmToJSWrapper(target, index);
// TODO(titzer): gen code to load the formal param count from the callable.
int formal_param_count = js_receiver->IsJSFunction()
? JSFunction::cast(*js_receiver)
->shared()
->internal_formal_parameter_count()
: static_cast<int>(sig->parameter_count());
builder.BuildWasmImportCallWrapper(kind, index, formal_param_count);
EmbeddedVector<char, 32> func_name;
func_name.Truncate(SNPrintF(func_name, "wasm-to-js#%d", index));
......
......@@ -68,11 +68,27 @@ class TurbofanWasmCompilationUnit {
DISALLOW_COPY_AND_ASSIGN(TurbofanWasmCompilationUnit);
};
// Wraps a JS function, producing a code object that can be called from wasm.
MaybeHandle<Code> CompileWasmToJSWrapper(Isolate*, Handle<JSReceiver> target,
wasm::FunctionSig*, uint32_t index,
wasm::ModuleOrigin,
wasm::UseTrapHandler);
// Calls to WASM imports are handled in several different ways, depending
// on the type of the target function/callable and whether the signature
// matches the argument arity.
enum class WasmImportCallKind {
kLinkError, // static WASM->WASM type error
kRuntimeTypeError, // runtime WASM->JS type error
kWasmToWasm, // fast WASM->WASM call
kJSFunctionArityMatch, // fast WASM->JS call
kJSFunctionArityMatchSloppy, // fast WASM->JS call, sloppy receiver
kJSFunctionArityMismatch, // WASM->JS, needs adapter frame
kJSFunctionArityMismatchSloppy, // WASM->JS, needs adapter frame, sloppy
kUseCallBuiltin // everything else
};
WasmImportCallKind GetWasmImportCallKind(Handle<JSReceiver> callable,
wasm::FunctionSig* sig);
// Compiles an import call wrapper, which allows WASM to call imports.
MaybeHandle<Code> CompileWasmImportCallWrapper(
Isolate*, WasmImportCallKind, Handle<JSReceiver>, wasm::FunctionSig*,
uint32_t index, wasm::ModuleOrigin, wasm::UseTrapHandler);
// Creates a code object calling a wasm function with the given signature,
// callable from JS.
......@@ -366,9 +382,9 @@ class WasmGraphBuilder {
Node* BuildWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, Node* instance_node,
UseRetpoline use_retpoline);
Node* BuildImportWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
Node* BuildImportCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, int func_index);
Node* BuildImportWasmCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
Node* BuildImportCall(wasm::FunctionSig* sig, Node** args, Node*** rets,
wasm::WasmCodePosition position, Node* func_index);
Node* BuildF32CopySign(Node* left, Node* right);
......
......@@ -1494,33 +1494,31 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
}
uint32_t func_index = import.index;
DCHECK_EQ(num_imported_functions, func_index);
auto js_receiver = Handle<JSReceiver>::cast(value);
FunctionSig* expected_sig = module_->functions[func_index].sig;
if (WasmExportedFunction::IsWasmExportedFunction(*value)) {
// The imported function is a WASM function from another instance.
Handle<WasmExportedFunction> imported_function(
WasmExportedFunction::cast(*value), isolate_);
Handle<WasmInstanceObject> imported_instance(
imported_function->instance(), isolate_);
FunctionSig* imported_sig =
imported_instance->module()
->functions[imported_function->function_index()]
.sig;
if (*imported_sig != *expected_sig) {
auto kind = compiler::GetWasmImportCallKind(js_receiver, expected_sig);
switch (kind) {
case compiler::WasmImportCallKind::kLinkError:
ReportLinkError(
"imported function does not match the expected type", index,
module_name, import_name);
return -1;
}
case compiler::WasmImportCallKind::kWasmToWasm: {
// The imported function is a WASM function from another instance.
auto imported_function = Handle<WasmExportedFunction>::cast(value);
Handle<WasmInstanceObject> imported_instance(
imported_function->instance(), isolate_);
// The import reference is the instance object itself.
Address imported_target = imported_function->GetWasmCallTarget();
ImportedFunctionEntry entry(instance, func_index);
entry.set_wasm_to_wasm(*imported_instance, imported_target);
} else {
break;
}
default: {
// The imported function is a callable.
Handle<JSReceiver> js_receiver(JSReceiver::cast(*value), isolate_);
Handle<Code> wrapper_code =
compiler::CompileWasmToJSWrapper(
isolate_, js_receiver, expected_sig, func_index,
compiler::CompileWasmImportCallWrapper(
isolate_, kind, js_receiver, expected_sig, func_index,
module_->origin, use_trap_handler())
.ToHandleChecked();
RecordStats(*wrapper_code, isolate_->counters());
......@@ -1529,6 +1527,8 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
native_module->AddImportWrapper(wrapper_code, func_index);
ImportedFunctionEntry entry(instance, func_index);
entry.set_wasm_to_js(*js_receiver, wasm_code);
break;
}
}
num_imported_functions++;
break;
......
......@@ -41,8 +41,10 @@ TestingModuleBuilder::TestingModuleBuilder(
if (maybe_import) {
// Manually compile a wasm to JS wrapper and insert it into the instance.
CodeSpaceMemoryModificationScope modification_scope(isolate_->heap());
MaybeHandle<Code> code = compiler::CompileWasmToJSWrapper(
isolate_, maybe_import->js_function, maybe_import->sig,
auto kind = compiler::GetWasmImportCallKind(maybe_import->js_function,
maybe_import->sig);
MaybeHandle<Code> code = compiler::CompileWasmImportCallWrapper(
isolate_, kind, maybe_import->js_function, maybe_import->sig,
maybe_import_index, test_module_->origin,
trap_handler::IsTrapHandlerEnabled() ? kUseTrapHandler
: kNoTrapHandler);
......
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