[turbofan] Second part of brokerization/serialization for instanceof

Serialize for all cases of JSNativeContextSpecialization::ReduceJSInstanceOf.

Bug: v8:7790
Change-Id: I147991353b86619808257a92961b7051105511f1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1722558
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62965}
This commit is contained in:
Georg Neis 2019-07-29 19:30:17 +02:00 committed by Commit Bot
parent e016562bf9
commit 6636420fce
6 changed files with 260 additions and 79 deletions

View File

@ -507,7 +507,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) {
// - target, which is Function.prototype.bind JSFunction
// - receiver, which is the [[BoundTargetFunction]]
// - bound_this (optional), which is the [[BoundThis]]
// - and all the remaining value inouts are [[BoundArguments]]
// - and all the remaining value inputs are [[BoundArguments]]
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* bound_this = (node->op()->ValueInputCount() < 3)
? jsgraph()->UndefinedConstant()

View File

@ -2155,8 +2155,9 @@ JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone,
tracing_enabled_(tracing_enabled),
feedback_(zone()),
bytecode_analyses_(zone()),
ais_for_loading_then_(zone()),
ais_for_loading_exec_(zone()) {
ais_for_loading_exec_(zone()),
ais_for_loading_has_instance_(zone()),
ais_for_loading_then_(zone()) {
// Note that this initialization of the refs_ pointer with the minimal
// initial capacity is redundant in the normal use case (concurrent
// compilation enabled, standard objects to be serialized), as the map
@ -4138,8 +4139,29 @@ base::Optional<NameRef> JSHeapBroker::GetNameFeedback(
PropertyAccessInfo JSHeapBroker::GetAccessInfoForLoadingThen(MapRef map) {
auto access_info = ais_for_loading_then_.find(map);
if (access_info == ais_for_loading_then_.end()) {
TRACE_BROKER_MISSING(this,
"access info for property 'then' on map " << map);
return PropertyAccessInfo::Invalid(zone());
}
return access_info->second;
}
PropertyAccessInfo JSHeapBroker::GetAccessInfoForLoadingHasInstance(
MapRef map) {
auto access_info = ais_for_loading_has_instance_.find(map);
if (access_info == ais_for_loading_has_instance_.end()) {
TRACE_BROKER_MISSING(
this, "access info for reducing JSResolvePromise with map " << map);
this, "access info for property Symbol.hasInstance on map " << map);
return PropertyAccessInfo::Invalid(zone());
}
return access_info->second;
}
PropertyAccessInfo JSHeapBroker::GetAccessInfoForLoadingExec(MapRef map) {
auto access_info = ais_for_loading_exec_.find(map);
if (access_info == ais_for_loading_exec_.end()) {
TRACE_BROKER_MISSING(this,
"access info for property 'exec' on map " << map);
return PropertyAccessInfo::Invalid(zone());
}
return access_info->second;
@ -4157,30 +4179,27 @@ void JSHeapBroker::CreateAccessInfoForLoadingThen(
}
}
PropertyAccessInfo JSHeapBroker::GetAccessInfoForLoadingExec(MapRef map) {
auto access_info = ais_for_loading_exec_.find(map);
if (access_info == ais_for_loading_exec_.end()) {
TRACE_BROKER_MISSING(this,
"access info for property 'exec' on map " << map);
return PropertyAccessInfo::Invalid(zone());
}
return access_info->second;
PropertyAccessInfo const& JSHeapBroker::CreateAccessInfoForLoadingHasInstance(
MapRef map, CompilationDependencies* dependencies) {
auto it = ais_for_loading_has_instance_.find(map);
if (it != ais_for_loading_has_instance_.end()) return it->second;
AccessInfoFactory access_info_factory(this, dependencies, zone());
auto access_info = access_info_factory.ComputePropertyAccessInfo(
map.object(), isolate()->factory()->has_instance_symbol(),
AccessMode::kLoad);
return ais_for_loading_has_instance_.insert({map, access_info}).first->second;
}
PropertyAccessInfo const& JSHeapBroker::CreateAccessInfoForLoadingExec(
MapRef map, CompilationDependencies* dependencies) {
auto access_info = ais_for_loading_exec_.find(map);
if (access_info != ais_for_loading_exec_.end()) {
return access_info->second;
}
auto it = ais_for_loading_exec_.find(map);
if (it != ais_for_loading_exec_.end()) return it->second;
ZoneVector<PropertyAccessInfo> access_infos(zone());
AccessInfoFactory access_info_factory(this, dependencies, zone());
PropertyAccessInfo ai_exec = access_info_factory.ComputePropertyAccessInfo(
auto access_info = access_info_factory.ComputePropertyAccessInfo(
map.object(), isolate()->factory()->exec_string(), AccessMode::kLoad);
auto inserted_ai = ais_for_loading_exec_.insert(std::make_pair(map, ai_exec));
return inserted_ai.first->second;
return ais_for_loading_exec_.insert({map, access_info}).first->second;
}
ElementAccessFeedback const* ProcessedFeedback::AsElementAccess() const {

View File

@ -117,12 +117,15 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
// If there is no result stored for {map}, we return an Invalid
// PropertyAccessInfo.
PropertyAccessInfo GetAccessInfoForLoadingThen(MapRef map);
void CreateAccessInfoForLoadingThen(MapRef map,
CompilationDependencies* dependencies);
PropertyAccessInfo GetAccessInfoForLoadingExec(MapRef map);
PropertyAccessInfo GetAccessInfoForLoadingHasInstance(MapRef map);
PropertyAccessInfo GetAccessInfoForLoadingThen(MapRef map);
PropertyAccessInfo const& CreateAccessInfoForLoadingExec(
MapRef map, CompilationDependencies* dependencies);
PropertyAccessInfo const& CreateAccessInfoForLoadingHasInstance(
MapRef map, CompilationDependencies* dependencies);
void CreateAccessInfoForLoadingThen(MapRef map,
CompilationDependencies* dependencies);
std::ostream& Trace();
void IncrementTracingIndentation();
@ -156,8 +159,9 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
typedef ZoneUnorderedMap<MapRef, PropertyAccessInfo, ObjectRef::Hash,
ObjectRef::Equal>
MapToAccessInfos;
MapToAccessInfos ais_for_loading_then_;
MapToAccessInfos ais_for_loading_exec_;
MapToAccessInfos ais_for_loading_has_instance_;
MapToAccessInfos ais_for_loading_then_;
static const size_t kMinimalRefsBucketCount = 8; // must be power of 2
static const size_t kInitialRefsBucketCount = 1024; // must be power of 2

View File

@ -398,22 +398,31 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// we have feedback from the InstanceOfIC.
Handle<JSObject> receiver;
HeapObjectMatcher m(constructor);
if (m.HasValue() && m.Value()->IsJSObject()) {
receiver = Handle<JSObject>::cast(m.Value());
if (m.HasValue() && m.Ref(broker()).IsJSObject()) {
receiver = m.Ref(broker()).AsJSObject().object();
} else if (p.feedback().IsValid()) {
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) return NoChange();
if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) {
return NoChange();
}
} else {
return NoChange();
}
Handle<Map> receiver_map(receiver->map(), isolate());
// Compute property access info for @@hasInstance on the constructor.
AccessInfoFactory access_info_factory(broker(), dependencies(),
graph()->zone());
PropertyAccessInfo access_info =
access_info_factory.ComputePropertyAccessInfo(
receiver_map, factory()->has_instance_symbol(), AccessMode::kLoad);
JSObjectRef receiver_ref(broker(), receiver);
MapRef receiver_map = receiver_ref.map();
PropertyAccessInfo access_info = PropertyAccessInfo::Invalid(graph()->zone());
if (FLAG_concurrent_inlining) {
access_info = broker()->GetAccessInfoForLoadingHasInstance(receiver_map);
} else {
AccessInfoFactory access_info_factory(broker(), dependencies(),
graph()->zone());
access_info = access_info_factory.ComputePropertyAccessInfo(
receiver_map.object(), factory()->has_instance_symbol(),
AccessMode::kLoad);
}
if (access_info.IsInvalid()) return NoChange();
access_info.RecordDependencies(dependencies());
@ -422,7 +431,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
if (access_info.IsNotFound()) {
// If there's no @@hasInstance handler, the OrdinaryHasInstance operation
// takes over, but that requires the constructor to be callable.
if (!receiver_map->is_callable()) return NoChange();
if (!receiver_map.is_callable()) return NoChange();
dependencies()->DependOnStablePrototypeChains(access_info.receiver_maps(),
kStartAtPrototype);
@ -441,17 +450,15 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
}
if (access_info.IsDataConstant()) {
// Determine actual holder.
Handle<JSObject> holder;
bool found_on_proto = access_info.holder().ToHandle(&holder);
if (!found_on_proto) holder = receiver;
FieldIndex field_index = access_info.field_index();
Handle<Object> constant = JSObject::FastPropertyAt(
holder, access_info.field_representation(), field_index);
if (!constant->IsCallable()) {
JSObjectRef holder_ref =
found_on_proto ? JSObjectRef(broker(), holder) : receiver_ref;
base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty(
access_info.field_representation(), access_info.field_index());
if (!constant.has_value() || !constant->IsHeapObject() ||
!constant->AsHeapObject().map().is_callable())
return NoChange();
}
if (found_on_proto) {
dependencies()->DependOnStablePrototypeChains(
@ -459,8 +466,6 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
JSObjectRef(broker(), holder));
}
DCHECK(constant->IsCallable());
// Check that {constructor} is actually {receiver}.
constructor =
access_builder.BuildCheckValue(constructor, &effect, control, receiver);
@ -480,7 +485,7 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
0, frame_state, ContinuationFrameStateMode::LAZY);
// Call the @@hasInstance handler.
Node* target = jsgraph()->Constant(constant);
Node* target = jsgraph()->Constant(*constant);
node->InsertInput(graph()->zone(), 0, target);
node->ReplaceInput(1, constructor);
node->ReplaceInput(2, object);
@ -615,7 +620,7 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
// of the instanceof operator again.
JSBoundFunctionRef function = m.Ref(broker()).AsJSBoundFunction();
if (FLAG_concurrent_inlining && !function.serialized()) {
TRACE_BROKER_MISSING(broker(), "data for function " << function);
TRACE_BROKER_MISSING(broker(), "data for JSBoundFunction " << function);
return NoChange();
}
@ -634,7 +639,7 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
JSFunctionRef function = m.Ref(broker()).AsJSFunction();
if (FLAG_concurrent_inlining && !function.serialized()) {
TRACE_BROKER_MISSING(broker(), "data for function " << function);
TRACE_BROKER_MISSING(broker(), "data for JSFunction " << function);
return NoChange();
}

View File

@ -373,11 +373,17 @@ class SerializerForBackgroundCompilation {
FeedbackSlot slot, AccessMode mode);
void ProcessMapHintsForPromises(Hints const& receiver_hints);
void ProcessHintsForPromiseResolve(Hints const& resolution_hints);
void ProcessHintsForObjectIsPrototypeOf(Hints const& value_hints);
void ProcessHintsForHasInPrototypeChain(Hints const& instance_hints);
void ProcessHintsForRegExpTest(Hints const& regexp_hints);
PropertyAccessInfo ProcessMapForRegExpTest(MapRef map);
void ProcessHintsForFunctionCall(Hints const& target_hints);
void ProcessHintsForFunctionBind(Hints const& receiver_hints);
void ProcessConstantForOrdinaryHasInstance(HeapObjectRef const& constructor,
bool* walk_prototypes);
void ProcessConstantForInstanceOf(ObjectRef const& constant,
bool* walk_prototypes);
void ProcessHintsForOrdinaryHasInstance(Hints const& constructor_hints,
Hints const& instance_hints);
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(FeedbackSlot slot);
NamedAccessFeedback const* ProcessFeedbackMapsForNamedAccess(
@ -1676,14 +1682,19 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
break;
case Builtins::kObjectPrototypeIsPrototypeOf:
if (arguments.size() >= 2) {
ProcessHintsForObjectIsPrototypeOf(arguments[1]);
ProcessHintsForHasInPrototypeChain(arguments[1]);
}
break;
case Builtins::kFunctionPrototypeHasInstance:
// For JSCallReducer::ReduceFunctionPrototypeHasInstance.
if (arguments.size() >= 2) {
ProcessHintsForOrdinaryHasInstance(arguments[0], arguments[1]);
}
break;
case Builtins::kFastFunctionPrototypeBind:
if (arguments.size() >= 2 &&
if (arguments.size() >= 1 &&
speculation_mode != SpeculationMode::kDisallowSpeculation) {
Hints const& receiver_hints = arguments[1];
ProcessHintsForFunctionBind(receiver_hints);
ProcessHintsForFunctionBind(arguments[0]);
}
break;
default:
@ -1691,8 +1702,22 @@ void SerializerForBackgroundCompilation::ProcessBuiltinCall(
}
}
void SerializerForBackgroundCompilation::ProcessHintsForObjectIsPrototypeOf(
Hints const& value_hints) {
void SerializerForBackgroundCompilation::ProcessHintsForOrdinaryHasInstance(
Hints const& constructor_hints, Hints const& instance_hints) {
bool walk_prototypes = false;
for (Handle<Object> constructor : constructor_hints.constants()) {
// For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance.
if (constructor->IsHeapObject()) {
ProcessConstantForOrdinaryHasInstance(
HeapObjectRef(broker(), constructor), &walk_prototypes);
}
}
// For JSNativeContextSpecialization::ReduceJSHasInPrototypeChain.
if (walk_prototypes) ProcessHintsForHasInPrototypeChain(instance_hints);
}
void SerializerForBackgroundCompilation::ProcessHintsForHasInPrototypeChain(
Hints const& instance_hints) {
auto processMap = [&](Handle<Map> map_handle) {
MapRef map(broker(), map_handle);
while (map.IsJSObjectMap()) {
@ -1701,12 +1726,12 @@ void SerializerForBackgroundCompilation::ProcessHintsForObjectIsPrototypeOf(
}
};
for (auto hint : value_hints.constants()) {
for (auto hint : instance_hints.constants()) {
if (!hint->IsHeapObject()) continue;
Handle<HeapObject> object(Handle<HeapObject>::cast(hint));
processMap(handle(object->map(), broker()->isolate()));
}
for (auto map_hint : value_hints.maps()) {
for (auto map_hint : instance_hints.maps()) {
processMap(map_hint);
}
}
@ -2237,36 +2262,84 @@ void SerializerForBackgroundCompilation::VisitTestIn(
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas);
}
// For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance.
void SerializerForBackgroundCompilation::ProcessConstantForOrdinaryHasInstance(
HeapObjectRef const& constructor, bool* walk_prototypes) {
if (constructor.IsJSBoundFunction()) {
constructor.AsJSBoundFunction().Serialize();
ProcessConstantForInstanceOf(
constructor.AsJSBoundFunction().bound_target_function(),
walk_prototypes);
} else if (constructor.IsJSFunction()) {
constructor.AsJSFunction().Serialize();
*walk_prototypes =
*walk_prototypes ||
(constructor.map().has_prototype_slot() &&
constructor.AsJSFunction().has_prototype() &&
!constructor.AsJSFunction().PrototypeRequiresRuntimeLookup());
}
}
void SerializerForBackgroundCompilation::ProcessConstantForInstanceOf(
ObjectRef const& constructor, bool* walk_prototypes) {
if (!constructor.IsHeapObject()) return;
HeapObjectRef constructor_heap_object = constructor.AsHeapObject();
PropertyAccessInfo const& access_info =
broker()->CreateAccessInfoForLoadingHasInstance(
constructor_heap_object.map(), dependencies());
if (access_info.IsNotFound()) {
ProcessConstantForOrdinaryHasInstance(constructor_heap_object,
walk_prototypes);
} else if (access_info.IsDataConstant()) {
Handle<JSObject> holder;
bool found_on_proto = access_info.holder().ToHandle(&holder);
JSObjectRef holder_ref = found_on_proto ? JSObjectRef(broker(), holder)
: constructor.AsJSObject();
base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty(
access_info.field_representation(), access_info.field_index(), true);
CHECK(constant.has_value());
if (constant->IsJSFunction()) {
JSFunctionRef function = constant->AsJSFunction();
function.Serialize();
if (function.shared().HasBuiltinId() &&
function.shared().builtin_id() ==
Builtins::kFunctionPrototypeHasInstance) {
// For JSCallReducer::ReduceFunctionPrototypeHasInstance.
ProcessConstantForOrdinaryHasInstance(constructor_heap_object,
walk_prototypes);
}
}
}
}
void SerializerForBackgroundCompilation::VisitTestInstanceOf(
BytecodeArrayIterator* iterator) {
Hints const& lhs =
environment()->register_hints(iterator->GetRegisterOperand(0));
Hints const& rhs = environment()->accumulator_hints();
Hints& rhs = environment()->accumulator_hints();
FeedbackSlot slot = iterator->GetSlotOperand(1);
// Inspect feedback (about the rhs of the operator).
Handle<FeedbackVector> feedback_vector =
environment()->function().feedback_vector();
FeedbackNexus nexus(feedback_vector, slot);
VectorSlotPair rhs_feedback(feedback_vector, slot, nexus.ic_state());
Handle<JSObject> constructor;
if (rhs_feedback.IsValid() &&
nexus.GetConstructorFeedback().ToHandle(&constructor)) {
if (constructor->IsJSBoundFunction()) {
JSBoundFunctionRef(broker(), constructor).Serialize();
} else if (constructor->IsJSFunction()) {
JSFunctionRef(broker(), constructor).Serialize();
} else {
UNREACHABLE();
// Incorporate feedback (about the rhs of the operator) into hints.
{
Handle<FeedbackVector> feedback_vector =
environment()->function().feedback_vector();
FeedbackNexus nexus(feedback_vector, slot);
VectorSlotPair rhs_feedback(feedback_vector, slot, nexus.ic_state());
Handle<JSObject> constructor;
if (rhs_feedback.IsValid() &&
nexus.GetConstructorFeedback().ToHandle(&constructor)) {
rhs.AddConstant(constructor);
}
}
// TODO(neis): Support full instanceof semantics.
ProcessHintsForObjectIsPrototypeOf(lhs);
// TODO(neis): Process rhs hints.
USE(rhs);
bool walk_prototypes = false;
for (Handle<Object> constant : rhs.constants()) {
ProcessConstantForInstanceOf(ObjectRef(broker(), constant),
&walk_prototypes);
}
if (walk_prototypes) ProcessHintsForHasInPrototypeChain(lhs);
}
void SerializerForBackgroundCompilation::VisitStaKeyedProperty(

View File

@ -0,0 +1,80 @@
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
(function testFunctionPrototypeHasInstance() {
class A {};
var a = new A;
function foo() {
return A[Symbol.hasInstance](a);
};
%PrepareFunctionForOptimization(foo);
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
(function testFunctionPrototypeHasInstanceWithInference() {
class A {};
var a = new A;
a.bla = 42;
function foo() {
a.bla;
return A[Symbol.hasInstance](a);
};
%PrepareFunctionForOptimization(foo);
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();
(function testFunctionPrototypeHasInstanceWithBoundFunction() {
class A {};
var a = new A;
var f = A.bind({});
function foo() {
return f[Symbol.hasInstance](a);
};
%PrepareFunctionForOptimization(foo);
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
// JSCallReducer::ReduceFunctionPrototypeHasInstance ->
// JSNative...::ReduceJSOrdinaryHasInstance ->
// JSNative...::ReduceJSInstanceOf (on bound_target_function)
// ~~~~~>
// JSCallReducer::ReduceFunctionPrototypeHasInstance
// JSNative...::ReduceJSOrdinaryHasInstance ->
// JSNative...::ReduceJSHasInPrototypeChain
})();
(function testSimpleInstanceOf() {
class A {};
var a = new A;
function foo() {
return a instanceof A;
};
%PrepareFunctionForOptimization(foo);
assertTrue(foo());
assertTrue(foo());
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo());
})();