[wasm] Introduce a faster param transformation in the js-to-wasm wrapper
This CL introduces a fast path for transforming JavaScript parameters to WebAssembly when calling an exported WebAssembly function from JavaScript. A transformation is considered fast when it does not require a call to a built-in function, thus spilling registers for the call can be avoided. Bug: v8:10943 Change-Id: I5563bfdf77a68bef7ab8afc6d0f4ab2d2ea67bd4 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2418857 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Vicky Kontoura <vkont@google.com> Cr-Commit-Position: refs/heads/master@{#70033}
This commit is contained in:
parent
69ca751bc8
commit
66300a0422
@ -6226,6 +6226,26 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
Node* FromJSFast(Node* input, wasm::ValueType type) {
|
||||
switch (type.kind()) {
|
||||
case wasm::ValueType::kI32:
|
||||
return BuildChangeSmiToInt32(input);
|
||||
case wasm::ValueType::kRef:
|
||||
case wasm::ValueType::kOptRef:
|
||||
case wasm::ValueType::kF32:
|
||||
case wasm::ValueType::kF64:
|
||||
case wasm::ValueType::kI64:
|
||||
case wasm::ValueType::kRtt:
|
||||
case wasm::ValueType::kS128:
|
||||
case wasm::ValueType::kI8:
|
||||
case wasm::ValueType::kI16:
|
||||
case wasm::ValueType::kBottom:
|
||||
case wasm::ValueType::kStmt:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BuildModifyThreadInWasmFlag(bool new_value) {
|
||||
if (!trap_handler::IsTrapHandlerEnabled()) return;
|
||||
Node* isolate_root = BuildLoadIsolateRoot();
|
||||
@ -6296,55 +6316,15 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
|
||||
return SetControl(CALL_BUILTIN(WasmAllocateJSArray, array_length, context));
|
||||
}
|
||||
|
||||
void BuildJSToWasmWrapper(bool is_import) {
|
||||
const int wasm_count = static_cast<int>(sig_->parameter_count());
|
||||
const int rets_count = static_cast<int>(sig_->return_count());
|
||||
|
||||
// Build the start and the JS parameter nodes.
|
||||
SetEffectControl(Start(wasm_count + 5));
|
||||
|
||||
// Create the js_closure and js_context parameters.
|
||||
Node* js_closure =
|
||||
graph()->NewNode(mcgraph()->common()->Parameter(
|
||||
Linkage::kJSCallClosureParamIndex, "%closure"),
|
||||
graph()->start());
|
||||
Node* js_context = graph()->NewNode(
|
||||
mcgraph()->common()->Parameter(
|
||||
Linkage::GetJSCallContextParamIndex(wasm_count + 1), "%context"),
|
||||
graph()->start());
|
||||
|
||||
// Create the instance_node node to pass as parameter. It is loaded from
|
||||
// an actual reference to an instance or a placeholder reference,
|
||||
// called {WasmExportedFunction} via the {WasmExportedFunctionData}
|
||||
// structure.
|
||||
Node* function_data = BuildLoadFunctionDataFromExportedFunction(js_closure);
|
||||
instance_node_.set(
|
||||
BuildLoadInstanceFromExportedFunctionData(function_data));
|
||||
|
||||
if (!wasm::IsJSCompatibleSignature(sig_, module_, enabled_features_)) {
|
||||
// Throw a TypeError. Use the js_context of the calling javascript
|
||||
// function (passed as a parameter), such that the generated code is
|
||||
// js_context independent.
|
||||
BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context,
|
||||
nullptr, 0);
|
||||
TerminateThrow(effect(), control());
|
||||
return;
|
||||
}
|
||||
|
||||
const int args_count = wasm_count + 1; // +1 for wasm_code.
|
||||
base::SmallVector<Node*, 16> args(args_count);
|
||||
base::SmallVector<Node*, 1> rets(rets_count);
|
||||
|
||||
// Convert JS parameters to wasm numbers.
|
||||
for (int i = 0; i < wasm_count; ++i) {
|
||||
Node* param = Param(i + 1);
|
||||
Node* wasm_param = FromJS(param, js_context, sig_->GetParam(i));
|
||||
args[i + 1] = wasm_param;
|
||||
}
|
||||
|
||||
Node* BuildCallAndReturn(bool is_import, Node* js_context,
|
||||
Node* function_data,
|
||||
base::SmallVector<Node*, 16> args) {
|
||||
// Set the ThreadInWasm flag before we do the actual call.
|
||||
BuildModifyThreadInWasmFlag(true);
|
||||
|
||||
const int rets_count = static_cast<int>(sig_->return_count());
|
||||
base::SmallVector<Node*, 1> rets(rets_count);
|
||||
|
||||
if (is_import) {
|
||||
// Call to an imported function.
|
||||
// Load function index from {WasmExportedFunctionData}.
|
||||
@ -6389,7 +6369,145 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
|
||||
STORE_FIXED_ARRAY_SLOT_ANY(fixed_array, i, value);
|
||||
}
|
||||
}
|
||||
Return(jsval);
|
||||
return jsval;
|
||||
}
|
||||
|
||||
bool QualifiesForFastTransform(const wasm::FunctionSig*) {
|
||||
const int wasm_count = static_cast<int>(sig_->parameter_count());
|
||||
for (int i = 0; i < wasm_count; ++i) {
|
||||
wasm::ValueType type = sig_->GetParam(i);
|
||||
switch (type.kind()) {
|
||||
case wasm::ValueType::kRef:
|
||||
case wasm::ValueType::kOptRef:
|
||||
case wasm::ValueType::kF32:
|
||||
case wasm::ValueType::kF64:
|
||||
case wasm::ValueType::kI64:
|
||||
case wasm::ValueType::kRtt:
|
||||
case wasm::ValueType::kS128:
|
||||
case wasm::ValueType::kI8:
|
||||
case wasm::ValueType::kI16:
|
||||
case wasm::ValueType::kBottom:
|
||||
case wasm::ValueType::kStmt:
|
||||
return false;
|
||||
case wasm::ValueType::kI32:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Node* CanTransformFast(Node* input, wasm::ValueType type) {
|
||||
switch (type.kind()) {
|
||||
case wasm::ValueType::kI32: {
|
||||
Node* is_smi = gasm_->Word32Equal(
|
||||
gasm_->Word32And(BuildTruncateIntPtrToInt32(input),
|
||||
gasm_->Int32Constant(kSmiTagMask)),
|
||||
gasm_->Int32Constant(0));
|
||||
return is_smi;
|
||||
}
|
||||
case wasm::ValueType::kRef:
|
||||
case wasm::ValueType::kOptRef:
|
||||
case wasm::ValueType::kF32:
|
||||
case wasm::ValueType::kF64:
|
||||
case wasm::ValueType::kI64:
|
||||
case wasm::ValueType::kRtt:
|
||||
case wasm::ValueType::kS128:
|
||||
case wasm::ValueType::kI8:
|
||||
case wasm::ValueType::kI16:
|
||||
case wasm::ValueType::kBottom:
|
||||
case wasm::ValueType::kStmt:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void BuildJSToWasmWrapper(bool is_import) {
|
||||
const int wasm_count = static_cast<int>(sig_->parameter_count());
|
||||
|
||||
// Build the start and the JS parameter nodes.
|
||||
SetEffectControl(Start(wasm_count + 5));
|
||||
|
||||
// Create the js_closure and js_context parameters.
|
||||
Node* js_closure =
|
||||
graph()->NewNode(mcgraph()->common()->Parameter(
|
||||
Linkage::kJSCallClosureParamIndex, "%closure"),
|
||||
graph()->start());
|
||||
Node* js_context = graph()->NewNode(
|
||||
mcgraph()->common()->Parameter(
|
||||
Linkage::GetJSCallContextParamIndex(wasm_count + 1), "%context"),
|
||||
graph()->start());
|
||||
|
||||
// Create the instance_node node to pass as parameter. It is loaded from
|
||||
// an actual reference to an instance or a placeholder reference,
|
||||
// called {WasmExportedFunction} via the {WasmExportedFunctionData}
|
||||
// structure.
|
||||
Node* function_data = BuildLoadFunctionDataFromExportedFunction(js_closure);
|
||||
instance_node_.set(
|
||||
BuildLoadInstanceFromExportedFunctionData(function_data));
|
||||
|
||||
if (!wasm::IsJSCompatibleSignature(sig_, module_, enabled_features_)) {
|
||||
// Throw a TypeError. Use the js_context of the calling javascript
|
||||
// function (passed as a parameter), such that the generated code is
|
||||
// js_context independent.
|
||||
BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context,
|
||||
nullptr, 0);
|
||||
TerminateThrow(effect(), control());
|
||||
return;
|
||||
}
|
||||
|
||||
const int args_count = wasm_count + 1; // +1 for wasm_code.
|
||||
|
||||
// Check whether the signature of the function allows for a fast
|
||||
// transformation. Create a fast transformation path, only if it does.
|
||||
bool include_fast_path = QualifiesForFastTransform(sig_);
|
||||
|
||||
// Prepare Param() nodes. Param() nodes can only be created once,
|
||||
// so we need to use the same nodes along all possible transformation paths.
|
||||
base::SmallVector<Node*, 16> params(args_count);
|
||||
for (int i = 0; i < wasm_count; ++i) params[i + 1] = Param(i + 1);
|
||||
|
||||
auto done = gasm_->MakeLabel(MachineRepresentation::kTagged);
|
||||
if (include_fast_path) {
|
||||
auto slow_path = gasm_->MakeDeferredLabel();
|
||||
// Create a condition to determine the transformation path to be used
|
||||
// on runtime.
|
||||
Node* use_fast_path = gasm_->Int32Constant(1);
|
||||
for (int i = 0; i < wasm_count; ++i) {
|
||||
Node* can_transform_fast =
|
||||
CanTransformFast(params[i + 1], sig_->GetParam(i));
|
||||
use_fast_path = gasm_->Word32And(can_transform_fast, use_fast_path);
|
||||
}
|
||||
gasm_->GotoIfNot(use_fast_path, &slow_path);
|
||||
// Convert JS parameters to wasm numbers using the fast transformation
|
||||
// and build the call.
|
||||
base::SmallVector<Node*, 16> args(args_count);
|
||||
for (int i = 0; i < wasm_count; ++i) {
|
||||
Node* wasm_param = FromJSFast(params[i + 1], sig_->GetParam(i));
|
||||
args[i + 1] = wasm_param;
|
||||
}
|
||||
Node* jsval =
|
||||
BuildCallAndReturn(is_import, js_context, function_data, args);
|
||||
gasm_->Goto(&done, jsval);
|
||||
gasm_->Bind(&slow_path);
|
||||
}
|
||||
// Convert JS parameters to wasm numbers using the default transformation
|
||||
// and build the call.
|
||||
base::SmallVector<Node*, 16> args(args_count);
|
||||
for (int i = 0; i < wasm_count; ++i) {
|
||||
Node* wasm_param = FromJS(params[i + 1], js_context, sig_->GetParam(i));
|
||||
args[i + 1] = wasm_param;
|
||||
}
|
||||
Node* jsval =
|
||||
BuildCallAndReturn(is_import, js_context, function_data, args);
|
||||
// If both the default and a fast transformation paths are present,
|
||||
// get the return value based on the path used.
|
||||
if (include_fast_path) {
|
||||
gasm_->Goto(&done, jsval);
|
||||
gasm_->Bind(&done);
|
||||
Return(done.PhiAt(0));
|
||||
} else {
|
||||
Return(jsval);
|
||||
}
|
||||
if (ContainsInt64(sig_)) LowerInt64(kCalledFromJS);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user