Commit 249f6069 authored by Théotime Grohens's avatar Théotime Grohens Committed by Commit Bot

[turbofan] Inline DataView Int8 and Uint8 getters

This CL adds code to inline the Int8 and Uint8 getters for DataView
objects in TurboFan in js-call-reducer.cc, as well as a new test file.

It already improves execution speed compared to the Torque baseline
implementation, and implements most of the architecture needed
for inlining the other DataView getters and setters as well.

Change-Id: I0e62b98fd6ec995f7db5ec42ea1eff1f03572f97
Reviewed-on: https://chromium-review.googlesource.com/1119909Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarMichael Stanton <mvstanton@chromium.org>
Commit-Queue: Théotime Grohens <theotime@google.com>
Cr-Commit-Position: refs/heads/master@{#54157}
parent c23a6623
...@@ -3428,6 +3428,12 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, ...@@ -3428,6 +3428,12 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
return ReduceArrayBufferViewAccessor( return ReduceArrayBufferViewAccessor(
node, JS_DATA_VIEW_TYPE, node, JS_DATA_VIEW_TYPE,
AccessBuilder::ForJSArrayBufferViewByteOffset()); AccessBuilder::ForJSArrayBufferViewByteOffset());
case Builtins::kDataViewPrototypeGetUint8:
return ReduceDataViewPrototypeGet(node,
ExternalArrayType::kExternalUint8Array);
case Builtins::kDataViewPrototypeGetInt8:
return ReduceDataViewPrototypeGet(node,
ExternalArrayType::kExternalInt8Array);
case Builtins::kTypedArrayPrototypeByteLength: case Builtins::kTypedArrayPrototypeByteLength:
return ReduceArrayBufferViewAccessor( return ReduceArrayBufferViewAccessor(
node, JS_TYPED_ARRAY_TYPE, node, JS_TYPED_ARRAY_TYPE,
...@@ -6657,12 +6663,11 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor( ...@@ -6657,12 +6663,11 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
factory()->array_buffer_neutering_protector()); factory()->array_buffer_neutering_protector());
} else { } else {
// Check if the {receiver}s buffer was neutered. // Check if the {receiver}s buffer was neutered.
Node* receiver_buffer = effect = graph()->NewNode( Node* buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control); receiver, effect, control);
Node* check = effect = Node* check = effect = graph()->NewNode(
graph()->NewNode(simplified()->ArrayBufferWasNeutered(), simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
receiver_buffer, effect, control);
// Default to zero if the {receiver}s buffer was neutered. // Default to zero if the {receiver}s buffer was neutered.
value = graph()->NewNode( value = graph()->NewNode(
...@@ -6676,6 +6681,172 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor( ...@@ -6676,6 +6681,172 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
return NoChange(); return NoChange();
} }
Reduction JSCallReducer::ReduceDataViewPrototypeGet(
Node* node, ExternalArrayType element_type) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
CallParameters const& p = CallParametersOf(node->op());
Node* offset = node->op()->ValueInputCount() > 2
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->ZeroConstant();
Node* is_little_endian = node->op()->ValueInputCount() > 3
? NodeProperties::GetValueInput(node, 3)
: jsgraph()->FalseConstant();
// Only do stuff if the {receiver} is really a DataView.
if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
JS_DATA_VIEW_TYPE)) {
// Check that the {offset} is a positive Smi.
offset = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
offset, effect, control);
Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(),
jsgraph()->ZeroConstant(), offset);
effect = graph()->NewNode(
simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()),
is_positive, effect, control);
// Coerce {is_little_endian} to boolean.
is_little_endian =
graph()->NewNode(simplified()->ToBoolean(), is_little_endian);
// Get the underlying buffer and check that it has not been neutered.
Node* buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
Node* check_neutered = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
Node* branch_neutered = graph()->NewNode(
common()->Branch(BranchHint::kFalse), check_neutered, control);
// Raise an error if it was neuteured.
Node* if_true_neutered =
graph()->NewNode(common()->IfTrue(), branch_neutered);
Node* etrue_neutered = effect;
{
if_true_neutered = etrue_neutered = graph()->NewNode(
javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
jsgraph()->Constant(MessageTemplate::kDetachedOperation),
jsgraph()->HeapConstant(
factory()->NewStringFromAsciiChecked("DataView.prototype.get")),
context, frame_state, etrue_neutered, if_true_neutered);
}
// Otherwise, proceed.
Node* if_false_neutered =
graph()->NewNode(common()->IfFalse(), branch_neutered);
Node* efalse_neutered = effect;
// Get the byte offset and byte length of the {receiver}.
Node* byte_offset = efalse_neutered =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferViewByteOffset()),
receiver, efalse_neutered, if_false_neutered);
Node* byte_length = efalse_neutered =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferViewByteLength()),
receiver, efalse_neutered, if_false_neutered);
// The end offset is the offset plus the element size
// of the type that we want to load.
// Since we only load int8 and uint8 for now, that size is 1.
Node* end_offset = graph()->NewNode(simplified()->NumberAdd(), offset,
jsgraph()->OneConstant());
// We need to check that {end_offset} <= {byte_length}, ie
// throw a RangeError if {byte_length} < {end_offset}.
Node* check_range = graph()->NewNode(simplified()->NumberLessThan(),
byte_length, end_offset);
Node* branch_range = graph()->NewNode(common()->Branch(BranchHint::kFalse),
check_range, if_false_neutered);
Node* if_true_range = graph()->NewNode(common()->IfTrue(), branch_range);
Node* etrue_range = efalse_neutered;
{
if_true_range = etrue_range = graph()->NewNode(
javascript()->CallRuntime(Runtime::kThrowRangeError, 2),
jsgraph()->Constant(MessageTemplate::kInvalidDataViewAccessorOffset),
jsgraph()->HeapConstant(
factory()->NewStringFromAsciiChecked("DataView.prototype.get")),
context, frame_state, etrue_range, if_true_range);
}
Node* if_false_range = graph()->NewNode(common()->IfFalse(), branch_range);
Node* efalse_range = efalse_neutered;
Node* vfalse_range;
{
// Get the buffer's backing store.
Node* backing_store = efalse_range =
graph()->NewNode(simplified()->LoadField(
AccessBuilder::ForJSArrayBufferBackingStore()),
buffer, efalse_range, if_false_range);
// Compute the buffer index at which we'll read.
Node* buffer_index =
graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
// Perform the load.
vfalse_range = efalse_range = graph()->NewNode(
simplified()->LoadElement(AccessBuilder::ForTypedArrayElement(
element_type, true, LoadSensitivity::kCritical)),
backing_store, buffer_index, efalse_range, if_false_range);
}
// Rewire potential exception edges.
Node* on_exception = nullptr;
if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
// Create appropriate {IfException} and {IfSuccess} nodes.
Node* extrue_neutered = graph()->NewNode(
common()->IfException(), etrue_neutered,
if_true_neutered); // We threw because the array was neutered.
if_true_neutered =
graph()->NewNode(common()->IfSuccess(), if_true_neutered);
Node* extrue_range =
graph()->NewNode(common()->IfException(), etrue_range,
if_true_range); // We threw because out of bounds.
if_true_range = graph()->NewNode(common()->IfSuccess(), if_true_range);
// We can't throw in LoadTypedElement(),
// so we don't need to handle that path here.
// Join the exception edges.
Node* merge =
graph()->NewNode(common()->Merge(2), extrue_neutered, extrue_range);
Node* ephi = graph()->NewNode(common()->EffectPhi(2), extrue_neutered,
extrue_range, merge);
Node* phi =
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
extrue_neutered, extrue_range, merge);
ReplaceWithValue(on_exception, phi, ephi, merge);
}
// Connect the throwing paths to end.
if_true_neutered =
graph()->NewNode(common()->Throw(), etrue_neutered, if_true_neutered);
NodeProperties::MergeControlToEnd(graph(), common(), if_true_neutered);
if_true_range =
graph()->NewNode(common()->Throw(), etrue_range, if_true_range);
NodeProperties::MergeControlToEnd(graph(), common(), if_true_range);
// Continue on the regular path.
ReplaceWithValue(node, vfalse_range, efalse_range, if_false_range);
return Changed(vfalse_range);
}
return NoChange();
}
// ES6 section 18.2.2 isFinite ( number ) // ES6 section 18.2.2 isFinite ( number )
Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) { Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) {
CallParameters const& p = CallParametersOf(node->op()); CallParameters const& p = CallParametersOf(node->op());
......
...@@ -183,6 +183,9 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer { ...@@ -183,6 +183,9 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
InstanceType instance_type, InstanceType instance_type,
FieldAccess const& access); FieldAccess const& access);
Reduction ReduceDataViewPrototypeGet(Node* node,
ExternalArrayType element_type);
Reduction ReduceDatePrototypeGetTime(Node* node); Reduction ReduceDatePrototypeGetTime(Node* node);
Reduction ReduceDateNow(Node* node); Reduction ReduceDateNow(Node* node);
Reduction ReduceNumberParseInt(Node* node); Reduction ReduceNumberParseInt(Node* node);
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax --opt --no-always-opt
var buffer = new ArrayBuffer(64);
var dataview = new DataView(buffer, 8, 24);
var values = [-1, 2, -3, 42];
function readInt8Handled(offset) {
try {
return dataview.getInt8(offset);
} catch (e) {
return e;
}
}
function readUint8(offset) {
return dataview.getUint8(offset);
}
function warmup(f) {
f(0);
f(1);
%OptimizeFunctionOnNextCall(f);
f(2);
f(3);
}
// TurboFan valid getInt8.
for (var i = 0; i < values.length; i++) {
dataview.setInt8(i, values[i]);
}
warmup(readInt8Handled);
assertOptimized(readInt8Handled);
assertEquals(values[0], readInt8Handled(0));
assertEquals(values[1], readInt8Handled(1));
assertEquals(values[2], readInt8Handled(2));
assertEquals(values[3], readInt8Handled(3));
// TurboFan valid getUint8.
dataview.setUint32(4, 0xdeadbeef);
warmup(readUint8);
assertOptimized(readUint8);
assertEquals(0xde, readUint8(4));
assertEquals(0xad, readUint8(5));
assertEquals(0xbe, readUint8(6));
assertEquals(0xef, readUint8(7));
// TurboFan out of bounds read, throw with exception handler.
assertOptimized(readInt8Handled);
assertInstanceof(readInt8Handled(64), RangeError);
assertOptimized(readInt8Handled);
// Without exception handler.
assertOptimized(readUint8);
assertThrows(() => readUint8(64));
assertOptimized(readUint8);
// TurboFan deoptimizations.
assertOptimized(readInt8Handled);
assertInstanceof(readInt8Handled(-1), RangeError); // Negative Smi deopts.
assertUnoptimized(readInt8Handled);
warmup(readInt8Handled);
assertOptimized(readInt8Handled);
assertEquals(values[3], readInt8Handled(3.14)); // Non-Smi index deopts.
assertUnoptimized(readInt8Handled);
// None of the stores wrote out of bounds.
var bytes = new Uint8Array(buffer);
for (var i = 0; i < 8; i++) assertEquals(0, bytes[i]);
for (var i = 24; i < 64; i++) assertEquals(0, bytes[i]);
// TurboFan neutered buffer.
warmup(readInt8Handled);
assertOptimized(readInt8Handled);
%ArrayBufferNeuter(buffer);
assertInstanceof(readInt8Handled(0), TypeError);
assertOptimized(readInt8Handled);
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