Skip to content

Commit

Permalink
[wasm] Introduce a faster param transformation in the js-to-wasm wrapper
Browse files Browse the repository at this point in the history
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 <[email protected]>
Reviewed-by: Andreas Haas <[email protected]>
Commit-Queue: Vicky Kontoura <[email protected]>
Cr-Commit-Position: refs/heads/master@{#70033}
  • Loading branch information
Vicky Kontoura authored and Commit Bot committed Sep 21, 2020
1 parent 69ca751 commit 66300a0
Showing 1 changed file with 165 additions and 47 deletions.
212 changes: 165 additions & 47 deletions src/compiler/wasm-compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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}.
Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit 66300a0

Please sign in to comment.