| Index: src/compiler/js-builtin-reducer.cc
|
| diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc
|
| index 868ae7b0b3cb0411e35084c6bad2f9325dc39c90..94946a5279e9e6b8cc938239719886f70713871f 100644
|
| --- a/src/compiler/js-builtin-reducer.cc
|
| +++ b/src/compiler/js-builtin-reducer.cc
|
| @@ -4,6 +4,7 @@
|
|
|
| #include "src/compiler/js-builtin-reducer.h"
|
|
|
| +#include "src/compilation-dependencies.h"
|
| #include "src/compiler/access-builder.h"
|
| #include "src/compiler/js-graph.h"
|
| #include "src/compiler/node-matchers.h"
|
| @@ -93,11 +94,157 @@ class JSCallReduction {
|
| Node* node_;
|
| };
|
|
|
| -JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph)
|
| +JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph,
|
| + Flags flags,
|
| + CompilationDependencies* dependencies)
|
| : AdvancedReducer(editor),
|
| + dependencies_(dependencies),
|
| + flags_(flags),
|
| jsgraph_(jsgraph),
|
| type_cache_(TypeCache::Get()) {}
|
|
|
| +namespace {
|
| +
|
| +MaybeHandle<Map> GetMapWitness(Node* node) {
|
| + Node* receiver = NodeProperties::GetValueInput(node, 1);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + // Check if the {node} is dominated by a CheckMaps with a single map
|
| + // for the {receiver}, and if so use that map for the lowering below.
|
| + for (Node* dominator = effect;;) {
|
| + if (dominator->opcode() == IrOpcode::kCheckMaps &&
|
| + dominator->InputAt(0) == receiver) {
|
| + if (dominator->op()->ValueInputCount() == 2) {
|
| + HeapObjectMatcher m(dominator->InputAt(1));
|
| + if (m.HasValue()) return Handle<Map>::cast(m.Value());
|
| + }
|
| + return MaybeHandle<Map>();
|
| + }
|
| + if (dominator->op()->EffectInputCount() != 1) {
|
| + // Didn't find any appropriate CheckMaps node.
|
| + return MaybeHandle<Map>();
|
| + }
|
| + dominator = NodeProperties::GetEffectInput(dominator);
|
| + }
|
| +}
|
| +
|
| +// TODO(turbofan): This was copied from Crankshaft, might be too restrictive.
|
| +bool IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map) {
|
| + DCHECK(!jsarray_map->is_dictionary_map());
|
| + Isolate* isolate = jsarray_map->GetIsolate();
|
| + Handle<Name> length_string = isolate->factory()->length_string();
|
| + DescriptorArray* descriptors = jsarray_map->instance_descriptors();
|
| + int number =
|
| + descriptors->SearchWithCache(isolate, *length_string, *jsarray_map);
|
| + DCHECK_NE(DescriptorArray::kNotFound, number);
|
| + return descriptors->GetDetails(number).IsReadOnly();
|
| +}
|
| +
|
| +// TODO(turbofan): This was copied from Crankshaft, might be too restrictive.
|
| +bool CanInlineArrayResizeOperation(Handle<Map> receiver_map) {
|
| + Isolate* const isolate = receiver_map->GetIsolate();
|
| + if (!receiver_map->prototype()->IsJSArray()) return false;
|
| + Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
|
| + isolate);
|
| + return receiver_map->instance_type() == JS_ARRAY_TYPE &&
|
| + IsFastElementsKind(receiver_map->elements_kind()) &&
|
| + !receiver_map->is_dictionary_map() && receiver_map->is_extensible() &&
|
| + (!receiver_map->is_prototype_map() || receiver_map->is_stable()) &&
|
| + receiver_prototype->map()->is_stable() &&
|
| + isolate->IsFastArrayConstructorPrototypeChainIntact() &&
|
| + isolate->IsAnyInitialArrayPrototype(receiver_prototype) &&
|
| + !IsReadOnlyLengthDescriptor(receiver_map);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +// ES6 section 22.1.3.17 Array.prototype.pop ( )
|
| +Reduction JSBuiltinReducer::ReduceArrayPop(Node* node) {
|
| + Handle<Map> receiver_map;
|
| + Node* receiver = NodeProperties::GetValueInput(node, 1);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| + // TODO(turbofan): Extend this to also handle fast (holey) double elements
|
| + // once we got the hole NaN mess sorted out in TurboFan/V8.
|
| + if (GetMapWitness(node).ToHandle(&receiver_map) &&
|
| + CanInlineArrayResizeOperation(receiver_map) &&
|
| + IsFastSmiOrObjectElementsKind(receiver_map->elements_kind())) {
|
| + // Install code dependencies on the {receiver} prototype maps and the
|
| + // global array protector cell.
|
| + dependencies()->AssumePropertyCell(factory()->array_protector());
|
| + dependencies()->AssumePrototypeMapsStable(receiver_map);
|
| +
|
| + // Load the "length" property of the {receiver}.
|
| + Node* length = effect = graph()->NewNode(
|
| + simplified()->LoadField(
|
| + AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
|
| + receiver, effect, control);
|
| +
|
| + // Check if the {receiver} has any elements.
|
| + Node* check = graph()->NewNode(simplified()->NumberEqual(), length,
|
| + jsgraph()->ZeroConstant());
|
| + Node* branch =
|
| + graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
|
| +
|
| + Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
|
| + Node* etrue = effect;
|
| + Node* vtrue = jsgraph()->UndefinedConstant();
|
| +
|
| + Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
| + Node* efalse = effect;
|
| + Node* vfalse;
|
| + {
|
| + // Load the elements backing store from the {receiver}.
|
| + Node* elements = efalse = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
|
| + receiver, efalse, if_false);
|
| +
|
| + // Ensure that we aren't popping from a copy-on-write backing store.
|
| + elements = efalse =
|
| + graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver,
|
| + elements, efalse, if_false);
|
| +
|
| + // Compute the new {length}.
|
| + length = graph()->NewNode(simplified()->NumberSubtract(), length,
|
| + jsgraph()->OneConstant());
|
| +
|
| + // Store the new {length} to the {receiver}.
|
| + efalse = graph()->NewNode(
|
| + simplified()->StoreField(
|
| + AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
|
| + receiver, length, efalse, if_false);
|
| +
|
| + // Load the last entry from the {elements}.
|
| + vfalse = efalse = graph()->NewNode(
|
| + simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(
|
| + receiver_map->elements_kind())),
|
| + elements, length, efalse, if_false);
|
| +
|
| + // Store a hole to the element we just removed from the {receiver}.
|
| + efalse = graph()->NewNode(
|
| + simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(
|
| + GetHoleyElementsKind(receiver_map->elements_kind()))),
|
| + elements, length, jsgraph()->TheHoleConstant(), efalse, if_false);
|
| + }
|
| +
|
| + control = graph()->NewNode(common()->Merge(2), if_true, if_false);
|
| + effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
|
| + Node* value =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
| + vtrue, vfalse, control);
|
| +
|
| + // Convert the hole to undefined. Do this last, so that we can optimize
|
| + // conversion operator via some smart strength reduction in many cases.
|
| + if (IsFastHoleyElementsKind(receiver_map->elements_kind())) {
|
| + value =
|
| + graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
|
| + }
|
| +
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| // ES6 section 20.2.2.1 Math.abs ( x )
|
| Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) {
|
| JSCallReduction r(node);
|
| @@ -749,6 +896,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
|
| // Dispatch according to the BuiltinFunctionId if present.
|
| if (!r.HasBuiltinFunctionId()) return NoChange();
|
| switch (r.GetBuiltinFunctionId()) {
|
| + case kArrayPop:
|
| + return ReduceArrayPop(node);
|
| case kMathAbs:
|
| reduction = ReduceMathAbs(node);
|
| break;
|
| @@ -903,6 +1052,7 @@ Node* JSBuiltinReducer::ToUint32(Node* input) {
|
|
|
| Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
|
|
|
| +Factory* JSBuiltinReducer::factory() const { return isolate()->factory(); }
|
|
|
| Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
|
|
|
|
|