summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/js-native-context-specialization.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/js-native-context-specialization.cc')
-rw-r--r--deps/v8/src/compiler/js-native-context-specialization.cc305
1 files changed, 260 insertions, 45 deletions
diff --git a/deps/v8/src/compiler/js-native-context-specialization.cc b/deps/v8/src/compiler/js-native-context-specialization.cc
index 66013b85ca..5a3ccebed1 100644
--- a/deps/v8/src/compiler/js-native-context-specialization.cc
+++ b/deps/v8/src/compiler/js-native-context-specialization.cc
@@ -24,21 +24,21 @@ namespace compiler {
namespace {
-bool HasNumberMaps(MapList const& maps) {
+bool HasNumberMaps(MapHandles const& maps) {
for (auto map : maps) {
if (map->instance_type() == HEAP_NUMBER_TYPE) return true;
}
return false;
}
-bool HasOnlyJSArrayMaps(MapList const& maps) {
+bool HasOnlyJSArrayMaps(MapHandles const& maps) {
for (auto map : maps) {
if (!map->IsJSArrayMap()) return false;
}
return true;
}
-bool HasOnlyNumberMaps(MapList const& maps) {
+bool HasOnlyNumberMaps(MapHandles const& maps) {
for (auto map : maps) {
if (map->instance_type() != HEAP_NUMBER_TYPE) return false;
}
@@ -154,11 +154,6 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
if (function_prototype->IsConstructor()) {
ReplaceWithValue(node, value);
return Replace(value);
- } else {
- node->InsertInput(graph()->zone(), 0, value);
- NodeProperties::ChangeOp(
- node, javascript()->CallRuntime(Runtime::kThrowNotSuperConstructor));
- return Changed(node);
}
}
@@ -247,9 +242,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
node->ReplaceInput(2, object);
node->ReplaceInput(5, effect);
NodeProperties::ChangeOp(
- node,
- javascript()->Call(3, 0.0f, VectorSlotPair(),
- ConvertReceiverMode::kNotNullOrUndefined));
+ node, javascript()->Call(3, CallFrequency(), VectorSlotPair(),
+ ConvertReceiverMode::kNotNullOrUndefined));
// Rewire the value uses of {node} to ToBoolean conversion of the result.
Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
@@ -271,10 +265,17 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
Node* constructor = NodeProperties::GetValueInput(node, 0);
Node* object = NodeProperties::GetValueInput(node, 1);
+ Node* context = NodeProperties::GetContextInput(node);
+ Node* frame_state = NodeProperties::GetFrameStateInput(node);
+ Node* effect = NodeProperties::GetEffectInput(node);
+ Node* control = NodeProperties::GetControlInput(node);
- // Check if the {constructor} is a JSBoundFunction.
+ // Check if the {constructor} is known at compile time.
HeapObjectMatcher m(constructor);
- if (m.HasValue() && m.Value()->IsJSBoundFunction()) {
+ if (!m.HasValue()) return NoChange();
+
+ // Check if the {constructor} is a JSBoundFunction.
+ if (m.Value()->IsJSBoundFunction()) {
// OrdinaryHasInstance on bound functions turns into a recursive
// invocation of the instanceof operator again.
// ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2.
@@ -288,6 +289,160 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
return reduction.Changed() ? reduction : Changed(node);
}
+ // Check if the {constructor} is a JSFunction.
+ if (m.Value()->IsJSFunction()) {
+ // Check if the {function} is a constructor and has an instance "prototype".
+ Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
+ if (function->IsConstructor() && function->has_instance_prototype() &&
+ function->prototype()->IsJSReceiver()) {
+ // Ensure that the {function} has a valid initial map, so we can
+ // depend on that for the prototype constant-folding below.
+ JSFunction::EnsureHasInitialMap(function);
+
+ // Install a code dependency on the {function}s initial map.
+ Handle<Map> initial_map(function->initial_map(), isolate());
+ dependencies()->AssumeInitialMapCantChange(initial_map);
+ Handle<JSReceiver> function_prototype =
+ handle(JSReceiver::cast(initial_map->prototype()), isolate());
+
+ // Check if we can constant-fold the prototype chain walk
+ // for the given {object} and the {function_prototype}.
+ InferHasInPrototypeChainResult result =
+ InferHasInPrototypeChain(object, effect, function_prototype);
+ if (result != kMayBeInPrototypeChain) {
+ Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain);
+ ReplaceWithValue(node, value, effect, control);
+ return Replace(value);
+ }
+
+ Node* prototype = jsgraph()->Constant(function_prototype);
+
+ Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), object);
+ Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
+ check0, control);
+
+ Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
+ Node* etrue0 = effect;
+ Node* vtrue0 = jsgraph()->FalseConstant();
+
+ control = graph()->NewNode(common()->IfFalse(), branch0);
+
+ // Loop through the {object}s prototype chain looking for the {prototype}.
+ Node* loop = control =
+ graph()->NewNode(common()->Loop(2), control, control);
+ Node* eloop = effect =
+ graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
+ Node* vloop = object =
+ graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
+ object, object, loop);
+
+ // Load the {object} map and instance type.
+ Node* object_map = effect =
+ graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
+ object, effect, control);
+ Node* object_instance_type = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
+ object_map, effect, control);
+
+ // Check if the {object} is a special receiver, because for special
+ // receivers, i.e. proxies or API objects that need access checks,
+ // we have to use the %HasInPrototypeChain runtime function instead.
+ Node* check1 = graph()->NewNode(
+ simplified()->NumberLessThanOrEqual(), object_instance_type,
+ jsgraph()->Constant(LAST_SPECIAL_RECEIVER_TYPE));
+ Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
+ check1, control);
+
+ control = graph()->NewNode(common()->IfFalse(), branch1);
+
+ Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
+ Node* etrue1 = effect;
+ Node* vtrue1;
+
+ // Check if the {object} is not a receiver at all.
+ Node* check10 =
+ graph()->NewNode(simplified()->NumberLessThan(), object_instance_type,
+ jsgraph()->Constant(FIRST_JS_RECEIVER_TYPE));
+ Node* branch10 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
+ check10, if_true1);
+
+ // A primitive value cannot match the {prototype} we're looking for.
+ if_true1 = graph()->NewNode(common()->IfTrue(), branch10);
+ vtrue1 = jsgraph()->FalseConstant();
+
+ Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch10);
+ Node* efalse1 = etrue1;
+ Node* vfalse1;
+ {
+ // Slow path, need to call the %HasInPrototypeChain runtime function.
+ vfalse1 = efalse1 = if_false1 = graph()->NewNode(
+ javascript()->CallRuntime(Runtime::kHasInPrototypeChain), object,
+ prototype, context, frame_state, efalse1, if_false1);
+
+ // Replace any potential {IfException} uses of {node} to catch
+ // exceptions from this %HasInPrototypeChain runtime call instead.
+ Node* on_exception = nullptr;
+ if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
+ NodeProperties::ReplaceControlInput(on_exception, vfalse1);
+ NodeProperties::ReplaceEffectInput(on_exception, efalse1);
+ if_false1 = graph()->NewNode(common()->IfSuccess(), vfalse1);
+ Revisit(on_exception);
+ }
+ }
+
+ // Load the {object} prototype.
+ Node* object_prototype = effect = graph()->NewNode(
+ simplified()->LoadField(AccessBuilder::ForMapPrototype()), object_map,
+ effect, control);
+
+ // Check if we reached the end of {object}s prototype chain.
+ Node* check2 =
+ graph()->NewNode(simplified()->ReferenceEqual(), object_prototype,
+ jsgraph()->NullConstant());
+ Node* branch2 = graph()->NewNode(common()->Branch(), check2, control);
+
+ Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
+ Node* etrue2 = effect;
+ Node* vtrue2 = jsgraph()->FalseConstant();
+
+ control = graph()->NewNode(common()->IfFalse(), branch2);
+
+ // Check if we reached the {prototype}.
+ Node* check3 = graph()->NewNode(simplified()->ReferenceEqual(),
+ object_prototype, prototype);
+ Node* branch3 = graph()->NewNode(common()->Branch(), check3, control);
+
+ Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3);
+ Node* etrue3 = effect;
+ Node* vtrue3 = jsgraph()->TrueConstant();
+
+ control = graph()->NewNode(common()->IfFalse(), branch3);
+
+ // Close the loop.
+ vloop->ReplaceInput(1, object_prototype);
+ eloop->ReplaceInput(1, effect);
+ loop->ReplaceInput(1, control);
+
+ control = graph()->NewNode(common()->Merge(5), if_true0, if_true1,
+ if_true2, if_true3, if_false1);
+ effect = graph()->NewNode(common()->EffectPhi(5), etrue0, etrue1, etrue2,
+ etrue3, efalse1, control);
+
+ // Morph the {node} into an appropriate Phi.
+ ReplaceWithValue(node, node, effect, control);
+ node->ReplaceInput(0, vtrue0);
+ node->ReplaceInput(1, vtrue1);
+ node->ReplaceInput(2, vtrue2);
+ node->ReplaceInput(3, vtrue3);
+ node->ReplaceInput(4, vfalse1);
+ node->ReplaceInput(5, control);
+ node->TrimInputCount(6);
+ NodeProperties::ChangeOp(
+ node, common()->Phi(MachineRepresentation::kTagged, 5));
+ return Changed(node);
+ }
+ }
+
return NoChange();
}
@@ -551,9 +706,8 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
}
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
- Node* node, Node* value, MapHandleList const& receiver_maps,
- Handle<Name> name, AccessMode access_mode, LanguageMode language_mode,
- Node* index) {
+ Node* node, Node* value, MapHandles const& receiver_maps, Handle<Name> name,
+ AccessMode access_mode, LanguageMode language_mode, Node* index) {
DCHECK(node->opcode() == IrOpcode::kJSLoadNamed ||
node->opcode() == IrOpcode::kJSStoreNamed ||
node->opcode() == IrOpcode::kJSLoadProperty ||
@@ -568,8 +722,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// Check if we have an access o.x or o.x=v where o is the current
// native contexts' global proxy, and turn that into a direct access
// to the current native contexts' global object instead.
- if (receiver_maps.length() == 1) {
- Handle<Map> receiver_map = receiver_maps.first();
+ if (receiver_maps.size() == 1) {
+ Handle<Map> receiver_map = receiver_maps.front();
if (receiver_map->IsJSGlobalProxyMap()) {
Object* maybe_constructor = receiver_map->GetConstructor();
// Detached global proxies have |null| as their constructor.
@@ -686,7 +840,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
Node* this_control = fallthrough_control;
// Perform map check on {receiver}.
- MapList const& receiver_maps = access_info.receiver_maps();
+ MapHandles const& receiver_maps = access_info.receiver_maps();
{
// Emit a (sequence of) map checks for other {receiver}s.
ZoneVector<Node*> this_controls(zone());
@@ -801,10 +955,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus(
}
// Extract receiver maps from the IC using the {nexus}.
- MapHandleList receiver_maps;
+ MapHandles receiver_maps;
if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
return NoChange();
- } else if (receiver_maps.length() == 0) {
+ } else if (receiver_maps.empty()) {
if (flags() & kBailoutOnUninitialized) {
return ReduceSoftDeoptimize(
node,
@@ -831,11 +985,11 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) {
p.name().is_identical_to(factory()->prototype_string())) {
// Optimize "prototype" property of functions.
Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
- if (function->has_initial_map()) {
+ if (function->IsConstructor()) {
// We need to add a code dependency on the initial map of the
// {function} in order to be notified about changes to the
- // "prototype" of {function}, so it doesn't make sense to
- // continue unless deoptimization is enabled.
+ // "prototype" of {function}.
+ JSFunction::EnsureHasInitialMap(function);
Handle<Map> initial_map(function->initial_map(), isolate());
dependencies()->AssumeInitialMapCantChange(initial_map);
Handle<Object> prototype(function->prototype(), isolate());
@@ -892,7 +1046,7 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) {
}
Reduction JSNativeContextSpecialization::ReduceElementAccess(
- Node* node, Node* index, Node* value, MapHandleList const& receiver_maps,
+ Node* node, Node* index, Node* value, MapHandles const& receiver_maps,
AccessMode access_mode, LanguageMode language_mode,
KeyedAccessStoreMode store_mode) {
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
@@ -1056,7 +1210,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
receiver, this_effect, this_control);
// Perform map check(s) on {receiver}.
- MapList const& receiver_maps = access_info.receiver_maps();
+ MapHandles const& receiver_maps = access_info.receiver_maps();
if (j == access_infos.size() - 1) {
// Last map check on the fallthrough control path, do a
// conditional eager deoptimization exit here.
@@ -1189,10 +1343,10 @@ Reduction JSNativeContextSpecialization::ReduceKeyedAccess(
}
// Extract receiver maps from the {nexus}.
- MapHandleList receiver_maps;
+ MapHandles receiver_maps;
if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
return NoChange();
- } else if (receiver_maps.length() == 0) {
+ } else if (receiver_maps.empty()) {
if (flags() & kBailoutOnUninitialized) {
return ReduceSoftDeoptimize(
node,
@@ -1347,7 +1501,7 @@ JSNativeContextSpecialization::BuildPropertyAccess(
// Introduce the call to the getter function.
if (access_info.constant()->IsJSFunction()) {
value = effect = control = graph()->NewNode(
- javascript()->Call(2, 0.0f, VectorSlotPair(),
+ javascript()->Call(2, CallFrequency(), VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, context, frame_state0, effect, control);
} else {
@@ -1383,7 +1537,7 @@ JSNativeContextSpecialization::BuildPropertyAccess(
// Introduce the call to the setter function.
if (access_info.constant()->IsJSFunction()) {
effect = control = graph()->NewNode(
- javascript()->Call(3, 0.0f, VectorSlotPair(),
+ javascript()->Call(3, CallFrequency(), VectorSlotPair(),
ConvertReceiverMode::kNotNullOrUndefined),
target, receiver, value, context, frame_state0, effect, control);
} else {
@@ -1756,7 +1910,7 @@ JSNativeContextSpecialization::BuildElementAccess(
// TODO(bmeurer): We currently specialize based on elements kind. We should
// also be able to properly support strings and other JSObjects here.
ElementsKind elements_kind = access_info.elements_kind();
- MapList const& receiver_maps = access_info.receiver_maps();
+ MapHandles const& receiver_maps = access_info.receiver_maps();
if (IsFixedTypedArrayElementsKind(elements_kind)) {
Node* buffer;
@@ -2059,7 +2213,7 @@ JSNativeContextSpecialization::InlineApiCall(
int const argc = value == nullptr ? 0 : 1;
// The stub always expects the receiver as the first param on the stack.
CallApiCallbackStub stub(
- isolate(), argc, call_data_object->IsUndefined(isolate()),
+ isolate(), argc,
true /* FunctionTemplateInfo doesn't have an associated context. */);
CallInterfaceDescriptor call_interface_descriptor =
stub.GetCallInterfaceDescriptor();
@@ -2127,7 +2281,7 @@ Node* JSNativeContextSpecialization::BuildCheckHeapObject(Node* receiver,
Node* JSNativeContextSpecialization::BuildCheckMaps(
Node* receiver, Node* effect, Node* control,
- std::vector<Handle<Map>> const& receiver_maps) {
+ MapHandles const& receiver_maps) {
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
Handle<Map> receiver_map(m.Value()->map(), isolate());
@@ -2154,6 +2308,15 @@ Node* JSNativeContextSpecialization::BuildCheckMaps(
Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
Handle<Map> map, Node* properties, Node* effect, Node* control) {
+ // TODO(bmeurer/jkummerow): Property deletions can undo map transitions
+ // while keeping the backing store around, meaning that even though the
+ // map might believe that objects have no unused property fields, there
+ // might actually be some. It would be nice to not create a new backing
+ // store in that case (i.e. when properties->length() >= new_length).
+ // However, introducing branches and Phi nodes here would make it more
+ // difficult for escape analysis to get rid of the backing stores used
+ // for intermediate states of chains of property additions. That makes
+ // it unclear what the best approach is here.
DCHECK_EQ(0, map->unused_property_fields());
// Compute the length of the old {properties} and the new properties.
int length = map->NextFreePropertyIndex() - map->GetInObjectProperties();
@@ -2192,7 +2355,7 @@ Node* JSNativeContextSpecialization::BuildExtendPropertiesBackingStore(
}
void JSNativeContextSpecialization::AssumePrototypesStable(
- std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) {
+ MapHandles const& receiver_maps, Handle<JSObject> holder) {
// Determine actual holder and perform prototype chain checks.
for (auto map : receiver_maps) {
// Perform the implicit ToObject for primitives here.
@@ -2207,7 +2370,7 @@ void JSNativeContextSpecialization::AssumePrototypesStable(
}
bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
- std::vector<Handle<Map>> const& receiver_maps) {
+ MapHandles const& receiver_maps) {
// Check if the array prototype chain is intact.
if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) return false;
@@ -2241,10 +2404,61 @@ bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
return true;
}
+JSNativeContextSpecialization::InferHasInPrototypeChainResult
+JSNativeContextSpecialization::InferHasInPrototypeChain(
+ Node* receiver, Node* effect, Handle<JSReceiver> prototype) {
+ ZoneHandleSet<Map> receiver_maps;
+ NodeProperties::InferReceiverMapsResult result =
+ NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain;
+
+ // Check if either all or none of the {receiver_maps} have the given
+ // {prototype} in their prototype chain.
+ bool all = true;
+ bool none = true;
+ for (size_t i = 0; i < receiver_maps.size(); ++i) {
+ Handle<Map> receiver_map = receiver_maps[i];
+ if (receiver_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
+ return kMayBeInPrototypeChain;
+ }
+ if (result == NodeProperties::kUnreliableReceiverMaps) {
+ // In case of an unreliable {result} we need to ensure that all
+ // {receiver_maps} are stable, because otherwise we cannot trust
+ // the {receiver_maps} information, since arbitrary side-effects
+ // may have happened.
+ if (!receiver_map->is_stable()) {
+ return kMayBeInPrototypeChain;
+ }
+ }
+ for (PrototypeIterator j(receiver_map);; j.Advance()) {
+ if (j.IsAtEnd()) {
+ all = false;
+ break;
+ }
+ Handle<JSReceiver> const current =
+ PrototypeIterator::GetCurrent<JSReceiver>(j);
+ if (current.is_identical_to(prototype)) {
+ none = false;
+ break;
+ }
+ if (!current->map()->is_stable() ||
+ current->map()->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
+ return kMayBeInPrototypeChain;
+ }
+ }
+ }
+ DCHECK_IMPLIES(all, !none);
+ DCHECK_IMPLIES(none, !all);
+
+ if (all) return kIsInPrototypeChain;
+ if (none) return kIsNotInPrototypeChain;
+ return kMayBeInPrototypeChain;
+}
+
bool JSNativeContextSpecialization::ExtractReceiverMaps(
Node* receiver, Node* effect, FeedbackNexus const& nexus,
- MapHandleList* receiver_maps) {
- DCHECK_EQ(0, receiver_maps->length());
+ MapHandles* receiver_maps) {
+ DCHECK_EQ(0, receiver_maps->size());
// See if we can infer a concrete type for the {receiver}.
if (InferReceiverMaps(receiver, effect, receiver_maps)) {
// We can assume that the {receiver} still has the infered {receiver_maps}.
@@ -2255,11 +2469,12 @@ bool JSNativeContextSpecialization::ExtractReceiverMaps(
// Try to filter impossible candidates based on infered root map.
Handle<Map> receiver_map;
if (InferReceiverRootMap(receiver).ToHandle(&receiver_map)) {
- for (int i = receiver_maps->length(); --i >= 0;) {
- if (receiver_maps->at(i)->FindRootMap() != *receiver_map) {
- receiver_maps->Remove(i);
- }
- }
+ receiver_maps->erase(
+ std::remove_if(receiver_maps->begin(), receiver_maps->end(),
+ [receiver_map](const Handle<Map>& map) {
+ return map->FindRootMap() != *receiver_map;
+ }),
+ receiver_maps->end());
}
return true;
}
@@ -2267,13 +2482,13 @@ bool JSNativeContextSpecialization::ExtractReceiverMaps(
}
bool JSNativeContextSpecialization::InferReceiverMaps(
- Node* receiver, Node* effect, MapHandleList* receiver_maps) {
+ Node* receiver, Node* effect, MapHandles* receiver_maps) {
ZoneHandleSet<Map> maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &maps);
if (result == NodeProperties::kReliableReceiverMaps) {
for (size_t i = 0; i < maps.size(); ++i) {
- receiver_maps->Add(maps[i]);
+ receiver_maps->push_back(maps[i]);
}
return true;
} else if (result == NodeProperties::kUnreliableReceiverMaps) {
@@ -2283,7 +2498,7 @@ bool JSNativeContextSpecialization::InferReceiverMaps(
if (!maps[i]->is_stable()) return false;
}
for (size_t i = 0; i < maps.size(); ++i) {
- receiver_maps->Add(maps[i]);
+ receiver_maps->push_back(maps[i]);
}
return true;
}