Commit 4db73ae0 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Specialize to (optimization time) known TypedArray instances.

When we have a known JSTypedArray instance at optimization time, i.e. as
in asm.js-like use cases and also when there are global typed arrays that
are tracked via global object property constant tracking, we can generate
more efficient code in that case by specializing to the immutable internal
fields of the JSTypedArray (and the JSArrayBuffer backing it).

R=jarin@chromium.org
BUG=v8:4470,v8:5267

Review-Url: https://codereview.chromium.org/2510553002
Cr-Commit-Position: refs/heads/master@{#41017}
parent f16a7fe3
......@@ -3213,8 +3213,13 @@ EffectControlLinearizer::LowerLoadTypedElement(Node* node, Node* effect,
// ArrayBuffer (if there's any) as long as we are still operating on it.
effect = graph()->NewNode(common()->Retain(), buffer, effect);
// Compute the effective storage pointer.
Node* storage = effect = graph()->NewNode(machine()->UnsafePointerAdd(), base,
// Compute the effective storage pointer, handling the case where the
// {external} pointer is the effective storage pointer (i.e. the {base}
// is Smi zero).
Node* storage =
NumberMatcher(base).Is(0)
? external
: effect = graph()->NewNode(machine()->UnsafePointerAdd(), base,
external, effect, control);
// Perform the actual typed element access.
......@@ -3240,8 +3245,13 @@ EffectControlLinearizer::LowerStoreTypedElement(Node* node, Node* effect,
// ArrayBuffer (if there's any) as long as we are still operating on it.
effect = graph()->NewNode(common()->Retain(), buffer, effect);
// Compute the effective storage pointer.
Node* storage = effect = graph()->NewNode(machine()->UnsafePointerAdd(), base,
// Compute the effective storage pointer, handling the case where the
// {external} pointer is the effective storage pointer (i.e. the {base}
// is Smi zero).
Node* storage =
NumberMatcher(base).Is(0)
? external
: effect = graph()->NewNode(machine()->UnsafePointerAdd(), base,
external, effect, control);
// Perform the actual typed element access.
......
......@@ -171,8 +171,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
receiver, effect, control);
} else {
// Monomorphic property access.
receiver = effect = graph()->NewNode(simplified()->CheckHeapObject(),
receiver, effect, control);
receiver = BuildCheckHeapObject(receiver, &effect, control);
effect = BuildCheckMaps(receiver, effect, control,
access_info.receiver_maps());
}
......@@ -210,8 +209,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch);
receiverissmi_effect = effect;
} else {
receiver = effect = graph()->NewNode(simplified()->CheckHeapObject(),
receiver, effect, control);
receiver = BuildCheckHeapObject(receiver, &effect, control);
}
// Load the {receiver} map. The resulting effect is the dominating effect
......@@ -520,8 +518,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
}
// Ensure that {receiver} is a heap object.
receiver = effect = graph()->NewNode(simplified()->CheckHeapObject(),
receiver, effect, control);
receiver = BuildCheckHeapObject(receiver, &effect, control);
// Check for the monomorphic case.
if (access_infos.size() == 1) {
......@@ -1070,8 +1067,7 @@ JSNativeContextSpecialization::BuildPropertyAccess(
}
case MachineRepresentation::kTaggedPointer: {
// Ensure that {value} is a HeapObject.
value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
value, effect, control);
value = BuildCheckHeapObject(value, &effect, control);
Handle<Map> field_map;
if (access_info.field_map().ToHandle(&field_map)) {
// Emit a map check for the value.
......@@ -1165,34 +1161,61 @@ JSNativeContextSpecialization::BuildElementAccess(
ElementsKind elements_kind = access_info.elements_kind();
MapList const& receiver_maps = access_info.receiver_maps();
// Load the elements for the {receiver}.
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
effect, control);
if (IsFixedTypedArrayElementsKind(elements_kind)) {
Node* buffer;
Node* length;
Node* base_pointer;
Node* external_pointer;
// Don't try to store to a copy-on-write backing store.
if (access_mode == AccessMode::kStore &&
IsFastSmiOrObjectElementsKind(elements_kind) &&
store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
effect =
graph()->NewNode(simplified()->CheckMaps(1), elements,
jsgraph()->FixedArrayMapConstant(), effect, control);
}
// Check if we can constant-fold information about the {receiver} (i.e.
// for asm.js-like code patterns).
HeapObjectMatcher m(receiver);
if (m.HasValue() && m.Value()->IsJSTypedArray()) {
Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(m.Value());
if (IsFixedTypedArrayElementsKind(elements_kind)) {
// Determine the {receiver}s (known) length.
length = jsgraph()->Constant(typed_array->length_value());
// Check if the {receiver}s buffer was neutered.
buffer = jsgraph()->HeapConstant(typed_array->GetBuffer());
// Load the (known) base and external pointer for the {receiver}. The
// {external_pointer} might be invalid if the {buffer} was neutered, so
// we need to make sure that any access is properly guarded.
base_pointer = jsgraph()->ZeroConstant();
external_pointer = jsgraph()->PointerConstant(
FixedTypedArrayBase::cast(typed_array->elements())
->external_pointer());
} else {
// Load the {receiver}s length.
Node* length = effect = graph()->NewNode(
length = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()),
receiver, effect, control);
// Check if the {receiver}s buffer was neutered.
Node* buffer = effect = graph()->NewNode(
// Load the buffer for the {receiver}.
buffer = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
receiver, effect, control);
Node* check = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
// Load the elements for the {receiver}.
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
receiver, effect, control);
// Load the base and external pointer for the {receiver}s {elements}.
base_pointer = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
elements, effect, control);
external_pointer = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
elements, effect, control);
}
// Default to zero if the {receiver}s buffer was neutered.
Node* check = effect = graph()->NewNode(
simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
length = graph()->NewNode(
common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
check, jsgraph()->ZeroConstant(), length);
......@@ -1211,16 +1234,6 @@ JSNativeContextSpecialization::BuildElementAccess(
length, effect, control);
}
// Load the base and external pointer for the {receiver}.
Node* base_pointer = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
elements, effect, control);
Node* external_pointer = effect = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
elements, effect, control);
// Access the actual element.
ExternalArrayType external_array_type =
GetArrayTypeFromElementsKind(elements_kind);
......@@ -1280,6 +1293,20 @@ JSNativeContextSpecialization::BuildElementAccess(
}
}
} else {
// Load the elements for the {receiver}.
Node* elements = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
effect, control);
// Don't try to store to a copy-on-write backing store.
if (access_mode == AccessMode::kStore &&
IsFastSmiOrObjectElementsKind(elements_kind) &&
store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
effect =
graph()->NewNode(simplified()->CheckMaps(1), elements,
jsgraph()->FixedArrayMapConstant(), effect, control);
}
// Check if the {receiver} is a JSArray.
bool receiver_is_jsarray = HasOnlyJSArrayMaps(receiver_maps);
......@@ -1468,6 +1495,33 @@ JSNativeContextSpecialization::InlineApiCall(
return ValueEffectControl(value0, effect0, control0);
}
Node* JSNativeContextSpecialization::BuildCheckHeapObject(Node* receiver,
Node** effect,
Node* control) {
switch (receiver->opcode()) {
case IrOpcode::kHeapConstant:
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
case IrOpcode::kJSCreateArray:
case IrOpcode::kJSCreateClosure:
case IrOpcode::kJSCreateIterResultObject:
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSToName:
case IrOpcode::kJSToString:
case IrOpcode::kJSToObject:
case IrOpcode::kJSTypeOf: {
return receiver;
}
default: {
return *effect = graph()->NewNode(simplified()->CheckHeapObject(),
receiver, *effect, control);
}
}
}
Node* JSNativeContextSpecialization::BuildCheckMaps(
Node* receiver, Node* effect, Node* control,
std::vector<Handle<Map>> const& maps) {
......
......@@ -115,6 +115,9 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
AccessMode access_mode,
KeyedAccessStoreMode store_mode);
// Construct an appropriate heap object check.
Node* BuildCheckHeapObject(Node* receiver, Node** effect, Node* control);
// Construct an appropriate map check.
Node* BuildCheckMaps(Node* receiver, Node* effect, Node* control,
std::vector<Handle<Map>> const& maps);
......
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