|
5 | 5 | #include "src/compiler/js-builtin-reducer.h"
|
6 | 6 |
|
7 | 7 | #include "src/base/bits.h"
|
| 8 | +#include "src/builtins/builtins-utils.h" |
8 | 9 | #include "src/code-factory.h"
|
9 | 10 | #include "src/compilation-dependencies.h"
|
10 | 11 | #include "src/compiler/access-builder.h"
|
@@ -908,6 +909,179 @@ Reduction JSBuiltinReducer::ReduceArrayPush(Node* node) {
|
908 | 909 | return NoChange();
|
909 | 910 | }
|
910 | 911 |
|
| 912 | +// ES6 section 22.1.3.22 Array.prototype.shift ( ) |
| 913 | +Reduction JSBuiltinReducer::ReduceArrayShift(Node* node) { |
| 914 | + Node* target = NodeProperties::GetValueInput(node, 0); |
| 915 | + Node* receiver = NodeProperties::GetValueInput(node, 1); |
| 916 | + Node* context = NodeProperties::GetContextInput(node); |
| 917 | + Node* frame_state = NodeProperties::GetFrameStateInput(node); |
| 918 | + Node* effect = NodeProperties::GetEffectInput(node); |
| 919 | + Node* control = NodeProperties::GetControlInput(node); |
| 920 | + |
| 921 | + // TODO(turbofan): Extend this to also handle fast holey double elements |
| 922 | + // once we got the hole NaN mess sorted out in TurboFan/V8. |
| 923 | + Handle<Map> receiver_map; |
| 924 | + if (GetMapWitness(node).ToHandle(&receiver_map) && |
| 925 | + CanInlineArrayResizeOperation(receiver_map) && |
| 926 | + receiver_map->elements_kind() != FAST_HOLEY_DOUBLE_ELEMENTS) { |
| 927 | + // Install code dependencies on the {receiver} prototype maps and the |
| 928 | + // global array protector cell. |
| 929 | + dependencies()->AssumePropertyCell(factory()->array_protector()); |
| 930 | + dependencies()->AssumePrototypeMapsStable(receiver_map); |
| 931 | + |
| 932 | + // Load length of the {receiver}. |
| 933 | + Node* length = effect = graph()->NewNode( |
| 934 | + simplified()->LoadField( |
| 935 | + AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), |
| 936 | + receiver, effect, control); |
| 937 | + |
| 938 | + // Return undefined if {receiver} has no elements. |
| 939 | + Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length, |
| 940 | + jsgraph()->ZeroConstant()); |
| 941 | + Node* branch0 = |
| 942 | + graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); |
| 943 | + |
| 944 | + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
| 945 | + Node* etrue0 = effect; |
| 946 | + Node* vtrue0 = jsgraph()->UndefinedConstant(); |
| 947 | + |
| 948 | + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
| 949 | + Node* efalse0 = effect; |
| 950 | + Node* vfalse0; |
| 951 | + { |
| 952 | + // Check if we should take the fast-path. |
| 953 | + Node* check1 = |
| 954 | + graph()->NewNode(simplified()->NumberLessThanOrEqual(), length, |
| 955 | + jsgraph()->Constant(JSArray::kMaxCopyElements)); |
| 956 | + Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
| 957 | + check1, if_false0); |
| 958 | + |
| 959 | + Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
| 960 | + Node* etrue1 = efalse0; |
| 961 | + Node* vtrue1; |
| 962 | + { |
| 963 | + Node* elements = etrue1 = graph()->NewNode( |
| 964 | + simplified()->LoadField(AccessBuilder::ForJSObjectElements()), |
| 965 | + receiver, etrue1, if_true1); |
| 966 | + |
| 967 | + // Load the first element here, which we return below. |
| 968 | + vtrue1 = etrue1 = graph()->NewNode( |
| 969 | + simplified()->LoadElement(AccessBuilder::ForFixedArrayElement( |
| 970 | + receiver_map->elements_kind())), |
| 971 | + elements, jsgraph()->ZeroConstant(), etrue1, if_true1); |
| 972 | + |
| 973 | + // Ensure that we aren't shifting a copy-on-write backing store. |
| 974 | + if (IsFastSmiOrObjectElementsKind(receiver_map->elements_kind())) { |
| 975 | + elements = etrue1 = |
| 976 | + graph()->NewNode(simplified()->EnsureWritableFastElements(), |
| 977 | + receiver, elements, etrue1, if_true1); |
| 978 | + } |
| 979 | + |
| 980 | + // Shift the remaining {elements} by one towards the start. |
| 981 | + Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1); |
| 982 | + Node* eloop = |
| 983 | + graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop); |
| 984 | + Node* index = graph()->NewNode( |
| 985 | + common()->Phi(MachineRepresentation::kTagged, 2), |
| 986 | + jsgraph()->OneConstant(), |
| 987 | + jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop); |
| 988 | + |
| 989 | + { |
| 990 | + Node* check2 = |
| 991 | + graph()->NewNode(simplified()->NumberLessThan(), index, length); |
| 992 | + Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop); |
| 993 | + |
| 994 | + if_true1 = graph()->NewNode(common()->IfFalse(), branch2); |
| 995 | + etrue1 = eloop; |
| 996 | + |
| 997 | + Node* control = graph()->NewNode(common()->IfTrue(), branch2); |
| 998 | + Node* effect = etrue1; |
| 999 | + |
| 1000 | + ElementAccess const access = AccessBuilder::ForFixedArrayElement( |
| 1001 | + receiver_map->elements_kind()); |
| 1002 | + Node* value = effect = |
| 1003 | + graph()->NewNode(simplified()->LoadElement(access), elements, |
| 1004 | + index, effect, control); |
| 1005 | + effect = graph()->NewNode( |
| 1006 | + simplified()->StoreElement(access), elements, |
| 1007 | + graph()->NewNode(simplified()->NumberSubtract(), index, |
| 1008 | + jsgraph()->OneConstant()), |
| 1009 | + value, effect, control); |
| 1010 | + |
| 1011 | + loop->ReplaceInput(1, control); |
| 1012 | + eloop->ReplaceInput(1, effect); |
| 1013 | + index->ReplaceInput(1, |
| 1014 | + graph()->NewNode(simplified()->NumberAdd(), index, |
| 1015 | + jsgraph()->OneConstant())); |
| 1016 | + } |
| 1017 | + |
| 1018 | + // Compute the new {length}. |
| 1019 | + length = graph()->NewNode(simplified()->NumberSubtract(), length, |
| 1020 | + jsgraph()->OneConstant()); |
| 1021 | + |
| 1022 | + // Store the new {length} to the {receiver}. |
| 1023 | + etrue1 = graph()->NewNode( |
| 1024 | + simplified()->StoreField( |
| 1025 | + AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), |
| 1026 | + receiver, length, etrue1, if_true1); |
| 1027 | + |
| 1028 | + // Store a hole to the element we just removed from the {receiver}. |
| 1029 | + etrue1 = graph()->NewNode( |
| 1030 | + simplified()->StoreElement(AccessBuilder::ForFixedArrayElement( |
| 1031 | + GetHoleyElementsKind(receiver_map->elements_kind()))), |
| 1032 | + elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1); |
| 1033 | + } |
| 1034 | + |
| 1035 | + Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1); |
| 1036 | + Node* efalse1 = efalse0; |
| 1037 | + Node* vfalse1; |
| 1038 | + { |
| 1039 | + // Call the generic C++ implementation. |
| 1040 | + const int builtin_index = Builtins::kArrayShift; |
| 1041 | + CallDescriptor const* const desc = Linkage::GetCEntryStubCallDescriptor( |
| 1042 | + graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver, |
| 1043 | + Builtins::name(builtin_index), node->op()->properties(), |
| 1044 | + CallDescriptor::kNeedsFrameState); |
| 1045 | + Node* stub_code = jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, |
| 1046 | + kArgvOnStack, true); |
| 1047 | + Address builtin_entry = Builtins::CppEntryOf(builtin_index); |
| 1048 | + Node* entry = jsgraph()->ExternalConstant( |
| 1049 | + ExternalReference(builtin_entry, isolate())); |
| 1050 | + Node* argc = |
| 1051 | + jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver); |
| 1052 | + if_false1 = efalse1 = vfalse1 = |
| 1053 | + graph()->NewNode(common()->Call(desc), stub_code, receiver, argc, |
| 1054 | + target, jsgraph()->UndefinedConstant(), entry, |
| 1055 | + argc, context, frame_state, efalse1, if_false1); |
| 1056 | + } |
| 1057 | + |
| 1058 | + if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
| 1059 | + efalse0 = |
| 1060 | + graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); |
| 1061 | + vfalse0 = |
| 1062 | + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
| 1063 | + vtrue1, vfalse1, if_false0); |
| 1064 | + } |
| 1065 | + |
| 1066 | + control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
| 1067 | + effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); |
| 1068 | + Node* value = |
| 1069 | + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
| 1070 | + vtrue0, vfalse0, control); |
| 1071 | + |
| 1072 | + // Convert the hole to undefined. Do this last, so that we can optimize |
| 1073 | + // conversion operator via some smart strength reduction in many cases. |
| 1074 | + if (IsFastHoleyElementsKind(receiver_map->elements_kind())) { |
| 1075 | + value = |
| 1076 | + graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value); |
| 1077 | + } |
| 1078 | + |
| 1079 | + ReplaceWithValue(node, value, effect, control); |
| 1080 | + return Replace(value); |
| 1081 | + } |
| 1082 | + return NoChange(); |
| 1083 | +} |
| 1084 | + |
911 | 1085 | namespace {
|
912 | 1086 |
|
913 | 1087 | bool HasInstanceTypeWitness(Node* receiver, Node* effect,
|
@@ -1995,6 +2169,8 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
|
1995 | 2169 | return ReduceArrayPop(node);
|
1996 | 2170 | case kArrayPush:
|
1997 | 2171 | return ReduceArrayPush(node);
|
| 2172 | + case kArrayShift: |
| 2173 | + return ReduceArrayShift(node); |
1998 | 2174 | case kDateNow:
|
1999 | 2175 | return ReduceDateNow(node);
|
2000 | 2176 | case kDateGetTime:
|
|
0 commit comments