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(); } |