Index: src/compiler/js-call-reducer.cc |
diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc |
index e42b4ca1dc53d685165960e4542a4fe688488931..71745cd54024492203c71a9058e5b72320855f9b 100644 |
--- a/src/compiler/js-call-reducer.cc |
+++ b/src/compiler/js-call-reducer.cc |
@@ -4,7 +4,9 @@ |
#include "src/compiler/js-call-reducer.h" |
+#include "src/code-stubs.h" |
#include "src/compiler/js-graph.h" |
+#include "src/compiler/linkage.h" |
#include "src/compiler/node-matchers.h" |
#include "src/compiler/simplified-operator.h" |
#include "src/objects-inl.h" |
@@ -257,8 +259,58 @@ MaybeHandle<Map> InferReceiverMap(Node* node) { |
} |
} |
+bool CanInlineApiCall(Isolate* isolate, Node* node, |
+ Handle<FunctionTemplateInfo> function_template_info) { |
+ DCHECK(node->opcode() == IrOpcode::kJSCallFunction); |
+ if (function_template_info->call_code()->IsUndefined(isolate)) { |
+ return false; |
+ } |
+ CallFunctionParameters const& params = CallFunctionParametersOf(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; |
+} |
+ |
// ES6 section B.2.2.1.1 get Object.prototype.__proto__ |
Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) { |
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
@@ -280,6 +332,69 @@ Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) { |
return NoChange(); |
} |
+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* receiver_node = NodeProperties::GetValueInput(node, 1); |
+ CallFunctionParameters const& params = CallFunctionParametersOf(node->op()); |
+ |
+ 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()); |
+ } |
+ |
+ 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); |
+ } |
+ 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, data->IsUndefined(isolate), false); |
+ CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor(); |
+ CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor( |
+ isolate, 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())); |
+ 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)); |
+ NodeProperties::ChangeOp(node, common()->Call(call_descriptor)); |
+ return Changed(node); |
+} |
+ |
Reduction JSCallReducer::ReduceJSCallFunction(Node* node) { |
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
@@ -323,6 +438,12 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) { |
if (*function == function->native_context()->array_function()) { |
return ReduceArrayConstructor(node); |
} |
+ |
+ if (shared->IsApiFunction()) { |
+ return ReduceCallApiFunction( |
+ node, target, |
+ handle(FunctionTemplateInfo::cast(shared->function_data()))); |
+ } |
} else if (m.Value()->IsJSBoundFunction()) { |
Handle<JSBoundFunction> function = |
Handle<JSBoundFunction>::cast(m.Value()); |