Commit ce904d1d authored by Clemens Backes's avatar Clemens Backes Committed by Commit Bot

[wasm] Add fast path for Smi in wasm-to-js wrappers

For Smi, we can avoid calling the ToNumber builtin, and also the
conversion to float and back. This adds some complexity, but greatly
improves performance for parameters that fit in a Smi.

R=jkummerow@chromium.org

Bug: v8:10070
Change-Id: Ic55f17e42ce52811296293f3cd218c850af42961
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1965576
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65525}
parent e7ddb89c
......@@ -13,30 +13,42 @@ namespace v8 {
namespace internal {
namespace compiler {
// A helper to make it easier to build diamond-shaped control patterns.
struct Diamond {
struct Diamond;
// A helper to make it easier to build branches that are not fully
// diamond-shaped.
struct HalfDiamond {
Graph* graph;
CommonOperatorBuilder* common;
Node* branch;
Node* if_true;
Node* if_false;
Node* merge;
Diamond(Graph* g, CommonOperatorBuilder* b, Node* cond,
BranchHint hint = BranchHint::kNone) {
HalfDiamond(Graph* g, CommonOperatorBuilder* b, Node* cond,
BranchHint hint = BranchHint::kNone) {
graph = g;
common = b;
branch = graph->NewNode(common->Branch(hint), cond, graph->start());
if_true = graph->NewNode(common->IfTrue(), branch);
if_false = graph->NewNode(common->IfFalse(), branch);
merge = graph->NewNode(common->Merge(2), if_true, if_false);
}
// Place {this} after {that} in control flow order.
void Chain(Diamond const& that) { branch->ReplaceInput(1, that.merge); }
void Chain(Node* that) { branch->ReplaceInput(1, that); }
// Place {this} after {that} in control flow order.
void Chain(Node* that) { branch->ReplaceInput(1, that); }
inline void Chain(Diamond const& that);
};
// A helper to make it easier to build diamond-shaped control patterns.
struct Diamond : public HalfDiamond {
Node* merge;
Diamond(Graph* g, CommonOperatorBuilder* b, Node* cond,
BranchHint hint = BranchHint::kNone)
: HalfDiamond(g, b, cond, hint) {
merge = graph->NewNode(common->Merge(2), if_true, if_false);
}
// Nest {this} into either the if_true or if_false branch of {that}.
void Nest(Diamond const& that, bool if_true) {
......@@ -58,6 +70,10 @@ struct Diamond {
}
};
void HalfDiamond::Chain(Diamond const& that) {
branch->ReplaceInput(1, that.merge);
}
} // namespace compiler
} // namespace internal
} // namespace v8
......
......@@ -5417,69 +5417,126 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder {
graph()->NewNode(call, target, input, context, Effect(), Control())));
}
Node* FromJS(Node* node, Node* js_context, wasm::ValueType type) {
DCHECK_NE(wasm::kWasmStmt, type);
// The parameter is of type anyref or exnref, we take it as is.
if (type == wasm::kWasmAnyRef || type == wasm::kWasmExnRef) {
return node;
}
Node* BuildTestSmi(Node* value) {
MachineOperatorBuilder* machine = mcgraph()->machine();
return graph()->NewNode(machine->Word32Equal(),
graph()->NewNode(machine->Word32And(),
BuildTruncateIntPtrToInt32(value),
Int32Constant(kSmiTagMask)),
Int32Constant(0));
}
if (type == wasm::kWasmFuncRef) {
Node* check =
BuildChangeSmiToInt32(SetEffect(BuildCallToRuntimeWithContext(
Runtime::kWasmIsValidFuncRefValue, js_context, &node, 1, effect_,
Control())));
Node* FromJS(Node* input, Node* js_context, wasm::ValueType type) {
switch (type) {
case wasm::kWasmAnyRef:
case wasm::kWasmExnRef:
// The parameter is of type anyref or exnref, we take it as is.
return input;
Diamond type_check(graph(), mcgraph()->common(), check,
BranchHint::kTrue);
type_check.Chain(Control());
case wasm::kWasmFuncRef: {
Node* check =
BuildChangeSmiToInt32(SetEffect(BuildCallToRuntimeWithContext(
Runtime::kWasmIsValidFuncRefValue, js_context, &input, 1,
effect_, Control())));
Node* effect = Effect();
BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context,
nullptr, 0, &effect, type_check.if_false);
Diamond type_check(graph(), mcgraph()->common(), check,
BranchHint::kTrue);
type_check.Chain(Control());
SetEffect(type_check.EffectPhi(Effect(), effect));
Node* effect = Effect();
BuildCallToRuntimeWithContext(Runtime::kWasmThrowTypeError, js_context,
nullptr, 0, &effect, type_check.if_false);
SetControl(type_check.merge);
SetEffect(type_check.EffectPhi(Effect(), effect));
return node;
}
Node* num = nullptr;
SetControl(type_check.merge);
if (type != wasm::kWasmI64) {
// Do a JavaScript ToNumber.
num = BuildJavaScriptToNumber(node, js_context);
return input;
}
// Change representation.
num = BuildChangeNumberToFloat64(num);
}
case wasm::kWasmI64:
// i64 values can only come from BigInt.
DCHECK(enabled_features_.has_bigint());
return BuildChangeBigIntToInt64(input, js_context);
switch (type) {
case wasm::kWasmI32: {
num = graph()->NewNode(mcgraph()->machine()->TruncateFloat64ToWord32(),
num);
break;
}
case wasm::kWasmI64: {
DCHECK(enabled_features_.has_bigint());
num = BuildChangeBigIntToInt64(node, js_context);
break;
// For i32 types, handle Smis first. The rest goes through the ToNumber
// stub.
// TODO(clemensb): Also handle HeapNumbers specially.
STATIC_ASSERT(kSmiTagMask < kMaxUInt32);
MachineOperatorBuilder* machine = mcgraph()->machine();
CommonOperatorBuilder* common = mcgraph()->common();
// Build a graph implementing this diagram:
// input smi?
// ├─ true: ───────────────────────┬─ smi-to-int32 ──────┬─ result
// └─ false: ToNumber -> smi? │ │
// ├─ true: ─┘ │
// └─ false: load -> f64-to-int32 ─┘
HalfDiamond input_is_smi(graph(), common, BuildTestSmi(input),
BranchHint::kTrue);
input_is_smi.Chain(Control());
Node* effect_orig = Effect();
// If not smi, call ToNumber and check if the result is Smi.
SetControl(input_is_smi.if_false);
Node* num = BuildJavaScriptToNumber(input, js_context);
Node* effect_after_tonumber = Effect();
HalfDiamond num_is_smi(graph(), common, BuildTestSmi(num),
BranchHint::kTrue);
num_is_smi.Chain(Control());
// If ToNumber returns HeapNumber, load its value and convert to int32.
SetControl(num_is_smi.if_false);
Node* heap_number_value = BuildLoadHeapNumberValue(num);
Node* converted_heap_number_value = graph()->NewNode(
machine->TruncateFloat64ToWord32(), heap_number_value);
Node* effect_after_heapnumber = Effect();
// Now merge the two Smi cases together, and convert the Smi to int32.
Node* smi_merge = graph()->NewNode(
common->Merge(2), input_is_smi.if_true, num_is_smi.if_true);
Node* smi_effect = graph()->NewNode(common->EffectPhi(2), effect_orig,
effect_after_tonumber, smi_merge);
Node* smi_value = graph()->NewNode(
common->Phi(MachineRepresentation::kTaggedSigned, 2), input, num,
smi_merge);
Node* converted_smi = BuildChangeSmiToInt32(smi_value);
// Merge the final result, control and effect.
Node* final_merge =
graph()->NewNode(common->Merge(2), smi_merge, num_is_smi.if_false);
Node* final_value = graph()->NewNode(
common->Phi(MachineRepresentation::kWord32, 2), converted_smi,
converted_heap_number_value, final_merge);
Node* final_effect =
graph()->NewNode(common->EffectPhi(2), smi_effect,
effect_after_heapnumber, final_merge);
SetEffect(final_effect);
SetControl(final_merge);
return final_value;
}
case wasm::kWasmF32:
num = graph()->NewNode(mcgraph()->machine()->TruncateFloat64ToFloat32(),
num);
break;
case wasm::kWasmF64:
break;
case wasm::kWasmS128:
UNREACHABLE();
case wasm::kWasmF64: {
// Do a JavaScript ToNumber.
Node* num = BuildJavaScriptToNumber(input, js_context);
// Change representation.
num = BuildChangeNumberToFloat64(num);
if (type == wasm::kWasmF32) {
num = graph()->NewNode(
mcgraph()->machine()->TruncateFloat64ToFloat32(), num);
}
return num;
}
default:
UNREACHABLE();
}
DCHECK_NOT_NULL(num);
return num;
}
void BuildModifyThreadInWasmFlag(bool new_value) {
......
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