Index: src/compiler/js-call-reducer.cc |
diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc |
index 963ab9417f17c67ec2389533893d6b35c0d116fe..edb3d7149f7fcab2410ae1d815f7ce66c5ebc8a6 100644 |
--- a/src/compiler/js-call-reducer.cc |
+++ b/src/compiler/js-call-reducer.cc |
@@ -12,6 +12,7 @@ |
#include "src/compiler/node-matchers.h" |
#include "src/compiler/simplified-operator.h" |
#include "src/feedback-vector-inl.h" |
+#include "src/ic/call-optimization.h" |
#include "src/objects-inl.h" |
namespace v8 { |
@@ -272,61 +273,6 @@ Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) { |
return Changed(node); |
} |
-namespace { |
- |
-bool CanInlineApiCall(Isolate* isolate, Node* node, |
- Handle<FunctionTemplateInfo> function_template_info) { |
- DCHECK(node->opcode() == IrOpcode::kJSCall); |
- if (V8_UNLIKELY(FLAG_runtime_stats)) return false; |
- if (function_template_info->call_code()->IsUndefined(isolate)) { |
- return false; |
- } |
- CallParameters const& params = CallParametersOf(node->op()); |
- // 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. |
- int const argc = static_cast<int>(params.arity()) - 2; |
- if (argc > CallApiCallbackStub::kArgMax || !params.feedback().IsValid()) { |
- return false; |
- } |
- HeapObjectMatcher receiver(NodeProperties::GetValueInput(node, 1)); |
- if (!receiver.HasValue()) { |
- return false; |
- } |
- return receiver.Value()->IsUndefined(isolate) || |
- (receiver.Value()->map()->IsJSObjectMap() && |
- !receiver.Value()->map()->is_access_check_needed()); |
-} |
- |
-} // namespace |
- |
-JSCallReducer::HolderLookup JSCallReducer::LookupHolder( |
- Handle<JSObject> object, |
- Handle<FunctionTemplateInfo> function_template_info, |
- Handle<JSObject>* holder) { |
- DCHECK(object->map()->IsJSObjectMap()); |
- Handle<Map> object_map(object->map()); |
- Handle<FunctionTemplateInfo> expected_receiver_type; |
- if (!function_template_info->signature()->IsUndefined(isolate())) { |
- expected_receiver_type = |
- handle(FunctionTemplateInfo::cast(function_template_info->signature())); |
- } |
- if (expected_receiver_type.is_null() || |
- expected_receiver_type->IsTemplateFor(*object_map)) { |
- *holder = Handle<JSObject>::null(); |
- return kHolderIsReceiver; |
- } |
- while (object_map->has_hidden_prototype()) { |
- Handle<JSObject> prototype(JSObject::cast(object_map->prototype())); |
- object_map = handle(prototype->map()); |
- if (expected_receiver_type->IsTemplateFor(*object_map)) { |
- *holder = prototype; |
- return kHolderFound; |
- } |
- } |
- return kHolderNotFound; |
-} |
- |
Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { |
Node* effect = NodeProperties::GetEffectInput(node); |
@@ -398,64 +344,90 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { |
} |
Reduction JSCallReducer::ReduceCallApiFunction( |
- Node* node, Node* target, |
- Handle<FunctionTemplateInfo> function_template_info) { |
- Isolate* isolate = this->isolate(); |
- CHECK(!isolate->serializer_enabled()); |
- HeapObjectMatcher m(target); |
- DCHECK(m.HasValue() && m.Value()->IsJSFunction()); |
- if (!CanInlineApiCall(isolate, node, function_template_info)) { |
- return NoChange(); |
- } |
- Handle<CallHandlerInfo> call_handler_info( |
- handle(CallHandlerInfo::cast(function_template_info->call_code()))); |
- Handle<Object> data(call_handler_info->data(), isolate); |
+ Node* node, Handle<FunctionTemplateInfo> function_template_info) { |
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
+ CallParameters const& p = CallParametersOf(node->op()); |
+ int const argc = static_cast<int>(p.arity()) - 2; |
+ Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) |
+ ? jsgraph()->HeapConstant(global_proxy()) |
+ : NodeProperties::GetValueInput(node, 1); |
+ Node* effect = NodeProperties::GetEffectInput(node); |
- Node* receiver_node = NodeProperties::GetValueInput(node, 1); |
- CallParameters const& params = CallParametersOf(node->op()); |
+ // 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(); |
- Handle<HeapObject> receiver = HeapObjectMatcher(receiver_node).Value(); |
- bool const receiver_is_undefined = receiver->IsUndefined(isolate); |
- if (receiver_is_undefined) { |
- receiver = handle(Handle<JSFunction>::cast(m.Value())->global_proxy()); |
- } else { |
- DCHECK(receiver->map()->IsJSObjectMap() && |
- !receiver->map()->is_access_check_needed()); |
+ // 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(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() || |
+ (!function_template_info->accept_any_receiver() && |
+ 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(); |
+ } |
} |
- Handle<JSObject> holder; |
- HolderLookup lookup = LookupHolder(Handle<JSObject>::cast(receiver), |
- function_template_info, &holder); |
- if (lookup == kHolderNotFound) return NoChange(); |
- if (receiver_is_undefined) { |
- receiver_node = jsgraph()->HeapConstant(receiver); |
- NodeProperties::ReplaceValueInput(node, receiver_node, 1); |
+ // See if we can constant-fold the compatible receiver checks. |
+ CallOptimization call_optimization(function_template_info); |
+ if (!call_optimization.is_simple_api_call()) return NoChange(); |
+ CallOptimization::HolderLookup lookup; |
+ Handle<JSObject> api_holder = |
+ call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup); |
+ if (lookup == CallOptimization::kHolderNotFound) return NoChange(); |
+ for (size_t i = 1; i < receiver_maps.size(); ++i) { |
+ CallOptimization::HolderLookup lookupi; |
+ Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType( |
+ receiver_maps[i], &lookupi); |
+ if (lookup != lookupi) return NoChange(); |
+ if (!api_holder.is_identical_to(holder)) return NoChange(); |
} |
- Node* holder_node = |
- lookup == kHolderFound ? jsgraph()->HeapConstant(holder) : receiver_node; |
- |
- Zone* zone = graph()->zone(); |
- // Same as CanInlineApiCall: exclude the target (which goes in a register) and |
- // the receiver (which is implicitly counted by CallApiCallbackStub) from the |
- // arguments count. |
- int const argc = static_cast<int>(params.arity() - 2); |
- CallApiCallbackStub stub(isolate, argc, false); |
+ |
+ // Install stability dependencies for unreliable {receiver_maps}. |
+ if (result == NodeProperties::kUnreliableReceiverMaps) { |
+ for (size_t i = 0; i < receiver_maps.size(); ++i) { |
+ dependencies()->AssumeMapStable(receiver_maps[i]); |
+ } |
+ } |
+ |
+ // CallApiCallbackStub's register arguments: code, target, call data, holder, |
+ // function address. |
+ // TODO(turbofan): Consider introducing a JSCallApiCallback operator for |
+ // this and lower it during JSGenericLowering, and unify this with the |
+ // JSNativeContextSpecialization::InlineApiCall method a bit. |
+ Handle<CallHandlerInfo> call_handler_info( |
+ CallHandlerInfo::cast(function_template_info->call_code()), isolate()); |
+ Handle<Object> data(call_handler_info->data(), isolate()); |
+ CallApiCallbackStub stub(isolate(), argc, false); |
CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor(); |
CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor( |
- isolate, zone, cid, |
+ isolate(), graph()->zone(), cid, |
cid.GetStackParameterCount() + argc + 1 /* implicit receiver */, |
CallDescriptor::kNeedsFrameState, Operator::kNoProperties, |
MachineType::AnyTagged(), 1); |
ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback())); |
+ Node* holder = lookup == CallOptimization::kHolderFound |
+ ? jsgraph()->HeapConstant(api_holder) |
+ : receiver; |
ExternalReference function_reference( |
- &api_function, ExternalReference::DIRECT_API_CALL, isolate); |
- |
- // CallApiCallbackStub's register arguments: code, target, call data, holder, |
- // function address. |
- node->InsertInput(zone, 0, jsgraph()->HeapConstant(stub.GetCode())); |
- node->InsertInput(zone, 2, jsgraph()->Constant(data)); |
- node->InsertInput(zone, 3, holder_node); |
- node->InsertInput(zone, 4, jsgraph()->ExternalConstant(function_reference)); |
+ &api_function, ExternalReference::DIRECT_API_CALL, isolate()); |
+ node->InsertInput(graph()->zone(), 0, |
+ jsgraph()->HeapConstant(stub.GetCode())); |
+ node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data)); |
+ node->InsertInput(graph()->zone(), 3, holder); |
+ node->InsertInput(graph()->zone(), 4, |
+ jsgraph()->ExternalConstant(function_reference)); |
+ node->ReplaceInput(5, receiver); |
NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); |
return Changed(node); |
} |
@@ -612,10 +584,10 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { |
return ReduceArrayConstructor(node); |
} |
- if (shared->IsApiFunction()) { |
- return ReduceCallApiFunction( |
- node, target, |
- handle(FunctionTemplateInfo::cast(shared->function_data()))); |
+ if (!FLAG_runtime_stats && shared->IsApiFunction()) { |
+ Handle<FunctionTemplateInfo> function_template_info( |
+ FunctionTemplateInfo::cast(shared->function_data()), isolate()); |
+ return ReduceCallApiFunction(node, function_template_info); |
} |
} else if (m.Value()->IsJSBoundFunction()) { |
Handle<JSBoundFunction> function = |
@@ -866,6 +838,11 @@ 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(); |
} |