Index: src/compiler/js-builtin-reducer.cc |
diff --git a/src/compiler/js-builtin-reducer.cc b/src/compiler/js-builtin-reducer.cc |
index afb9b1b6c009c4a0076e9eefaf1b99d694979afe..9b63e94a217c095f25553156812381af897194cf 100644 |
--- a/src/compiler/js-builtin-reducer.cc |
+++ b/src/compiler/js-builtin-reducer.cc |
@@ -5,6 +5,7 @@ |
#include "src/compiler/js-builtin-reducer.h" |
#include "src/base/bits.h" |
+#include "src/builtins/builtins-utils.h" |
#include "src/code-factory.h" |
#include "src/compilation-dependencies.h" |
#include "src/compiler/access-builder.h" |
@@ -1021,6 +1022,179 @@ Reduction JSBuiltinReducer::ReduceArrayPush(Node* node) { |
return NoChange(); |
} |
+// ES6 section 22.1.3.22 Array.prototype.shift ( ) |
+Reduction JSBuiltinReducer::ReduceArrayShift(Node* node) { |
+ Node* target = NodeProperties::GetValueInput(node, 0); |
+ Node* receiver = NodeProperties::GetValueInput(node, 1); |
+ Node* context = NodeProperties::GetContextInput(node); |
+ Node* frame_state = NodeProperties::GetFrameStateInput(node); |
+ 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. |
+ Handle<Map> receiver_map; |
+ if (GetMapWitness(node).ToHandle(&receiver_map) && |
+ CanInlineArrayResizeOperation(receiver_map) && |
+ receiver_map->elements_kind() != FAST_HOLEY_DOUBLE_ELEMENTS) { |
+ // Install code dependencies on the {receiver} prototype maps and the |
+ // global array protector cell. |
+ dependencies()->AssumePropertyCell(factory()->array_protector()); |
+ dependencies()->AssumePrototypeMapsStable(receiver_map); |
+ |
+ // Load length of the {receiver}. |
+ Node* length = effect = graph()->NewNode( |
+ simplified()->LoadField( |
+ AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), |
+ receiver, effect, control); |
+ |
+ // Return undefined if {receiver} has no elements. |
+ Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length, |
+ jsgraph()->ZeroConstant()); |
+ Node* branch0 = |
+ graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); |
+ |
+ Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
+ Node* etrue0 = effect; |
+ Node* vtrue0 = jsgraph()->UndefinedConstant(); |
+ |
+ Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
+ Node* efalse0 = effect; |
+ Node* vfalse0; |
+ { |
+ // Check if we should take the fast-path. |
+ Node* check1 = |
+ graph()->NewNode(simplified()->NumberLessThanOrEqual(), length, |
+ jsgraph()->Constant(JSArray::kMaxCopyElements)); |
+ Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
+ check1, if_false0); |
+ |
+ Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
+ Node* etrue1 = efalse0; |
+ Node* vtrue1; |
+ { |
+ Node* elements = etrue1 = graph()->NewNode( |
+ simplified()->LoadField(AccessBuilder::ForJSObjectElements()), |
+ receiver, etrue1, if_true1); |
+ |
+ // Load the first element here, which we return below. |
+ vtrue1 = etrue1 = graph()->NewNode( |
+ simplified()->LoadElement(AccessBuilder::ForFixedArrayElement( |
+ receiver_map->elements_kind())), |
+ elements, jsgraph()->ZeroConstant(), etrue1, if_true1); |
+ |
+ // Ensure that we aren't shifting a copy-on-write backing store. |
+ if (IsFastSmiOrObjectElementsKind(receiver_map->elements_kind())) { |
+ elements = etrue1 = |
+ graph()->NewNode(simplified()->EnsureWritableFastElements(), |
+ receiver, elements, etrue1, if_true1); |
+ } |
+ |
+ // Shift the remaining {elements} by one towards the start. |
+ Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1); |
+ Node* eloop = |
+ graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop); |
+ Node* index = graph()->NewNode( |
+ common()->Phi(MachineRepresentation::kTagged, 2), |
+ jsgraph()->OneConstant(), |
+ jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop); |
+ |
+ { |
+ Node* check2 = |
+ graph()->NewNode(simplified()->NumberLessThan(), index, length); |
+ Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop); |
+ |
+ if_true1 = graph()->NewNode(common()->IfFalse(), branch2); |
+ etrue1 = eloop; |
+ |
+ Node* control = graph()->NewNode(common()->IfTrue(), branch2); |
+ Node* effect = etrue1; |
+ |
+ ElementAccess const access = AccessBuilder::ForFixedArrayElement( |
+ receiver_map->elements_kind()); |
+ Node* value = effect = |
+ graph()->NewNode(simplified()->LoadElement(access), elements, |
+ index, effect, control); |
+ effect = graph()->NewNode( |
+ simplified()->StoreElement(access), elements, |
+ graph()->NewNode(simplified()->NumberSubtract(), index, |
+ jsgraph()->OneConstant()), |
+ value, effect, control); |
+ |
+ loop->ReplaceInput(1, control); |
+ eloop->ReplaceInput(1, effect); |
+ index->ReplaceInput(1, |
+ graph()->NewNode(simplified()->NumberAdd(), index, |
+ jsgraph()->OneConstant())); |
+ } |
+ |
+ // Compute the new {length}. |
+ length = graph()->NewNode(simplified()->NumberSubtract(), length, |
+ jsgraph()->OneConstant()); |
+ |
+ // Store the new {length} to the {receiver}. |
+ etrue1 = graph()->NewNode( |
+ simplified()->StoreField( |
+ AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), |
+ receiver, length, etrue1, if_true1); |
+ |
+ // Store a hole to the element we just removed from the {receiver}. |
+ etrue1 = graph()->NewNode( |
+ simplified()->StoreElement(AccessBuilder::ForFixedArrayElement( |
+ GetHoleyElementsKind(receiver_map->elements_kind()))), |
+ elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1); |
+ } |
+ |
+ Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
+ Node* efalse1 = efalse0; |
+ Node* vfalse1; |
+ { |
+ // Call the generic C++ implementation. |
+ const int builtin_index = Builtins::kArrayShift; |
+ CallDescriptor const* const desc = Linkage::GetCEntryStubCallDescriptor( |
+ graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver, |
+ Builtins::name(builtin_index), node->op()->properties(), |
+ CallDescriptor::kNeedsFrameState); |
+ Node* stub_code = jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, |
+ kArgvOnStack, true); |
+ Address builtin_entry = Builtins::CppEntryOf(builtin_index); |
+ Node* entry = jsgraph()->ExternalConstant( |
+ ExternalReference(builtin_entry, isolate())); |
+ Node* argc = |
+ jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver); |
+ if_false1 = efalse1 = vfalse1 = |
+ graph()->NewNode(common()->Call(desc), stub_code, receiver, argc, |
+ target, jsgraph()->UndefinedConstant(), entry, |
+ argc, context, frame_state, efalse1, if_false1); |
+ } |
+ |
+ if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
+ efalse0 = |
+ graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); |
+ vfalse0 = |
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
+ vtrue1, vfalse1, if_false0); |
+ } |
+ |
+ control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
+ effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); |
+ Node* value = |
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
+ vtrue0, vfalse0, 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(); |
+} |
+ |
namespace { |
bool HasInstanceTypeWitness(Node* receiver, Node* effect, |
@@ -2141,6 +2315,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { |
return ReduceArrayPop(node); |
case kArrayPush: |
return ReduceArrayPush(node); |
+ case kArrayShift: |
+ return ReduceArrayShift(node); |
case kDateNow: |
return ReduceDateNow(node); |
case kDateGetTime: |