diff options
Diffstat (limited to 'deps/v8/src/compiler/js-call-reducer.cc')
-rw-r--r-- | deps/v8/src/compiler/js-call-reducer.cc | 1358 |
1 files changed, 664 insertions, 694 deletions
diff --git a/deps/v8/src/compiler/js-call-reducer.cc b/deps/v8/src/compiler/js-call-reducer.cc index 5b04731a64..d16e38a458 100644 --- a/deps/v8/src/compiler/js-call-reducer.cc +++ b/deps/v8/src/compiler/js-call-reducer.cc @@ -5,10 +5,9 @@ #include "src/compiler/js-call-reducer.h" #include "src/api-inl.h" -#include "src/builtins/builtins-promise-gen.h" +#include "src/builtins/builtins-promise.h" #include "src/builtins/builtins-utils.h" #include "src/code-factory.h" -#include "src/code-stubs.h" #include "src/compiler/access-builder.h" #include "src/compiler/access-info.h" #include "src/compiler/allocation-builder.h" @@ -253,8 +252,8 @@ Reduction JSCallReducer::ReduceObjectConstructor(Node* node) { Node* effect = NodeProperties::GetEffectInput(node); // We can fold away the Object(x) call if |x| is definitely not a primitive. - if (NodeProperties::CanBePrimitive(isolate(), value, effect)) { - if (!NodeProperties::CanBeNullOrUndefined(isolate(), value, effect)) { + if (NodeProperties::CanBePrimitive(broker(), value, effect)) { + if (!NodeProperties::CanBeNullOrUndefined(broker(), value, effect)) { // Turn the {node} into a {JSToObject} call if we know that // the {value} cannot be null or undefined. NodeProperties::ReplaceValueInputs(node, value); @@ -295,7 +294,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { // If {arguments_list} cannot be null or undefined, we don't need // to expand this {node} to control-flow. - if (!NodeProperties::CanBeNullOrUndefined(isolate(), arguments_list, + if (!NodeProperties::CanBeNullOrUndefined(broker(), arguments_list, effect)) { // Massage the value inputs appropriately. node->ReplaceInput(0, target); @@ -408,30 +407,37 @@ Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) { // definitely a constructor or not a constructor. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); - bool const is_constructor = receiver_maps[0]->is_constructor(); - Handle<Object> const prototype(receiver_maps[0]->prototype(), isolate()); - for (Handle<Map> const receiver_map : receiver_maps) { + MapRef first_receiver_map(broker(), receiver_maps[0]); + bool const is_constructor = first_receiver_map.is_constructor(); + first_receiver_map.SerializePrototype(); + ObjectRef const prototype = first_receiver_map.prototype(); + for (Handle<Map> const map : receiver_maps) { + MapRef receiver_map(broker(), map); + // Check for consistency among the {receiver_maps}. STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE); - if (receiver_map->prototype() != *prototype) return NoChange(); - if (receiver_map->is_constructor() != is_constructor) return NoChange(); - if (receiver_map->instance_type() < FIRST_FUNCTION_TYPE) return NoChange(); + receiver_map.SerializePrototype(); + if (!receiver_map.prototype().equals(prototype) || + receiver_map.is_constructor() != is_constructor || + receiver_map.instance_type() < FIRST_FUNCTION_TYPE) { + return NoChange(); + } // Disallow binding of slow-mode functions. We need to figure out // whether the length and name property are in the original state. - if (receiver_map->is_dictionary_map()) return NoChange(); + if (receiver_map.is_dictionary_map()) return NoChange(); // Check whether the length and name properties are still present // as AccessorInfo objects. In that case, their values can be // recomputed even if the actual value of the object changes. // This mirrors the checks done in builtins-function-gen.cc at // runtime otherwise. - Handle<DescriptorArray> descriptors(receiver_map->instance_descriptors(), - isolate()); + Handle<DescriptorArray> descriptors( + receiver_map.object()->instance_descriptors(), isolate()); if (descriptors->number_of_descriptors() < 2) return NoChange(); if (descriptors->GetKey(JSFunction::kLengthDescriptorIndex) != ReadOnlyRoots(isolate()).length_string()) { @@ -451,16 +457,12 @@ Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) { } } - // Setup the map for the resulting JSBoundFunction with the - // correct instance {prototype}. - Handle<Map> map( - is_constructor - ? native_context()->bound_function_with_constructor_map() - : native_context()->bound_function_without_constructor_map(), - isolate()); - if (map->prototype() != *prototype) { - map = Map::TransitionToPrototype(isolate(), map, prototype); - } + // Choose the map for the resulting JSBoundFunction (but bail out in case of a + // custom prototype). + MapRef map = is_constructor + ? native_context().bound_function_with_constructor_map() + : native_context().bound_function_without_constructor_map(); + if (!map.prototype().equals(prototype)) return NoChange(); // Make sure we can rely on the {receiver_maps}. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -481,8 +483,9 @@ Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) { inputs[2 + arity + 0] = context; inputs[2 + arity + 1] = effect; inputs[2 + arity + 2] = control; - Node* value = effect = graph()->NewNode( - javascript()->CreateBoundFunction(arity, map), input_count, inputs); + Node* value = effect = + graph()->NewNode(javascript()->CreateBoundFunction(arity, map.object()), + input_count, inputs); ReplaceWithValue(node, value, effect, control); return Replace(value); } @@ -500,8 +503,8 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { Node* context; HeapObjectMatcher m(target); if (m.HasValue()) { - Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); - context = jsgraph()->HeapConstant(handle(function->context(), isolate())); + JSFunctionRef function = m.Ref(broker()).AsJSFunction(); + context = jsgraph()->Constant(function.context()); } else { context = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target, @@ -570,18 +573,19 @@ Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { // Try to determine the {object} map. ZoneHandleSet<Map> object_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), object, effect, - &object_maps); + NodeProperties::InferReceiverMaps(broker(), object, effect, &object_maps); if (result != NodeProperties::kNoReceiverMaps) { - Handle<Map> candidate_map = object_maps[0]; - Handle<Object> candidate_prototype(candidate_map->prototype(), isolate()); + MapRef candidate_map(broker(), object_maps[0]); + candidate_map.SerializePrototype(); + ObjectRef candidate_prototype = candidate_map.prototype(); // Check if we can constant-fold the {candidate_prototype}. for (size_t i = 0; i < object_maps.size(); ++i) { - Handle<Map> object_map = object_maps[i]; - if (object_map->IsSpecialReceiverMap() || - object_map->has_hidden_prototype() || - object_map->prototype() != *candidate_prototype) { + MapRef object_map(broker(), object_maps[i]); + object_map.SerializePrototype(); + if (IsSpecialReceiverInstanceType(object_map.instance_type()) || + object_map.has_hidden_prototype() || + !object_map.prototype().equals(candidate_prototype)) { // We exclude special receivers, like JSProxy or API objects that // might require access checks here; we also don't want to deal // with hidden prototypes at this point. @@ -589,16 +593,15 @@ Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { } // The above check also excludes maps for primitive values, which is // important because we are not applying [[ToObject]] here as expected. - DCHECK(!object_map->IsPrimitiveMap() && object_map->IsJSReceiverMap()); + DCHECK(!object_map.IsPrimitiveMap() && object_map.IsJSReceiverMap()); if (result == NodeProperties::kUnreliableReceiverMaps && - !object_map->is_stable()) { + !object_map.is_stable()) { return NoChange(); } } if (result == NodeProperties::kUnreliableReceiverMaps) { for (size_t i = 0; i < object_maps.size(); ++i) { - dependencies()->DependOnStableMap( - MapRef(js_heap_broker(), object_maps[i])); + dependencies()->DependOnStableMap(MapRef(broker(), object_maps[i])); } } Node* value = jsgraph()->Constant(candidate_prototype); @@ -731,11 +734,12 @@ Reduction JSCallReducer::ReduceObjectPrototypeIsPrototypeOf(Node* node) { // the ToObject step of Object.prototype.isPrototypeOf is a no-op). ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); - for (size_t i = 0; i < receiver_maps.size(); ++i) { - if (!receiver_maps[i]->IsJSReceiverMap()) return NoChange(); + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.IsJSReceiverMap()) return NoChange(); } // We don't check whether {value} is a proper JSReceiver here explicitly, @@ -852,10 +856,10 @@ Reduction JSCallReducer::ReduceReflectGet(Node* node) { { if_false = efalse = graph()->NewNode( javascript()->CallRuntime(Runtime::kThrowTypeError, 2), - jsgraph()->Constant(MessageTemplate::kCalledOnNonObject), - jsgraph()->HeapConstant( - factory()->NewStringFromAsciiChecked("Reflect.get")), - context, frame_state, efalse, if_false); + jsgraph()->Constant( + static_cast<int>(MessageTemplate::kCalledOnNonObject)), + jsgraph()->HeapConstant(factory()->ReflectGet_string()), context, + frame_state, efalse, if_false); } // Otherwise just use the existing GetPropertyStub. @@ -929,10 +933,10 @@ Reduction JSCallReducer::ReduceReflectHas(Node* node) { { if_false = efalse = graph()->NewNode( javascript()->CallRuntime(Runtime::kThrowTypeError, 2), - jsgraph()->Constant(MessageTemplate::kCalledOnNonObject), - jsgraph()->HeapConstant( - factory()->NewStringFromAsciiChecked("Reflect.has")), - context, frame_state, efalse, if_false); + jsgraph()->Constant( + static_cast<int>(MessageTemplate::kCalledOnNonObject)), + jsgraph()->HeapConstant(factory()->ReflectHas_string()), context, + frame_state, efalse, if_false); } // Otherwise just use the existing {JSHasProperty} logic. @@ -973,17 +977,6 @@ Reduction JSCallReducer::ReduceReflectHas(Node* node) { return Changed(vtrue); } -bool CanInlineArrayIteratingBuiltin(Isolate* isolate, - Handle<Map> receiver_map) { - 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()) && - isolate->IsNoElementsProtectorIntact() && - isolate->IsAnyInitialArrayPrototype(receiver_prototype); -} - Node* JSCallReducer::WireInLoopStart(Node* k, Node** control, Node** effect) { Node* loop = *control = graph()->NewNode(common()->Loop(2), *control, *control); @@ -1002,8 +995,50 @@ void JSCallReducer::WireInLoopEnd(Node* loop, Node* eloop, Node* vloop, Node* k, eloop->ReplaceInput(1, effect); } -Reduction JSCallReducer::ReduceArrayForEach(Node* node, - Handle<SharedFunctionInfo> shared) { +namespace { +bool CanInlineArrayIteratingBuiltin(JSHeapBroker* broker, + ZoneHandleSet<Map> receiver_maps, + ElementsKind* kind_return) { + DCHECK_NE(0, receiver_maps.size()); + *kind_return = MapRef(broker, receiver_maps[0]).elements_kind(); + for (auto receiver_map : receiver_maps) { + MapRef map(broker, receiver_map); + if (!map.supports_fast_array_iteration() || + !UnionElementsKindUptoSize(kind_return, map.elements_kind())) { + return false; + } + } + return true; +} + +bool CanInlineArrayResizingBuiltin(JSHeapBroker* broker, + ZoneHandleSet<Map> receiver_maps, + ElementsKind* kind_return, + bool builtin_is_push = false) { + DCHECK_NE(0, receiver_maps.size()); + *kind_return = MapRef(broker, receiver_maps[0]).elements_kind(); + for (auto receiver_map : receiver_maps) { + MapRef map(broker, receiver_map); + if (!map.supports_fast_array_resize()) return false; + if (builtin_is_push) { + if (!UnionElementsKindUptoPackedness(kind_return, map.elements_kind())) { + return false; + } + } else { + // TODO(turbofan): We should also handle fast holey double elements once + // we got the hole NaN mess sorted out in TurboFan/V8. + if (map.elements_kind() == HOLEY_DOUBLE_ELEMENTS || + !UnionElementsKindUptoSize(kind_return, map.elements_kind())) { + return false; + } + } + } + return true; +} +} // namespace + +Reduction JSCallReducer::ReduceArrayForEach( + Node* node, const SharedFunctionInfoRef& shared) { if (!FLAG_turbo_inline_array_builtins) return NoChange(); DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); @@ -1026,36 +1061,19 @@ Reduction JSCallReducer::ReduceArrayForEach(Node* node, : jsgraph()->UndefinedConstant(); ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); - // By ensuring that {kind} is object or double, we can be polymorphic - // on different elements kinds. - ElementsKind kind = receiver_maps[0]->elements_kind(); - if (IsSmiElementsKind(kind)) { - kind = FastSmiToObjectElementsKind(kind); - } - for (Handle<Map> receiver_map : receiver_maps) { - ElementsKind next_kind = receiver_map->elements_kind(); - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) { - return NoChange(); - } - if (!IsFastElementsKind(next_kind)) { - return NoChange(); - } - if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) { - return NoChange(); - } - if (IsHoleyElementsKind(next_kind)) { - kind = GetHoleyElementsKind(kind); - } + ElementsKind kind; + if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } // Install code dependencies on the {receiver} prototype maps and the // global array protector cell. dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -1180,6 +1198,11 @@ Reduction JSCallReducer::ReduceArrayForEach(Node* node, control = if_false; effect = eloop; + // Introduce proper LoopExit and LoopExitEffect nodes to mark + // {loop} as a candidate for loop peeling (crbug.com/v8/8273). + control = graph()->NewNode(common()->LoopExit(), control, loop); + effect = graph()->NewNode(common()->LoopExitEffect(), effect, control); + // Wire up the branch for the case when IsCallable fails for the callback. // Since {check_throw} is an unconditional throw, it's impossible to // return a successful completion. Therefore, we simply connect the successful @@ -1192,9 +1215,9 @@ Reduction JSCallReducer::ReduceArrayForEach(Node* node, return Replace(jsgraph()->UndefinedConstant()); } -Reduction JSCallReducer::ReduceArrayReduce(Node* node, - ArrayReduceDirection direction, - Handle<SharedFunctionInfo> shared) { +Reduction JSCallReducer::ReduceArrayReduce( + Node* node, ArrayReduceDirection direction, + const SharedFunctionInfoRef& shared) { if (!FLAG_turbo_inline_array_builtins) return NoChange(); DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); @@ -1216,16 +1239,13 @@ Reduction JSCallReducer::ReduceArrayReduce(Node* node, ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); - ElementsKind kind = receiver_maps[0]->elements_kind(); - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) - return NoChange(); - if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind())) - return NoChange(); + ElementsKind kind; + if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } std::function<Node*(Node*)> hole_check = [this, kind](Node* element) { @@ -1240,7 +1260,7 @@ Reduction JSCallReducer::ReduceArrayReduce(Node* node, // Install code dependencies on the {receiver} prototype maps and the // global array protector cell. dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -1470,7 +1490,7 @@ Reduction JSCallReducer::ReduceArrayReduce(Node* node, } Reduction JSCallReducer::ReduceArrayMap(Node* node, - Handle<SharedFunctionInfo> shared) { + const SharedFunctionInfoRef& shared) { if (!FLAG_turbo_inline_array_builtins) return NoChange(); DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); @@ -1493,37 +1513,28 @@ Reduction JSCallReducer::ReduceArrayMap(Node* node, : jsgraph()->UndefinedConstant(); ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); // Ensure that any changes to the Array species constructor cause deopt. if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); - const ElementsKind kind = receiver_maps[0]->elements_kind(); - - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) - return NoChange(); - // We can handle different maps, as long as their elements kind are the - // same. - if (receiver_map->elements_kind() != kind) return NoChange(); + ElementsKind kind; + if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } if (IsHoleyElementsKind(kind)) { dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); } dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); - - Handle<JSFunction> handle_constructor( - JSFunction::cast( - native_context()->GetInitialJSArrayMap(kind)->GetConstructor()), - isolate()); - Node* array_constructor = jsgraph()->HeapConstant(handle_constructor); + PropertyCellRef(broker(), factory()->array_species_protector())); + Node* array_constructor = jsgraph()->Constant( + native_context().GetInitialJSArrayMap(kind).GetConstructor()); Node* k = jsgraph()->ZeroConstant(); @@ -1643,15 +1654,12 @@ Reduction JSCallReducer::ReduceArrayMap(Node* node, // The array {a} should be HOLEY_SMI_ELEMENTS because we'd only come into this // loop if the input array length is non-zero, and "new Array({x > 0})" always // produces a HOLEY array. - Handle<Map> double_map(Map::cast(native_context()->get( - Context::ArrayMapIndex(HOLEY_DOUBLE_ELEMENTS))), - isolate()); - Handle<Map> fast_map( - Map::cast(native_context()->get(Context::ArrayMapIndex(HOLEY_ELEMENTS))), - isolate()); - effect = graph()->NewNode( - simplified()->TransitionAndStoreElement(double_map, fast_map), a, k, - callback_value, effect, control); + MapRef holey_double_map = + native_context().GetInitialJSArrayMap(HOLEY_DOUBLE_ELEMENTS); + MapRef holey_map = native_context().GetInitialJSArrayMap(HOLEY_ELEMENTS); + effect = graph()->NewNode(simplified()->TransitionAndStoreElement( + holey_double_map.object(), holey_map.object()), + a, k, callback_value, effect, control); if (IsHoleyElementsKind(kind)) { Node* after_call_and_store_control = control; @@ -1682,8 +1690,8 @@ Reduction JSCallReducer::ReduceArrayMap(Node* node, return Replace(a); } -Reduction JSCallReducer::ReduceArrayFilter(Node* node, - Handle<SharedFunctionInfo> shared) { +Reduction JSCallReducer::ReduceArrayFilter( + Node* node, const SharedFunctionInfoRef& shared) { if (!FLAG_turbo_inline_array_builtins) return NoChange(); DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); @@ -1705,37 +1713,30 @@ Reduction JSCallReducer::ReduceArrayFilter(Node* node, : jsgraph()->UndefinedConstant(); ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); // And ensure that any changes to the Array species constructor cause deopt. if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); - const ElementsKind kind = receiver_maps[0]->elements_kind(); + ElementsKind kind; + if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); + } + // The output array is packed (filter doesn't visit holes). const ElementsKind packed_kind = GetPackedElementsKind(kind); - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) { - return NoChange(); - } - // We can handle different maps, as long as their elements kind are the - // same. - if (receiver_map->elements_kind() != kind) return NoChange(); - } - if (IsHoleyElementsKind(kind)) { dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); } dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); + PropertyCellRef(broker(), factory()->array_species_protector())); - Handle<Map> initial_map( - Map::cast(native_context()->GetInitialJSArrayMap(packed_kind)), - isolate()); + MapRef initial_map = native_context().GetInitialJSArrayMap(packed_kind); Node* k = jsgraph()->ZeroConstant(); Node* to = jsgraph()->ZeroConstant(); @@ -1751,16 +1752,15 @@ Reduction JSCallReducer::ReduceArrayFilter(Node* node, Node* a; // Construct the output array. { AllocationBuilder ab(jsgraph(), effect, control); - ab.Allocate(initial_map->instance_size(), NOT_TENURED, Type::Array()); + ab.Allocate(initial_map.instance_size(), NOT_TENURED, Type::Array()); ab.Store(AccessBuilder::ForMap(), initial_map); Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant(); ab.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array); ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array); ab.Store(AccessBuilder::ForJSArrayLength(packed_kind), jsgraph()->ZeroConstant()); - for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) { - ab.Store(AccessBuilder::ForJSObjectInObjectProperty( - MapRef(js_heap_broker(), initial_map), i), + for (int i = 0; i < initial_map.GetInObjectProperties(); ++i) { + ab.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), jsgraph()->UndefinedConstant()); } a = effect = ab.Finish(); @@ -1947,7 +1947,7 @@ Reduction JSCallReducer::ReduceArrayFilter(Node* node, } Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, - Handle<SharedFunctionInfo> shared) { + const SharedFunctionInfoRef& shared) { if (!FLAG_turbo_inline_array_builtins) return NoChange(); DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); @@ -1988,24 +1988,19 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, : jsgraph()->UndefinedConstant(); ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); - const ElementsKind kind = receiver_maps[0]->elements_kind(); - - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) - return NoChange(); - // We can handle different maps, as long as their elements kind are the - // same. - if (receiver_map->elements_kind() != kind) return NoChange(); + ElementsKind kind; + if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } // Install code dependencies on the {receiver} prototype maps and the // global array protector cell. dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -2140,10 +2135,16 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, Node* if_not_found_value = (variant == ArrayFindVariant::kFind) ? jsgraph()->UndefinedConstant() : jsgraph()->MinusOneConstant(); - Node* return_value = + Node* value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), if_found_return_value, if_not_found_value, control); + // Introduce proper LoopExit/LoopExitEffect/LoopExitValue to mark + // {loop} as a candidate for loop peeling (crbug.com/v8/8273). + control = graph()->NewNode(common()->LoopExit(), control, loop); + effect = graph()->NewNode(common()->LoopExitEffect(), effect, control); + value = graph()->NewNode(common()->LoopExitValue(), value, control); + // Wire up the branch for the case when IsCallable fails for the callback. // Since {check_throw} is an unconditional throw, it's impossible to // return a successful completion. Therefore, we simply connect the successful @@ -2152,8 +2153,8 @@ Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, graph()->NewNode(common()->Throw(), check_throw, check_fail); NodeProperties::MergeControlToEnd(graph(), common(), throw_node); - ReplaceWithValue(node, return_value, effect, control); - return Replace(return_value); + ReplaceWithValue(node, value, effect, control); + return Replace(value); } Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control, @@ -2174,10 +2175,10 @@ Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control, simplified()->LoadField(AccessBuilder::ForJSObjectElements()), a, etrue, if_true); - DCHECK(TypeCache::Get().kFixedDoubleArrayLengthType.Is( - TypeCache::Get().kFixedArrayLengthType)); + DCHECK(TypeCache::Get()->kFixedDoubleArrayLengthType.Is( + TypeCache::Get()->kFixedArrayLengthType)); Node* checked_to = etrue = graph()->NewNode( - common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), to, etrue, + common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), to, etrue, if_true); Node* elements_length = etrue = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements, @@ -2226,8 +2227,9 @@ void JSCallReducer::WireInCallbackIsCallableCheck( *check_fail = graph()->NewNode(common()->IfFalse(), check_branch); *check_throw = *check_fail = graph()->NewNode( javascript()->CallRuntime(Runtime::kThrowTypeError, 2), - jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback, - context, check_frame_state, effect, *check_fail); + jsgraph()->Constant( + static_cast<int>(MessageTemplate::kCalledNonCallable)), + fncallback, context, check_frame_state, effect, *check_fail); *control = graph()->NewNode(common()->IfTrue(), check_branch); } @@ -2280,7 +2282,7 @@ Node* JSCallReducer::SafeLoadElement(ElementsKind kind, Node* receiver, } Reduction JSCallReducer::ReduceArrayEvery(Node* node, - Handle<SharedFunctionInfo> shared) { + const SharedFunctionInfoRef& shared) { if (!FLAG_turbo_inline_array_builtins) return NoChange(); DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); @@ -2302,30 +2304,25 @@ Reduction JSCallReducer::ReduceArrayEvery(Node* node, : jsgraph()->UndefinedConstant(); ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); // And ensure that any changes to the Array species constructor cause deopt. if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); - const ElementsKind kind = receiver_maps[0]->elements_kind(); - - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) - return NoChange(); - // We can handle different maps, as long as their elements kind are the - // same. - if (receiver_map->elements_kind() != kind) return NoChange(); + ElementsKind kind; + if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } if (IsHoleyElementsKind(kind)) { dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); } dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); + PropertyCellRef(broker(), factory()->array_species_protector())); // If we have unreliable maps, we need a map check. if (result == NodeProperties::kUnreliableReceiverMaps) { @@ -2488,10 +2485,16 @@ Reduction JSCallReducer::ReduceArrayEvery(Node* node, control = graph()->NewNode(common()->Merge(2), if_false, if_false_callback); effect = graph()->NewNode(common()->EffectPhi(2), eloop, efalse_callback, control); - Node* return_value = graph()->NewNode( + Node* value = graph()->NewNode( common()->Phi(MachineRepresentation::kTagged, 2), jsgraph()->TrueConstant(), jsgraph()->FalseConstant(), control); + // Introduce proper LoopExit/LoopExitEffect/LoopExitValue to mark + // {loop} as a candidate for loop peeling (crbug.com/v8/8273). + control = graph()->NewNode(common()->LoopExit(), control, loop); + effect = graph()->NewNode(common()->LoopExitEffect(), effect, control); + value = graph()->NewNode(common()->LoopExitValue(), value, control); + // Wire up the branch for the case when IsCallable fails for the callback. // Since {check_throw} is an unconditional throw, it's impossible to // return a successful completion. Therefore, we simply connect the successful @@ -2500,8 +2503,8 @@ Reduction JSCallReducer::ReduceArrayEvery(Node* node, graph()->NewNode(common()->Throw(), check_throw, check_fail); NodeProperties::MergeControlToEnd(graph(), common(), throw_node); - ReplaceWithValue(node, return_value, effect, control); - return Replace(return_value); + ReplaceWithValue(node, value, effect, control); + return Replace(value); } namespace { @@ -2562,23 +2565,23 @@ Reduction JSCallReducer::ReduceArrayIndexOfIncludes( return NoChange(); } - Handle<Map> receiver_map; - if (!NodeProperties::GetMapWitness(isolate(), node).ToHandle(&receiver_map)) + Handle<Map> map; + if (!NodeProperties::GetMapWitness(broker(), node).ToHandle(&map)) return NoChange(); - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) - return NoChange(); + MapRef receiver_map(broker(), map); + if (!receiver_map.supports_fast_array_iteration()) return NoChange(); - if (IsHoleyElementsKind(receiver_map->elements_kind())) { + ElementsKind const elements_kind = receiver_map.elements_kind(); + if (IsHoleyElementsKind(elements_kind)) { dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); } Callable const callable = search_variant == SearchVariant::kIndexOf - ? GetCallableForArrayIndexOf(receiver_map->elements_kind(), isolate()) - : GetCallableForArrayIncludes(receiver_map->elements_kind(), - isolate()); + ? GetCallableForArrayIndexOf(elements_kind, isolate()) + : GetCallableForArrayIncludes(elements_kind, isolate()); CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( graph()->zone(), callable.descriptor(), callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags, @@ -2596,8 +2599,7 @@ Reduction JSCallReducer::ReduceArrayIndexOfIncludes( ? NodeProperties::GetValueInput(node, 2) : jsgraph()->UndefinedConstant(); Node* length = effect = graph()->NewNode( - simplified()->LoadField( - AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())), + simplified()->LoadField(AccessBuilder::ForJSArrayLength(elements_kind)), receiver, effect, control); Node* new_from_index = jsgraph()->ZeroConstant(); if (node->op()->ValueInputCount() >= 4) { @@ -2627,7 +2629,7 @@ Reduction JSCallReducer::ReduceArrayIndexOfIncludes( } Reduction JSCallReducer::ReduceArraySome(Node* node, - Handle<SharedFunctionInfo> shared) { + const SharedFunctionInfoRef& shared) { if (!FLAG_turbo_inline_array_builtins) return NoChange(); DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); @@ -2649,32 +2651,25 @@ Reduction JSCallReducer::ReduceArraySome(Node* node, : jsgraph()->UndefinedConstant(); ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); // And ensure that any changes to the Array species constructor cause deopt. if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); - if (receiver_maps.size() == 0) return NoChange(); - - const ElementsKind kind = receiver_maps[0]->elements_kind(); - - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) - return NoChange(); - // We can handle different maps, as long as their elements kind are the - // same. - if (receiver_map->elements_kind() != kind) return NoChange(); + ElementsKind kind; + if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } if (IsHoleyElementsKind(kind)) { dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); } dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); + PropertyCellRef(broker(), factory()->array_species_protector())); Node* k = jsgraph()->ZeroConstant(); @@ -2844,10 +2839,16 @@ Reduction JSCallReducer::ReduceArraySome(Node* node, control = graph()->NewNode(common()->Merge(2), if_false, if_true_callback); effect = graph()->NewNode(common()->EffectPhi(2), eloop, etrue_callback, control); - Node* return_value = graph()->NewNode( + Node* value = graph()->NewNode( common()->Phi(MachineRepresentation::kTagged, 2), jsgraph()->FalseConstant(), jsgraph()->TrueConstant(), control); + // Introduce proper LoopExit/LoopExitEffect/LoopExitValue to mark + // {loop} as a candidate for loop peeling (crbug.com/v8/8273). + control = graph()->NewNode(common()->LoopExit(), control, loop); + effect = graph()->NewNode(common()->LoopExitEffect(), effect, control); + value = graph()->NewNode(common()->LoopExitValue(), value, control); + // Wire up the branch for the case when IsCallable fails for the callback. // Since {check_throw} is an unconditional throw, it's impossible to // return a successful completion. Therefore, we simply connect the successful @@ -2856,48 +2857,44 @@ Reduction JSCallReducer::ReduceArraySome(Node* node, graph()->NewNode(common()->Throw(), check_throw, check_fail); NodeProperties::MergeControlToEnd(graph(), common(), throw_node); - ReplaceWithValue(node, return_value, effect, control); - return Replace(return_value); + ReplaceWithValue(node, value, effect, control); + return Replace(value); } Reduction JSCallReducer::ReduceCallApiFunction( - Node* node, Handle<SharedFunctionInfo> shared) { + Node* node, const SharedFunctionInfoRef& shared) { DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); CallParameters const& p = CallParametersOf(node->op()); int const argc = static_cast<int>(p.arity()) - 2; Node* target = NodeProperties::GetValueInput(node, 0); - Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) - ? jsgraph()->HeapConstant(global_proxy()) - : NodeProperties::GetValueInput(node, 1); + Node* receiver = + (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) + ? jsgraph()->Constant(native_context().global_proxy_object()) + : NodeProperties::GetValueInput(node, 1); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Handle<FunctionTemplateInfo> function_template_info( - FunctionTemplateInfo::cast(shared->function_data()), isolate()); - - // CallApiCallbackStub expects the target in a register, so we count it out, - // and counts the receiver as an implicit argument, so we count the receiver - // out too. - if (argc > CallApiCallbackStub::kArgMax) return NoChange(); + FunctionTemplateInfo::cast(shared.object()->function_data()), isolate()); // Infer the {receiver} maps, and check if we can inline the API function // callback based on those. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); - for (size_t i = 0; i < receiver_maps.size(); ++i) { - Handle<Map> receiver_map = receiver_maps[i]; - if (!receiver_map->IsJSObjectMap() || + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.IsJSObjectMap() || (!function_template_info->accept_any_receiver() && - receiver_map->is_access_check_needed())) { + receiver_map.is_access_check_needed())) { return NoChange(); } // In case of unreliable {receiver} information, the {receiver_maps} // must all be stable in order to consume the information. if (result == NodeProperties::kUnreliableReceiverMaps) { - if (!receiver_map->is_stable()) return NoChange(); + if (!receiver_map.is_stable()) return NoChange(); } } @@ -2918,9 +2915,9 @@ Reduction JSCallReducer::ReduceCallApiFunction( // Install stability dependencies for unreliable {receiver_maps}. if (result == NodeProperties::kUnreliableReceiverMaps) { - for (size_t i = 0; i < receiver_maps.size(); ++i) { - dependencies()->DependOnStableMap( - MapRef(js_heap_broker(), receiver_maps[i])); + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + dependencies()->DependOnStableMap(receiver_map); } } @@ -2937,7 +2934,7 @@ Reduction JSCallReducer::ReduceCallApiFunction( Handle<CallHandlerInfo> call_handler_info( CallHandlerInfo::cast(function_template_info->call_code()), isolate()); Handle<Object> data(call_handler_info->data(), isolate()); - Callable call_api_callback = CodeFactory::CallApiCallback(isolate(), argc); + Callable call_api_callback = CodeFactory::CallApiCallback(isolate()); CallInterfaceDescriptor cid = call_api_callback.descriptor(); auto call_descriptor = Linkage::GetStubCallDescriptor( graph()->zone(), cid, @@ -2952,13 +2949,14 @@ Reduction JSCallReducer::ReduceCallApiFunction( node->InsertInput(graph()->zone(), 0, jsgraph()->HeapConstant(call_api_callback.code())); node->ReplaceInput(1, context); - node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); - node->InsertInput(graph()->zone(), 3, holder); - node->InsertInput(graph()->zone(), 4, + node->InsertInput(graph()->zone(), 2, jsgraph()->ExternalConstant(function_reference)); - node->ReplaceInput(5, receiver); - node->RemoveInput(6 + argc); // Remove context input. - node->ReplaceInput(7 + argc, effect); // Update effect input. + node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(argc)); + node->InsertInput(graph()->zone(), 4, jsgraph()->Constant(data)); + node->InsertInput(graph()->zone(), 5, holder); + node->ReplaceInput(6, receiver); + node->RemoveInput(7 + argc); // Remove context input. + node->ReplaceInput(8 + argc, effect); // Update effect input. NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); return Changed(node); } @@ -3018,8 +3016,9 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( FieldAccess const& access = FieldAccessOf(user->op()); if (access.offset == JSArray::kLengthOffset) { // Ignore uses for arguments#length. - STATIC_ASSERT(JSArray::kLengthOffset == - JSArgumentsObjectWithLength::kLengthOffset); + STATIC_ASSERT( + static_cast<int>(JSArray::kLengthOffset) == + static_cast<int>(JSArgumentsObjectWithLength::kLengthOffset)); continue; } else if (access.offset == JSObject::kElementsOffset) { // Ignore safe uses for arguments#elements. @@ -3066,10 +3065,15 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list); FrameStateInfo state_info = FrameStateInfoOf(frame_state->op()); int start_index = 0; - // Determine the formal parameter count; - Handle<SharedFunctionInfo> shared; - if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); - int formal_parameter_count = shared->internal_formal_parameter_count(); + + int formal_parameter_count; + { + Handle<SharedFunctionInfo> shared; + if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); + formal_parameter_count = SharedFunctionInfoRef(broker(), shared) + .internal_formal_parameter_count(); + } + if (type == CreateArgumentsType::kMappedArguments) { // Mapped arguments (sloppy mode) that are aliased can only be handled // here if there's no side-effect between the {node} and the {arg_array}. @@ -3090,8 +3094,8 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( // that no one messed with the %ArrayIteratorPrototype%.next method. if (node->opcode() == IrOpcode::kJSCallWithSpread || node->opcode() == IrOpcode::kJSConstructWithSpread) { - dependencies()->DependOnProtector(PropertyCellRef( - js_heap_broker(), factory()->array_iterator_protector())); + dependencies()->DependOnProtector( + PropertyCellRef(broker(), factory()->array_iterator_protector())); } // Remove the {arguments_list} input from the {node}. @@ -3146,10 +3150,10 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( Node* check_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch); - Node* check_throw = check_fail = - graph()->NewNode(javascript()->CallRuntime(Runtime::kThrowTypeError, 2), - jsgraph()->Constant(MessageTemplate::kNotConstructor), - new_target, context, frame_state, effect, check_fail); + Node* check_throw = check_fail = graph()->NewNode( + javascript()->CallRuntime(Runtime::kThrowTypeError, 2), + jsgraph()->Constant(static_cast<int>(MessageTemplate::kNotConstructor)), + new_target, context, frame_state, effect, check_fail); control = graph()->NewNode(common()->IfTrue(), check_branch); NodeProperties::ReplaceControlInput(node, control); @@ -3211,6 +3215,13 @@ bool ShouldUseCallICFeedback(Node* node) { return true; } +base::Optional<HeapObjectRef> GetHeapObjectFeedback( + JSHeapBroker* broker, const FeedbackNexus& nexus) { + HeapObject object; + if (!nexus.GetFeedback()->GetHeapObject(&object)) return base::nullopt; + return HeapObjectRef(broker, handle(object, broker->isolate())); +} + } // namespace Reduction JSCallReducer::ReduceJSCall(Node* node) { @@ -3225,40 +3236,45 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { // Try to specialize JSCall {node}s with constant {target}s. HeapObjectMatcher m(target); if (m.HasValue()) { - if (m.Value()->IsJSFunction()) { - Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); + ObjectRef target_ref = m.Ref(broker()); + if (target_ref.IsJSFunction()) { + JSFunctionRef function = target_ref.AsJSFunction(); + function.Serialize(); // Don't inline cross native context. - if (function->native_context() != *native_context()) return NoChange(); - - return ReduceJSCall(node, handle(function->shared(), isolate())); - } else if (m.Value()->IsJSBoundFunction()) { - Handle<JSBoundFunction> function = - Handle<JSBoundFunction>::cast(m.Value()); - Handle<JSReceiver> bound_target_function( - function->bound_target_function(), isolate()); - Handle<Object> bound_this(function->bound_this(), isolate()); - Handle<FixedArray> bound_arguments(function->bound_arguments(), - isolate()); + if (!function.native_context().equals(native_context())) { + return NoChange(); + } + + return ReduceJSCall(node, function.shared()); + } else if (target_ref.IsJSBoundFunction()) { + JSBoundFunctionRef function = target_ref.AsJSBoundFunction(); + function.Serialize(); + + ObjectRef bound_this = function.bound_this(); ConvertReceiverMode const convert_mode = - (bound_this->IsNullOrUndefined(isolate())) + bound_this.IsNullOrUndefined() ? ConvertReceiverMode::kNullOrUndefined : ConvertReceiverMode::kNotNullOrUndefined; + // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]]. NodeProperties::ReplaceValueInput( - node, jsgraph()->Constant(bound_target_function), 0); + node, jsgraph()->Constant(function.bound_target_function()), 0); NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this), 1); + // Insert the [[BoundArguments]] for {node}. - for (int i = 0; i < bound_arguments->length(); ++i) { - node->InsertInput( - graph()->zone(), i + 2, - jsgraph()->Constant(handle(bound_arguments->get(i), isolate()))); + FixedArrayRef bound_arguments = function.bound_arguments(); + for (int i = 0; i < bound_arguments.length(); ++i) { + node->InsertInput(graph()->zone(), i + 2, + jsgraph()->Constant(bound_arguments.get(i))); arity++; } + NodeProperties::ChangeOp( node, javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode)); + // Try to further reduce the JSCall {node}. Reduction const reduction = ReduceJSCall(node); return reduction.Changed() ? reduction : Changed(node); @@ -3275,7 +3291,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { // the {target} must have the same native context as the call site. if (target->opcode() == IrOpcode::kJSCreateClosure) { CreateClosureParameters const& p = CreateClosureParametersOf(target->op()); - return ReduceJSCall(node, p.shared_info()); + return ReduceJSCall(node, SharedFunctionInfoRef(broker(), p.shared_info())); } // If {target} is the result of a JSCreateBoundFunction operation, @@ -3300,7 +3316,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { // Update the JSCall operator on {node}. ConvertReceiverMode const convert_mode = - NodeProperties::CanBeNullOrUndefined(isolate(), bound_this, effect) + NodeProperties::CanBeNullOrUndefined(broker(), bound_this, effect) ? ConvertReceiverMode::kAny : ConvertReceiverMode::kNotNullOrUndefined; NodeProperties::ChangeOp( @@ -3324,44 +3340,41 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { return NoChange(); } - HeapObject* heap_object; - if (nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object)) { - Handle<HeapObject> feedback(heap_object, isolate()); - // Check if we want to use CallIC feedback here. - if (!ShouldUseCallICFeedback(target)) return NoChange(); - - if (feedback->IsCallable()) { - Node* target_function = jsgraph()->Constant(feedback); + base::Optional<HeapObjectRef> feedback = + GetHeapObjectFeedback(broker(), nexus); + if (feedback.has_value() && ShouldUseCallICFeedback(target) && + feedback->map().is_callable()) { + Node* target_function = jsgraph()->Constant(*feedback); - // Check that the {target} is still the {target_function}. - Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, - target_function); - effect = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, - effect, control); + // Check that the {target} is still the {target_function}. + Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, + target_function); + effect = graph()->NewNode( + simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, + effect, control); - // Specialize the JSCall node to the {target_function}. - NodeProperties::ReplaceValueInput(node, target_function, 0); - NodeProperties::ReplaceEffectInput(node, effect); + // Specialize the JSCall node to the {target_function}. + NodeProperties::ReplaceValueInput(node, target_function, 0); + NodeProperties::ReplaceEffectInput(node, effect); - // Try to further reduce the JSCall {node}. - Reduction const reduction = ReduceJSCall(node); - return reduction.Changed() ? reduction : Changed(node); - } + // Try to further reduce the JSCall {node}. + Reduction const reduction = ReduceJSCall(node); + return reduction.Changed() ? reduction : Changed(node); } + return NoChange(); } Reduction JSCallReducer::ReduceJSCall(Node* node, - Handle<SharedFunctionInfo> shared) { + const SharedFunctionInfoRef& shared) { DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); Node* target = NodeProperties::GetValueInput(node, 0); // Do not reduce calls to functions with break points. - if (shared->HasBreakInfo()) return NoChange(); + if (shared.HasBreakInfo()) return NoChange(); // Raise a TypeError if the {target} is a "classConstructor". - if (IsClassConstructor(shared->kind())) { + if (IsClassConstructor(shared.kind())) { NodeProperties::ReplaceValueInputs(node, target); NodeProperties::ChangeOp( node, javascript()->CallRuntime( @@ -3372,7 +3385,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, // Check for known builtin functions. int builtin_id = - shared->HasBuiltinId() ? shared->builtin_id() : Builtins::kNoBuiltinId; + shared.HasBuiltinId() ? shared.builtin_id() : Builtins::kNoBuiltinId; switch (builtin_id) { case Builtins::kArrayConstructor: return ReduceArrayConstructor(node); @@ -3642,17 +3655,13 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, case Builtins::kStringIteratorPrototypeNext: return ReduceStringIteratorPrototypeNext(node); case Builtins::kStringPrototypeConcat: - return ReduceStringPrototypeConcat(node, shared); + return ReduceStringPrototypeConcat(node); case Builtins::kTypedArrayPrototypeEntries: return ReduceArrayIterator(node, IterationKind::kEntries); case Builtins::kTypedArrayPrototypeKeys: return ReduceArrayIterator(node, IterationKind::kKeys); case Builtins::kTypedArrayPrototypeValues: return ReduceArrayIterator(node, IterationKind::kValues); - case Builtins::kAsyncFunctionPromiseCreate: - return ReduceAsyncFunctionPromiseCreate(node); - case Builtins::kAsyncFunctionPromiseRelease: - return ReduceAsyncFunctionPromiseRelease(node); case Builtins::kPromiseInternalConstructor: return ReducePromiseInternalConstructor(node); case Builtins::kPromiseInternalReject: @@ -3665,6 +3674,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, return ReducePromisePrototypeFinally(node); case Builtins::kPromisePrototypeThen: return ReducePromisePrototypeThen(node); + case Builtins::kPromiseResolveTrampoline: + return ReducePromiseResolveTrampoline(node); case Builtins::kMapPrototypeEntries: return ReduceCollectionIteration(node, CollectionKind::kMap, IterationKind::kEntries); @@ -3702,7 +3713,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node, break; } - if (!FLAG_runtime_stats && shared->IsApiFunction()) { + if (!FLAG_runtime_stats && shared.object()->IsApiFunction()) { return ReduceCallApiFunction(node, shared); } return NoChange(); @@ -3750,19 +3761,16 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { return NoChange(); } - HeapObject* feedback_object; - if (nexus.GetFeedback()->GetHeapObjectIfStrong(&feedback_object) && - feedback_object->IsAllocationSite()) { + base::Optional<HeapObjectRef> feedback = + GetHeapObjectFeedback(broker(), nexus); + if (feedback.has_value() && feedback->IsAllocationSite()) { // The feedback is an AllocationSite, which means we have called the // Array function and collected transition (and pretenuring) feedback // for the resulting arrays. This has to be kept in sync with the // implementation in Ignition. - Handle<AllocationSite> site(AllocationSite::cast(feedback_object), - isolate()); - // Retrieve the Array function from the {node}. - Node* array_function = jsgraph()->HeapConstant( - handle(native_context()->array_function(), isolate())); + Node* array_function = + jsgraph()->Constant(native_context().array_function()); // Check that the {target} is still the {array_function}. Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, @@ -3778,40 +3786,42 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { node, NodeProperties::GetValueInput(node, i), i + 1); } NodeProperties::ReplaceValueInput(node, array_function, 1); - NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site)); + NodeProperties::ChangeOp( + node, javascript()->CreateArray( + arity, feedback->AsAllocationSite().object())); return Changed(node); - } else if (nexus.GetFeedback()->GetHeapObjectIfWeak(&feedback_object) && - !HeapObjectMatcher(new_target).HasValue()) { - Handle<HeapObject> object(feedback_object, isolate()); - if (object->IsConstructor()) { - Node* new_target_feedback = jsgraph()->Constant(object); - - // Check that the {new_target} is still the {new_target_feedback}. - Node* check = graph()->NewNode(simplified()->ReferenceEqual(), - new_target, new_target_feedback); - effect = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, - effect, control); - - // Specialize the JSConstruct node to the {new_target_feedback}. - NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1); - NodeProperties::ReplaceEffectInput(node, effect); - if (target == new_target) { - NodeProperties::ReplaceValueInput(node, new_target_feedback, 0); - } + } else if (feedback.has_value() && + !HeapObjectMatcher(new_target).HasValue() && + feedback->map().is_constructor()) { + Node* new_target_feedback = jsgraph()->Constant(*feedback); + + // Check that the {new_target} is still the {new_target_feedback}. + Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target, + new_target_feedback); + effect = graph()->NewNode( + simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, + effect, control); - // Try to further reduce the JSConstruct {node}. - Reduction const reduction = ReduceJSConstruct(node); - return reduction.Changed() ? reduction : Changed(node); + // Specialize the JSConstruct node to the {new_target_feedback}. + NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1); + NodeProperties::ReplaceEffectInput(node, effect); + if (target == new_target) { + NodeProperties::ReplaceValueInput(node, new_target_feedback, 0); } + + // Try to further reduce the JSConstruct {node}. + Reduction const reduction = ReduceJSConstruct(node); + return reduction.Changed() ? reduction : Changed(node); } } // Try to specialize JSConstruct {node}s with constant {target}s. HeapObjectMatcher m(target); if (m.HasValue()) { + HeapObjectRef target_ref = m.Ref(broker()).AsHeapObject(); + // Raise a TypeError if the {target} is not a constructor. - if (!m.Value()->IsConstructor()) { + if (!target_ref.map().is_constructor()) { NodeProperties::ReplaceValueInputs(node, target); NodeProperties::ChangeOp(node, javascript()->CallRuntime( @@ -3819,31 +3829,33 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { return Changed(node); } - if (m.Value()->IsJSFunction()) { - Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); + if (target_ref.IsJSFunction()) { + JSFunctionRef function = target_ref.AsJSFunction(); + function.Serialize(); // Do not reduce constructors with break points. - if (function->shared()->HasBreakInfo()) return NoChange(); + if (function.shared().HasBreakInfo()) return NoChange(); // Don't inline cross native context. - if (function->native_context() != *native_context()) return NoChange(); + if (!function.native_context().equals(native_context())) { + return NoChange(); + } // Check for known builtin functions. - int builtin_id = function->shared()->HasBuiltinId() - ? function->shared()->builtin_id() + int builtin_id = function.shared().HasBuiltinId() + ? function.shared().builtin_id() : Builtins::kNoBuiltinId; switch (builtin_id) { case Builtins::kArrayConstructor: { // TODO(bmeurer): Deal with Array subclasses here. - Handle<AllocationSite> site; // Turn the {node} into a {JSCreateArray} call. for (int i = arity; i > 0; --i) { NodeProperties::ReplaceValueInput( node, NodeProperties::GetValueInput(node, i), i + 1); } NodeProperties::ReplaceValueInput(node, new_target, 1); - NodeProperties::ChangeOp(node, - javascript()->CreateArray(arity, site)); + NodeProperties::ChangeOp( + node, javascript()->CreateArray(arity, Handle<AllocationSite>())); return Changed(node); } case Builtins::kObjectConstructor: { @@ -3858,7 +3870,8 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { // the value parameter is ignored, which is only the case if // the {new_target} and {target} are definitely not identical. HeapObjectMatcher mnew_target(new_target); - if (mnew_target.HasValue() && *mnew_target.Value() != *function) { + if (mnew_target.HasValue() && + !mnew_target.Ref(broker()).equals(function)) { // Drop the value inputs. for (int i = arity; i > 0; --i) node->RemoveInput(i); NodeProperties::ChangeOp(node, javascript()->Create()); @@ -3869,18 +3882,16 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { case Builtins::kPromiseConstructor: return ReducePromiseConstructor(node); case Builtins::kTypedArrayConstructor: - return ReduceTypedArrayConstructor( - node, handle(function->shared(), isolate())); + return ReduceTypedArrayConstructor(node, function.shared()); default: break; } - } else if (m.Value()->IsJSBoundFunction()) { - Handle<JSBoundFunction> function = - Handle<JSBoundFunction>::cast(m.Value()); - Handle<JSReceiver> bound_target_function( - function->bound_target_function(), isolate()); - Handle<FixedArray> bound_arguments(function->bound_arguments(), - isolate()); + } else if (target_ref.IsJSBoundFunction()) { + JSBoundFunctionRef function = target_ref.AsJSBoundFunction(); + function.Serialize(); + + ObjectRef bound_target_function = function.bound_target_function(); + FixedArrayRef bound_arguments = function.bound_arguments(); // Patch {node} to use [[BoundTargetFunction]]. NodeProperties::ReplaceValueInput( @@ -3898,10 +3909,9 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) { arity + 1); // Insert the [[BoundArguments]] for {node}. - for (int i = 0; i < bound_arguments->length(); ++i) { - node->InsertInput( - graph()->zone(), i + 1, - jsgraph()->Constant(handle(bound_arguments->get(i), isolate()))); + for (int i = 0; i < bound_arguments.length(); ++i) { + node->InsertInput(graph()->zone(), i + 1, + jsgraph()->Constant(bound_arguments.get(i))); arity++; } @@ -4308,32 +4318,6 @@ Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node, return Changed(node); } -namespace { - -// TODO(turbofan): This was copied from old compiler, might be too restrictive. -bool IsReadOnlyLengthDescriptor(Isolate* isolate, Handle<Map> jsarray_map) { - DCHECK(!jsarray_map->is_dictionary_map()); - Handle<Name> length_string = isolate->factory()->length_string(); - DescriptorArray* descriptors = jsarray_map->instance_descriptors(); - int number = descriptors->Search(*length_string, *jsarray_map); - DCHECK_NE(DescriptorArray::kNotFound, number); - return descriptors->GetDetails(number).IsReadOnly(); -} - -// TODO(turbofan): This was copied from old compiler, might be too restrictive. -bool CanInlineArrayResizeOperation(Isolate* isolate, Handle<Map> receiver_map) { - 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() && - isolate->IsAnyInitialArrayPrototype(receiver_prototype) && - !IsReadOnlyLengthDescriptor(isolate, receiver_map); -} - -} // namespace - // ES6 section 22.1.3.18 Array.prototype.push ( ) Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) { DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); @@ -4342,33 +4326,25 @@ Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) { return NoChange(); } - if (!isolate()->IsNoElementsProtectorIntact()) return NoChange(); - int const num_values = node->op()->ValueInputCount() - 2; Node* receiver = NodeProperties::GetValueInput(node, 1); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); - // Try to determine the {receiver} map(s). ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); - ElementsKind kind = receiver_maps[0]->elements_kind(); - - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayResizeOperation(isolate(), receiver_map)) - return NoChange(); - if (!UnionElementsKindUptoPackedness(&kind, receiver_map->elements_kind())) - return NoChange(); + ElementsKind kind; + if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind, true)) { + return NoChange(); } - // Install code dependencies on the {receiver} global array protector cell. dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); // If the {receiver_maps} information is not reliable, we need // to check that the {receiver} still has one of these maps. @@ -4456,34 +4432,24 @@ Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) { return NoChange(); } - if (!isolate()->IsNoElementsProtectorIntact()) return NoChange(); - Node* receiver = NodeProperties::GetValueInput(node, 1); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); - ElementsKind kind = receiver_maps[0]->elements_kind(); - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayResizeOperation(isolate(), receiver_map)) - return NoChange(); - // TODO(turbofan): Extend this to also handle fast holey double elements - // once we got the hole NaN mess sorted out in TurboFan/V8. - if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS) - return NoChange(); - if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind())) - return NoChange(); + ElementsKind kind; + if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } - // Install code dependencies on the {receiver} global array protector cell. dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); // If the {receiver_maps} information is not reliable, we need // to check that the {receiver} still has one of these maps. @@ -4573,7 +4539,6 @@ Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) { return NoChange(); } - if (!isolate()->IsNoElementsProtectorIntact()) return NoChange(); Node* target = NodeProperties::GetValueInput(node, 0); Node* receiver = NodeProperties::GetValueInput(node, 1); Node* context = NodeProperties::GetContextInput(node); @@ -4583,26 +4548,18 @@ Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) { ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); - ElementsKind kind = receiver_maps[0]->elements_kind(); - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayResizeOperation(isolate(), receiver_map)) - return NoChange(); - // TODO(turbofan): Extend this to also handle fast holey double elements - // once we got the hole NaN mess sorted out in TurboFan/V8. - if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS) - return NoChange(); - if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind())) - return NoChange(); + ElementsKind kind; + if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind)) { + return NoChange(); } - // Install code dependencies on the {receiver} global array protector cell. dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); // If the {receiver_maps} information is not reliable, we need // to check that the {receiver} still has one of these maps. @@ -4771,51 +4728,60 @@ Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) { return NoChange(); } - int arity = static_cast<int>(p.arity() - 2); - // Here we only optimize for cloning, that is when slice is called - // without arguments, or with a single argument that is the constant 0. - if (arity >= 2) return NoChange(); - if (arity == 1) { - NumberMatcher m(NodeProperties::GetValueInput(node, 2)); - if (!m.HasValue()) return NoChange(); - if (m.Value() != 0) return NoChange(); - } - Node* receiver = NodeProperties::GetValueInput(node, 1); + Node* start = node->op()->ValueInputCount() > 2 + ? NodeProperties::GetValueInput(node, 2) + : jsgraph()->ZeroConstant(); + Node* end = node->op()->ValueInputCount() > 3 + ? NodeProperties::GetValueInput(node, 3) + : jsgraph()->UndefinedConstant(); + Node* context = NodeProperties::GetContextInput(node); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); - // Try to determine the {receiver} map. + // Optimize for the case where we simply clone the {receiver}, + // i.e. when the {start} is zero and the {end} is undefined + // (meaning it will be set to {receiver}s "length" property). + if (!NumberMatcher(start).Is(0) || + !HeapObjectMatcher(end).Is(factory()->undefined_value())) { + return NoChange(); + } + + // Try to determine the {receiver} maps. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); - // Ensure that any changes to the Array species constructor cause deopt. + // We cannot optimize unless the Array[@@species] lookup chain is intact. if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange(); - dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->array_species_protector())); + // Check that the maps are of JSArray (and more). + // TODO(turbofan): Consider adding special case for the common pattern + // `slice.call(arguments)`, for example jQuery makes heavy use of that. bool can_be_holey = false; - // Check that the maps are of JSArray (and more) - for (Handle<Map> receiver_map : receiver_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) - return NoChange(); + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.supports_fast_array_iteration()) return NoChange(); - if (IsHoleyElementsKind(receiver_map->elements_kind())) can_be_holey = true; + if (IsHoleyElementsKind(receiver_map.elements_kind())) { + can_be_holey = true; + } } + // Install code dependency on the Array[@@species] protector. + dependencies()->DependOnProtector( + PropertyCellRef(broker(), factory()->array_species_protector())); + // Install code dependency on the array protector for holey arrays. if (can_be_holey) { dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); } - // If we have unreliable maps, we need a map check. - // This is actually redundant due to how JSNativeContextSpecialization - // reduces the load of slice, but we do it here nevertheless for consistency - // and robustness. + // If we have unreliable maps, we need a map check, as there might be + // side-effects caused by the evaluation of the {node}s parameters. if (result == NodeProperties::kUnreliableReceiverMaps) { effect = graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, @@ -4823,8 +4789,12 @@ Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) { receiver, effect, control); } - Node* context = NodeProperties::GetContextInput(node); - + // TODO(turbofan): We can do even better here, either adding a CloneArray + // simplified operator, whose output type indicates that it's an Array, + // saving subsequent checks, or yet better, by introducing new operators + // CopySmiOrObjectElements / CopyDoubleElements and inlining the JSArray + // allocation in here. That way we'd even get escape analysis and scalar + // replacement to help in some cases. Callable callable = Builtins::CallableFor(isolate(), Builtins::kCloneFastJSArray); auto call_descriptor = Linkage::GetStubCallDescriptor( @@ -4876,12 +4846,13 @@ Reduction JSCallReducer::ReduceArrayIterator(Node* node, IterationKind kind) { // Check if we know that {receiver} is a valid JSReceiver. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); - for (Handle<Map> receiver_map : receiver_maps) { - if (!receiver_map->IsJSReceiverMap()) return NoChange(); + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.IsJSReceiverMap()) return NoChange(); } // Morph the {node} into a JSCreateArrayIterator with the given {kind}. @@ -4897,14 +4868,14 @@ Reduction JSCallReducer::ReduceArrayIterator(Node* node, IterationKind kind) { namespace { -bool InferIteratedObjectMaps(Isolate* isolate, Node* iterator, +bool InferIteratedObjectMaps(JSHeapBroker* broker, Node* iterator, ZoneHandleSet<Map>* iterated_object_maps) { DCHECK_EQ(IrOpcode::kJSCreateArrayIterator, iterator->opcode()); Node* iterated_object = NodeProperties::GetValueInput(iterator, 0); Node* effect = NodeProperties::GetEffectInput(iterator); NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate, iterated_object, effect, + NodeProperties::InferReceiverMaps(broker, iterated_object, effect, iterated_object_maps); return result != NodeProperties::kNoReceiverMaps; } @@ -4931,40 +4902,37 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { // Try to infer the [[IteratedObject]] maps from the {iterator}. ZoneHandleSet<Map> iterated_object_maps; - if (!InferIteratedObjectMaps(isolate(), iterator, &iterated_object_maps)) { + if (!InferIteratedObjectMaps(broker(), iterator, &iterated_object_maps)) { return NoChange(); } DCHECK_NE(0, iterated_object_maps.size()); // Check that various {iterated_object_maps} have compatible elements kinds. - ElementsKind elements_kind = iterated_object_maps[0]->elements_kind(); + ElementsKind elements_kind = + MapRef(broker(), iterated_object_maps[0]).elements_kind(); if (IsFixedTypedArrayElementsKind(elements_kind)) { // TurboFan doesn't support loading from BigInt typed arrays yet. if (elements_kind == BIGUINT64_ELEMENTS || elements_kind == BIGINT64_ELEMENTS) { return NoChange(); } - for (Handle<Map> iterated_object_map : iterated_object_maps) { - if (iterated_object_map->elements_kind() != elements_kind) { + for (Handle<Map> map : iterated_object_maps) { + MapRef iterated_object_map(broker(), map); + if (iterated_object_map.elements_kind() != elements_kind) { return NoChange(); } } } else { - for (Handle<Map> iterated_object_map : iterated_object_maps) { - if (!CanInlineArrayIteratingBuiltin(isolate(), iterated_object_map)) { - return NoChange(); - } - if (!UnionElementsKindUptoSize(&elements_kind, - iterated_object_map->elements_kind())) { - return NoChange(); - } + if (!CanInlineArrayIteratingBuiltin(broker(), iterated_object_maps, + &elements_kind)) { + return NoChange(); } } // Install code dependency on the array protector for holey arrays. if (IsHoleyElementsKind(elements_kind)) { dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->no_elements_protector())); + PropertyCellRef(broker(), factory()->no_elements_protector())); } // Load the (current) {iterated_object} from the {iterator}. @@ -4980,14 +4948,14 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { iterated_object, effect, control); if (IsFixedTypedArrayElementsKind(elements_kind)) { - // See if we can skip the neutering check. - if (isolate()->IsArrayBufferNeuteringIntact()) { + // See if we can skip the detaching check. + if (isolate()->IsArrayBufferDetachingIntact()) { // Add a code dependency so we are deoptimized in case an ArrayBuffer - // gets neutered. + // gets detached. dependencies()->DependOnProtector(PropertyCellRef( - js_heap_broker(), factory()->array_buffer_neutering_protector())); + broker(), factory()->array_buffer_detaching_protector())); } else { - // Bail out if the {iterated_object}s JSArrayBuffer was neutered. + // Bail out if the {iterated_object}s JSArrayBuffer was detached. Node* buffer = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), iterated_object, effect, control); @@ -4998,10 +4966,10 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { simplified()->NumberEqual(), graph()->NewNode( simplified()->NumberBitwiseAnd(), buffer_bit_field, - jsgraph()->Constant(JSArrayBuffer::WasNeuteredBit::kMask)), + jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), jsgraph()->ZeroConstant()); effect = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered, + simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached, p.feedback()), check, effect, control); } @@ -5013,11 +4981,11 @@ Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { // latter case we even know that it's a Smi in UnsignedSmall range. FieldAccess index_access = AccessBuilder::ForJSArrayIteratorNextIndex(); if (IsFixedTypedArrayElementsKind(elements_kind)) { - index_access.type = TypeCache::Get().kJSTypedArrayLengthType; + index_access.type = TypeCache::Get()->kJSTypedArrayLengthType; index_access.machine_type = MachineType::TaggedSigned(); index_access.write_barrier_kind = kNoWriteBarrier; } else { - index_access.type = TypeCache::Get().kJSArrayLengthType; + index_access.type = TypeCache::Get()->kJSArrayLengthType; } Node* index = effect = graph()->NewNode(simplified()->LoadField(index_access), iterator, effect, control); @@ -5373,7 +5341,7 @@ Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) { Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); Node* context = NodeProperties::GetContextInput(node); - if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, + if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, JS_STRING_ITERATOR_TYPE)) { Node* string = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()), @@ -5438,8 +5406,7 @@ Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) { } // ES #sec-string.prototype.concat -Reduction JSCallReducer::ReduceStringPrototypeConcat( - Node* node, Handle<SharedFunctionInfo> shared) { +Reduction JSCallReducer::ReduceStringPrototypeConcat(Node* node) { if (node->op()->ValueInputCount() < 2 || node->op()->ValueInputCount() > 3) { return NoChange(); } @@ -5479,46 +5446,13 @@ Reduction JSCallReducer::ReduceStringPrototypeConcat( return Replace(value); } -Reduction JSCallReducer::ReduceAsyncFunctionPromiseCreate(Node* node) { - DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); - Node* context = NodeProperties::GetContextInput(node); - Node* effect = NodeProperties::GetEffectInput(node); - if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); - - // Install a code dependency on the promise hook protector cell. - dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); - - // Morph this {node} into a JSCreatePromise node. - RelaxControls(node); - node->ReplaceInput(0, context); - node->ReplaceInput(1, effect); - node->TrimInputCount(2); - NodeProperties::ChangeOp(node, javascript()->CreatePromise()); - return Changed(node); -} - -Reduction JSCallReducer::ReduceAsyncFunctionPromiseRelease(Node* node) { - DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); - if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); - - dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); - - // The AsyncFunctionPromiseRelease builtin is a no-op as long as neither - // the debugger is active nor any promise hook has been installed (ever). - Node* value = jsgraph()->UndefinedConstant(); - ReplaceWithValue(node, value); - return Replace(value); -} - Node* JSCallReducer::CreateArtificialFrameState( Node* node, Node* outer_frame_state, int parameter_count, BailoutId bailout_id, FrameStateType frame_state_type, - Handle<SharedFunctionInfo> shared, Node* context) { + const SharedFunctionInfoRef& shared, Node* context) { const FrameStateFunctionInfo* state_info = - common()->CreateFrameStateFunctionInfo(frame_state_type, - parameter_count + 1, 0, shared); + common()->CreateFrameStateFunctionInfo( + frame_state_type, parameter_count + 1, 0, shared.object()); const Operator* op = common()->FrameState( bailout_id, OutputFrameStateCombine::Ignore(), state_info); @@ -5562,16 +5496,16 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) { if (target != new_target) return NoChange(); dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); + PropertyCellRef(broker(), factory()->promise_hook_protector())); - Handle<SharedFunctionInfo> promise_shared( - handle(native_context()->promise_function()->shared(), isolate())); + SharedFunctionInfoRef promise_shared = + native_context().promise_function().shared(); // Insert a construct stub frame into the chain of frame states. This will // reconstruct the proper frame when deoptimizing within the constructor. // For the frame state, we only provide the executor parameter, even if more // arugments were passed. This is not observable from JS. - DCHECK_EQ(1, promise_shared->internal_formal_parameter_count()); + DCHECK_EQ(1, promise_shared.internal_formal_parameter_count()); Node* constructor_frame_state = CreateArtificialFrameState( node, outer_frame_state, 1, BailoutId::ConstructStubInvoke(), FrameStateType::kConstructStub, promise_shared, context); @@ -5604,45 +5538,42 @@ Reduction JSCallReducer::ReducePromiseConstructor(Node* node) { // 8. CreatePromiseResolvingFunctions // Allocate a promise context for the closures below. - Node* promise_context = effect = - graph()->NewNode(javascript()->CreateFunctionContext( - handle(native_context()->scope_info(), isolate()), - PromiseBuiltinsAssembler::kPromiseContextLength - - Context::MIN_CONTEXT_SLOTS, - FUNCTION_SCOPE), - context, effect, control); - effect = - graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot( - PromiseBuiltinsAssembler::kPromiseSlot)), - promise_context, promise, effect, control); + Node* promise_context = effect = graph()->NewNode( + javascript()->CreateFunctionContext( + handle(native_context().object()->scope_info(), isolate()), + PromiseBuiltins::kPromiseContextLength - Context::MIN_CONTEXT_SLOTS, + FUNCTION_SCOPE), + context, effect, control); + effect = graph()->NewNode( + simplified()->StoreField( + AccessBuilder::ForContextSlot(PromiseBuiltins::kPromiseSlot)), + promise_context, promise, effect, control); effect = graph()->NewNode( - simplified()->StoreField(AccessBuilder::ForContextSlot( - PromiseBuiltinsAssembler::kAlreadyResolvedSlot)), + simplified()->StoreField( + AccessBuilder::ForContextSlot(PromiseBuiltins::kAlreadyResolvedSlot)), promise_context, jsgraph()->FalseConstant(), effect, control); effect = graph()->NewNode( - simplified()->StoreField(AccessBuilder::ForContextSlot( - PromiseBuiltinsAssembler::kDebugEventSlot)), + simplified()->StoreField( + AccessBuilder::ForContextSlot(PromiseBuiltins::kDebugEventSlot)), promise_context, jsgraph()->TrueConstant(), effect, control); // Allocate the closure for the resolve case. - Handle<SharedFunctionInfo> resolve_shared( - native_context()->promise_capability_default_resolve_shared_fun(), - isolate()); - Node* resolve = effect = - graph()->NewNode(javascript()->CreateClosure( - resolve_shared, factory()->many_closures_cell(), - handle(resolve_shared->GetCode(), isolate())), - promise_context, effect, control); + SharedFunctionInfoRef resolve_shared = + native_context().promise_capability_default_resolve_shared_fun(); + Node* resolve = effect = graph()->NewNode( + javascript()->CreateClosure( + resolve_shared.object(), factory()->many_closures_cell(), + handle(resolve_shared.object()->GetCode(), isolate())), + promise_context, effect, control); // Allocate the closure for the reject case. - Handle<SharedFunctionInfo> reject_shared( - native_context()->promise_capability_default_reject_shared_fun(), - isolate()); - Node* reject = effect = - graph()->NewNode(javascript()->CreateClosure( - reject_shared, factory()->many_closures_cell(), - handle(reject_shared->GetCode(), isolate())), - promise_context, effect, control); + SharedFunctionInfoRef reject_shared = + native_context().promise_capability_default_reject_shared_fun(); + Node* reject = effect = graph()->NewNode( + javascript()->CreateClosure( + reject_shared.object(), factory()->many_closures_cell(), + handle(reject_shared.object()->GetCode(), isolate())), + promise_context, effect, control); const std::vector<Node*> checkpoint_parameters_continuation( {jsgraph()->UndefinedConstant() /* receiver */, promise, reject}); @@ -5720,7 +5651,7 @@ Reduction JSCallReducer::ReducePromiseInternalConstructor(Node* node) { if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange(); dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); + PropertyCellRef(broker(), factory()->promise_hook_protector())); // Create a new pending promise. Node* value = effect = @@ -5798,22 +5729,25 @@ Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) { // Check if we know something about {receiver} already. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); // Check whether all {receiver_maps} are JSPromise maps and // have the initial Promise.prototype as their [[Prototype]]. - for (Handle<Map> receiver_map : receiver_maps) { - if (!receiver_map->IsJSPromiseMap()) return NoChange(); - if (receiver_map->prototype() != native_context()->promise_prototype()) { + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.IsJSPromiseMap()) return NoChange(); + receiver_map.SerializePrototype(); + if (!receiver_map.prototype().equals( + native_context().promise_prototype())) { return NoChange(); } } dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_then_protector())); + PropertyCellRef(broker(), factory()->promise_then_protector())); // If the {receiver_maps} aren't reliable, we need to repeat the // map check here, guarded by the CALL_IC. @@ -5827,8 +5761,7 @@ Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) { // Massage the {node} to call "then" instead by first removing all inputs // following the onRejected parameter, and then filling up the parameters // to two inputs from the left with undefined. - Node* target = - jsgraph()->Constant(handle(native_context()->promise_then(), isolate())); + Node* target = jsgraph()->Constant(native_context().promise_then()); NodeProperties::ReplaceValueInput(node, target, 0); NodeProperties::ReplaceEffectInput(node, effect); for (; arity > 1; --arity) node->RemoveInput(3); @@ -5875,26 +5808,29 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { // Check if we know something about {receiver} already. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); // Check whether all {receiver_maps} are JSPromise maps and // have the initial Promise.prototype as their [[Prototype]]. - for (Handle<Map> receiver_map : receiver_maps) { - if (!receiver_map->IsJSPromiseMap()) return NoChange(); - if (receiver_map->prototype() != native_context()->promise_prototype()) { + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.IsJSPromiseMap()) return NoChange(); + receiver_map.SerializePrototype(); + if (!receiver_map.prototype().equals( + native_context().promise_prototype())) { return NoChange(); } } dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); + PropertyCellRef(broker(), factory()->promise_hook_protector())); dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_then_protector())); - dependencies()->DependOnProtector(PropertyCellRef( - js_heap_broker(), factory()->promise_species_protector())); + PropertyCellRef(broker(), factory()->promise_then_protector())); + dependencies()->DependOnProtector( + PropertyCellRef(broker(), factory()->promise_species_protector())); // If the {receiver_maps} aren't reliable, we need to repeat the // map check here, guarded by the CALL_IC. @@ -5916,44 +5852,44 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { Node* catch_true; Node* then_true; { - Node* context = jsgraph()->HeapConstant(native_context()); - Node* constructor = jsgraph()->HeapConstant( - handle(native_context()->promise_function(), isolate())); + Node* context = jsgraph()->Constant(native_context()); + Node* constructor = + jsgraph()->Constant(native_context().promise_function()); // Allocate shared context for the closures below. context = etrue = graph()->NewNode( javascript()->CreateFunctionContext( - handle(native_context()->scope_info(), isolate()), - PromiseBuiltinsAssembler::kPromiseFinallyContextLength - + handle(native_context().object()->scope_info(), isolate()), + PromiseBuiltins::kPromiseFinallyContextLength - Context::MIN_CONTEXT_SLOTS, FUNCTION_SCOPE), context, etrue, if_true); - etrue = - graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot( - PromiseBuiltinsAssembler::kOnFinallySlot)), - context, on_finally, etrue, if_true); - etrue = - graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot( - PromiseBuiltinsAssembler::kConstructorSlot)), - context, constructor, etrue, if_true); + etrue = graph()->NewNode( + simplified()->StoreField( + AccessBuilder::ForContextSlot(PromiseBuiltins::kOnFinallySlot)), + context, on_finally, etrue, if_true); + etrue = graph()->NewNode( + simplified()->StoreField( + AccessBuilder::ForContextSlot(PromiseBuiltins::kConstructorSlot)), + context, constructor, etrue, if_true); // Allocate the closure for the reject case. - Handle<SharedFunctionInfo> catch_finally( - native_context()->promise_catch_finally_shared_fun(), isolate()); - catch_true = etrue = - graph()->NewNode(javascript()->CreateClosure( - catch_finally, factory()->many_closures_cell(), - handle(catch_finally->GetCode(), isolate())), - context, etrue, if_true); + SharedFunctionInfoRef catch_finally = + native_context().promise_catch_finally_shared_fun(); + catch_true = etrue = graph()->NewNode( + javascript()->CreateClosure( + catch_finally.object(), factory()->many_closures_cell(), + handle(catch_finally.object()->GetCode(), isolate())), + context, etrue, if_true); // Allocate the closure for the fulfill case. - Handle<SharedFunctionInfo> then_finally( - native_context()->promise_then_finally_shared_fun(), isolate()); - then_true = etrue = - graph()->NewNode(javascript()->CreateClosure( - then_finally, factory()->many_closures_cell(), - handle(then_finally->GetCode(), isolate())), - context, etrue, if_true); + SharedFunctionInfoRef then_finally = + native_context().promise_then_finally_shared_fun(); + then_true = etrue = graph()->NewNode( + javascript()->CreateClosure( + then_finally.object(), factory()->many_closures_cell(), + handle(then_finally.object()->GetCode(), isolate())), + context, etrue, if_true); } Node* if_false = graph()->NewNode(common()->IfFalse(), branch); @@ -5979,8 +5915,7 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { // Massage the {node} to call "then" instead by first removing all inputs // following the onFinally parameter, and then replacing the only parameter // input with the {on_finally} value. - Node* target = - jsgraph()->Constant(handle(native_context()->promise_then(), isolate())); + Node* target = jsgraph()->Constant(native_context().promise_then()); NodeProperties::ReplaceValueInput(node, target, 0); NodeProperties::ReplaceEffectInput(node, effect); NodeProperties::ReplaceControlInput(node, control); @@ -6028,7 +5963,7 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { // Check if we know something about {receiver} already. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult infer_receiver_maps_result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) { return NoChange(); @@ -6037,17 +5972,20 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { // Check whether all {receiver_maps} are JSPromise maps and // have the initial Promise.prototype as their [[Prototype]]. - for (Handle<Map> receiver_map : receiver_maps) { - if (!receiver_map->IsJSPromiseMap()) return NoChange(); - if (receiver_map->prototype() != native_context()->promise_prototype()) { + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.IsJSPromiseMap()) return NoChange(); + receiver_map.SerializePrototype(); + if (!receiver_map.prototype().equals( + native_context().promise_prototype())) { return NoChange(); } } dependencies()->DependOnProtector( - PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector())); - dependencies()->DependOnProtector(PropertyCellRef( - js_heap_broker(), factory()->promise_species_protector())); + PropertyCellRef(broker(), factory()->promise_hook_protector())); + dependencies()->DependOnProtector( + PropertyCellRef(broker(), factory()->promise_species_protector())); // If the {receiver_maps} aren't reliable, we need to repeat the // map check here, guarded by the CALL_IC. @@ -6078,6 +6016,18 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { result = effect = graph()->NewNode( javascript()->PerformPromiseThen(), receiver, on_fulfilled, on_rejected, result, context, frame_state, effect, control); + + // At this point we know that {result} is going to have the + // initial Promise map, since even if {PerformPromiseThen} + // above called into the host rejection tracker, the {result} + // doesn't escape to user JavaScript. So bake this information + // into the graph such that subsequent passes can use the + // information for further optimizations. + MapRef result_map = native_context().promise_function().initial_map(); + effect = graph()->NewNode( + simplified()->MapGuard(ZoneHandleSet<Map>(result_map.object())), result, + effect, control); + ReplaceWithValue(node, result, effect, control); return Replace(result); } @@ -6097,7 +6047,7 @@ Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) { // Check if we know something about {receiver} already. ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult infer_receiver_maps_result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) { return NoChange(); @@ -6105,8 +6055,9 @@ Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) { DCHECK_NE(0, receiver_maps.size()); // Only reduce when all {receiver_maps} are JSReceiver maps. - for (Handle<Map> receiver_map : receiver_maps) { - if (!receiver_map->IsJSReceiverMap()) return NoChange(); + for (Handle<Map> map : receiver_maps) { + MapRef receiver_map(broker(), map); + if (!receiver_map.IsJSReceiverMap()) return NoChange(); } // Morph the {node} into a JSPromiseResolve operation. @@ -6123,7 +6074,7 @@ Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) { // ES #sec-typedarray-constructors Reduction JSCallReducer::ReduceTypedArrayConstructor( - Node* node, Handle<SharedFunctionInfo> shared) { + Node* node, const SharedFunctionInfoRef& shared) { DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); ConstructParameters const& p = ConstructParametersOf(node->op()); int arity = static_cast<int>(p.arity() - 2); @@ -6293,7 +6244,7 @@ Reduction JSCallReducer::ReduceMapPrototypeGet(Node* node) { Node* control = NodeProperties::GetControlInput(node); Node* key = NodeProperties::GetValueInput(node, 2); - if (!NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, + if (!NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, JS_MAP_TYPE)) return NoChange(); @@ -6338,7 +6289,7 @@ Reduction JSCallReducer::ReduceMapPrototypeHas(Node* node) { Node* control = NodeProperties::GetControlInput(node); Node* key = NodeProperties::GetValueInput(node, 2); - if (!NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, + if (!NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, JS_MAP_TYPE)) return NoChange(); @@ -6379,7 +6330,7 @@ Reduction JSCallReducer::ReduceCollectionIteration( Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); if (NodeProperties::HasInstanceTypeWitness( - isolate(), receiver, effect, + broker(), receiver, effect, InstanceTypeForCollectionKind(collection_kind))) { Node* js_create_iterator = effect = graph()->NewNode( javascript()->CreateCollectionIterator(collection_kind, iteration_kind), @@ -6397,14 +6348,14 @@ Reduction JSCallReducer::ReduceCollectionPrototypeSize( Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); if (NodeProperties::HasInstanceTypeWitness( - isolate(), receiver, effect, + broker(), receiver, effect, InstanceTypeForCollectionKind(collection_kind))) { Node* table = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, effect, control); Node* value = effect = graph()->NewNode( simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfElements()), + AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()), table, effect, control); ReplaceWithValue(node, value, effect, control); return Replace(value); @@ -6438,7 +6389,7 @@ Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( InstanceType receiver_instance_type; ZoneHandleSet<Map> receiver_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), receiver, effect, + NodeProperties::InferReceiverMaps(broker(), receiver, effect, &receiver_maps); if (result == NodeProperties::kNoReceiverMaps) return NoChange(); DCHECK_NE(0, receiver_maps.size()); @@ -6471,7 +6422,7 @@ Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( receiver, effect, control); Node* next_table = effect = graph()->NewNode(simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNextTable()), + AccessBuilder::ForOrderedHashMapOrSetNextTable()), table, effect, control); Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table); control = @@ -6500,7 +6451,7 @@ Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( jsgraph()->NoContextConstant(), effect); index = effect = graph()->NewNode( - common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), index, + common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), index, effect, control); // Update the {index} and {table} on the {receiver}. @@ -6540,15 +6491,15 @@ Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( // Compute the currently used capacity. Node* number_of_buckets = effect = graph()->NewNode( simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets()), + AccessBuilder::ForOrderedHashMapOrSetNumberOfBuckets()), table, effect, control); Node* number_of_elements = effect = graph()->NewNode( simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfElements()), + AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()), table, effect, control); Node* number_of_deleted_elements = effect = graph()->NewNode( simplified()->LoadField( - AccessBuilder::ForOrderedHashTableBaseNumberOfDeletedElements()), + AccessBuilder::ForOrderedHashMapOrSetNumberOfDeletedElements()), table, effect, control); Node* used_capacity = graph()->NewNode(simplified()->NumberAdd(), number_of_elements, @@ -6564,7 +6515,7 @@ Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop); Node* index = effect = graph()->NewNode( - common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), iloop, + common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), iloop, eloop, control); { Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index, @@ -6590,6 +6541,8 @@ Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( Node* etrue0 = effect; { // Load the key of the entry. + STATIC_ASSERT(OrderedHashMap::HashTableStartIndex() == + OrderedHashSet::HashTableStartIndex()); Node* entry_start_position = graph()->NewNode( simplified()->NumberAdd(), graph()->NewNode( @@ -6597,7 +6550,7 @@ Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext( graph()->NewNode(simplified()->NumberMultiply(), index, jsgraph()->Constant(entry_size)), number_of_buckets), - jsgraph()->Constant(OrderedHashTableBase::kHashTableStartIndex)); + jsgraph()->Constant(OrderedHashMap::HashTableStartIndex())); Node* entry_key = etrue0 = graph()->NewNode( simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), table, entry_start_position, etrue0, if_true0); @@ -6716,20 +6669,20 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor( Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); - if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, + if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, instance_type)) { // Load the {receiver}s field. Node* value = effect = graph()->NewNode(simplified()->LoadField(access), receiver, effect, control); - // See if we can skip the neutering check. - if (isolate()->IsArrayBufferNeuteringIntact()) { + // See if we can skip the detaching check. + if (isolate()->IsArrayBufferDetachingIntact()) { // Add a code dependency so we are deoptimized in case an ArrayBuffer - // gets neutered. + // gets detached. dependencies()->DependOnProtector(PropertyCellRef( - js_heap_broker(), factory()->array_buffer_neutering_protector())); + broker(), factory()->array_buffer_detaching_protector())); } else { - // Check whether {receiver}s JSArrayBuffer was neutered. + // Check whether {receiver}s JSArrayBuffer was detached. Node* buffer = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), receiver, effect, control); @@ -6740,11 +6693,11 @@ Reduction JSCallReducer::ReduceArrayBufferViewAccessor( simplified()->NumberEqual(), graph()->NewNode( simplified()->NumberBitwiseAnd(), buffer_bit_field, - jsgraph()->Constant(JSArrayBuffer::WasNeuteredBit::kMask)), + jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), jsgraph()->ZeroConstant()); // TODO(turbofan): Ideally we would bail out here if the {receiver}s - // JSArrayBuffer was neutered, but there's no way to guard against + // JSArrayBuffer was detached, but there's no way to guard against // deoptimization loops right now, since the JSCall {node} is usually // created from a LOAD_IC inlining, and so there's no CALL_IC slot // from which we could use the speculation bit. @@ -6802,68 +6755,57 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, } // Only do stuff if the {receiver} is really a DataView. - if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, + if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, JS_DATA_VIEW_TYPE)) { + Node* byte_offset; + // Check that the {offset} is within range for the {receiver}. HeapObjectMatcher m(receiver); if (m.HasValue()) { // We only deal with DataViews here whose [[ByteLength]] is at least - // {element_size} and less than 2^31-{element_size}. - Handle<JSDataView> dataview = Handle<JSDataView>::cast(m.Value()); - if (dataview->byte_length() < element_size || - dataview->byte_length() - element_size > kMaxInt) { - return NoChange(); - } - - // The {receiver}s [[ByteOffset]] must be within Unsigned31 range. - if (dataview->byte_offset() > kMaxInt) { - return NoChange(); - } + // {element_size}, as for all other DataViews it'll be out-of-bounds. + JSDataViewRef dataview = m.Ref(broker()).AsJSDataView(); + if (dataview.byte_length() < element_size) return NoChange(); // Check that the {offset} is within range of the {byte_length}. Node* byte_length = - jsgraph()->Constant(dataview->byte_length() - (element_size - 1)); + jsgraph()->Constant(dataview.byte_length() - (element_size - 1)); offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, byte_length, effect, control); - // Add the [[ByteOffset]] to compute the effective offset. - Node* byte_offset = jsgraph()->Constant(dataview->byte_offset()); - offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); + // Load the [[ByteOffset]] from the {dataview}. + byte_offset = jsgraph()->Constant(dataview.byte_offset()); } else { // We only deal with DataViews here that have Smi [[ByteLength]]s. Node* byte_length = effect = graph()->NewNode(simplified()->LoadField( AccessBuilder::ForJSArrayBufferViewByteLength()), receiver, effect, control); - byte_length = effect = graph()->NewNode( - simplified()->CheckSmi(p.feedback()), byte_length, effect, control); + + if (element_size > 1) { + // For non-byte accesses we also need to check that the {offset} + // plus the {element_size}-1 fits within the given {byte_length}. + // So to keep this as a single check on the {offset}, we subtract + // the {element_size}-1 from the {byte_length} here (clamped to + // positive safe integer range), and perform a check against that + // with the {offset} below. + byte_length = graph()->NewNode( + simplified()->NumberMax(), jsgraph()->ZeroConstant(), + graph()->NewNode(simplified()->NumberSubtract(), byte_length, + jsgraph()->Constant(element_size - 1))); + } // Check that the {offset} is within range of the {byte_length}. offset = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, byte_length, effect, control); - if (element_size > 0) { - // For non-byte accesses we also need to check that the {offset} - // plus the {element_size}-1 fits within the given {byte_length}. - Node* end_offset = - graph()->NewNode(simplified()->NumberAdd(), offset, - jsgraph()->Constant(element_size - 1)); - effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()), - end_offset, byte_length, effect, control); - } - - // The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi. - Node* byte_offset = effect = + // Also load the [[ByteOffset]] from the {receiver}. + byte_offset = effect = graph()->NewNode(simplified()->LoadField( AccessBuilder::ForJSArrayBufferViewByteOffset()), receiver, effect, control); - byte_offset = effect = graph()->NewNode( - simplified()->CheckSmi(p.feedback()), byte_offset, effect, control); - - // Compute the buffer index at which we'll read. - offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset); } // Coerce {is_little_endian} to boolean. @@ -6878,18 +6820,18 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, value, effect, control); } - // Get the underlying buffer and check that it has not been neutered. + // Get the underlying buffer and check that it has not been detached. Node* buffer = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), receiver, effect, control); - if (isolate()->IsArrayBufferNeuteringIntact()) { + if (isolate()->IsArrayBufferDetachingIntact()) { // Add a code dependency so we are deoptimized in case an ArrayBuffer - // gets neutered. + // gets detached. dependencies()->DependOnProtector(PropertyCellRef( - js_heap_broker(), factory()->array_buffer_neutering_protector())); + broker(), factory()->array_buffer_detaching_protector())); } else { - // Bail out if the {buffer} was neutered. + // Bail out if the {buffer} was detached. Node* buffer_bit_field = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), buffer, effect, control); @@ -6897,10 +6839,10 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, simplified()->NumberEqual(), graph()->NewNode( simplified()->NumberBitwiseAnd(), buffer_bit_field, - jsgraph()->Constant(JSArrayBuffer::WasNeuteredBit::kMask)), + jsgraph()->Constant(JSArrayBuffer::WasDetachedBit::kMask)), jsgraph()->ZeroConstant()); effect = graph()->NewNode( - simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered, + simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasDetached, p.feedback()), check, effect, control); } @@ -6913,15 +6855,17 @@ Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, switch (access) { case DataViewAccess::kGet: // Perform the load. - value = effect = graph()->NewNode( - simplified()->LoadDataViewElement(element_type), buffer, - backing_store, offset, is_little_endian, effect, control); + value = effect = + graph()->NewNode(simplified()->LoadDataViewElement(element_type), + buffer, backing_store, byte_offset, offset, + is_little_endian, effect, control); break; case DataViewAccess::kSet: // Perform the store. - effect = graph()->NewNode( - simplified()->StoreDataViewElement(element_type), buffer, - backing_store, offset, value, is_little_endian, effect, control); + effect = + graph()->NewNode(simplified()->StoreDataViewElement(element_type), + buffer, backing_store, byte_offset, offset, value, + is_little_endian, effect, control); value = jsgraph()->UndefinedConstant(); break; } @@ -6989,7 +6933,7 @@ Reduction JSCallReducer::ReduceDatePrototypeGetTime(Node* node) { Node* receiver = NodeProperties::GetValueInput(node, 1); Node* effect = NodeProperties::GetEffectInput(node); Node* control = NodeProperties::GetControlInput(node); - if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect, + if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, JS_DATE_TYPE)) { Node* value = effect = graph()->NewNode( simplified()->LoadField(AccessBuilder::ForJSDateValue()), receiver, @@ -7053,8 +6997,7 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { // Check if we know something about the {regexp}. ZoneHandleSet<Map> regexp_maps; NodeProperties::InferReceiverMapsResult result = - NodeProperties::InferReceiverMaps(isolate(), regexp, effect, - ®exp_maps); + NodeProperties::InferReceiverMaps(broker(), regexp, effect, ®exp_maps); bool need_map_check = false; switch (result) { @@ -7068,31 +7011,63 @@ Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { } for (auto map : regexp_maps) { - if (map->instance_type() != JS_REGEXP_TYPE) return NoChange(); + MapRef receiver_map(broker(), map); + if (receiver_map.instance_type() != JS_REGEXP_TYPE) return NoChange(); } // Compute property access info for "exec" on {resolution}. PropertyAccessInfo ai_exec; - AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(), - native_context(), graph()->zone()); + AccessInfoFactory access_info_factory( + broker(), dependencies(), native_context().object(), graph()->zone()); if (!access_info_factory.ComputePropertyAccessInfo( MapHandles(regexp_maps.begin(), regexp_maps.end()), factory()->exec_string(), AccessMode::kLoad, &ai_exec)) { return NoChange(); } // If "exec" has been modified on {regexp}, we can't do anything. - if (!ai_exec.IsDataConstant()) return NoChange(); - Handle<Object> exec_on_proto = ai_exec.constant(); - if (*exec_on_proto != *isolate()->regexp_exec_function()) return NoChange(); + if (ai_exec.IsDataConstant()) { + if (!ai_exec.constant().is_identical_to( + isolate()->regexp_exec_function())) { + return NoChange(); + } + } else if (ai_exec.IsDataConstantField()) { + Handle<JSObject> holder; + // Do not reduce if the exec method is not on the prototype chain. + if (!ai_exec.holder().ToHandle(&holder)) return NoChange(); + + // Bail out if the exec method is not the original one. + Handle<Object> constant = JSObject::FastPropertyAt( + holder, Representation::Tagged(), ai_exec.field_index()); + if (!constant.is_identical_to(isolate()->regexp_exec_function())) { + return NoChange(); + } + + // Protect the prototype chain from changes. + dependencies()->DependOnStablePrototypeChains( + broker(), ai_exec.receiver_maps(), JSObjectRef(broker(), holder)); + + // Protect the exec method change in the holder. + Handle<Object> exec_on_proto; + Handle<Map> holder_map(holder->map(), isolate()); + Handle<DescriptorArray> descriptors(holder_map->instance_descriptors(), + isolate()); + int descriptor_index = + descriptors->Search(*(factory()->exec_string()), *holder_map); + CHECK_NE(descriptor_index, DescriptorArray::kNotFound); - PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(), - dependencies()); + dependencies()->DependOnFieldType(MapRef(broker(), holder_map), + descriptor_index); + } else { + return NoChange(); + } + + PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); // Add proper dependencies on the {regexp}s [[Prototype]]s. Handle<JSObject> holder; if (ai_exec.holder().ToHandle(&holder)) { dependencies()->DependOnStablePrototypeChains( - js_heap_broker(), native_context(), ai_exec.receiver_maps(), holder); + broker(), ai_exec.receiver_maps(), JSObjectRef(broker(), holder)); } if (need_map_check) { @@ -7145,8 +7120,8 @@ Reduction JSCallReducer::ReduceNumberConstructor(Node* node) { Node* frame_state = NodeProperties::GetFrameStateInput(node); // Create the artificial frame state in the middle of the Number constructor. - Handle<SharedFunctionInfo> shared_info( - handle(native_context()->number_function()->shared(), isolate())); + SharedFunctionInfoRef shared_info = + native_context().number_function().shared(); Node* stack_parameters[] = {receiver}; int stack_parameter_count = arraysize(stack_parameters); Node* continuation_frame_state = @@ -7169,11 +7144,6 @@ Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } Factory* JSCallReducer::factory() const { return isolate()->factory(); } -Handle<JSGlobalProxy> JSCallReducer::global_proxy() const { - return handle(JSGlobalProxy::cast(native_context()->global_proxy()), - isolate()); -} - CommonOperatorBuilder* JSCallReducer::common() const { return jsgraph()->common(); } |