Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(78)

Unified Diff: src/compiler/js-call-reducer.cc

Issue 2839953002: [turbofan] Optimize API function calls based on inferred receiver maps. (Closed)
Patch Set: REBASE Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/compiler/js-call-reducer.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
}
« no previous file with comments | « src/compiler/js-call-reducer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698