summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/js-call-reducer.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/js-call-reducer.cc')
-rw-r--r--deps/v8/src/compiler/js-call-reducer.cc355
1 files changed, 202 insertions, 153 deletions
diff --git a/deps/v8/src/compiler/js-call-reducer.cc b/deps/v8/src/compiler/js-call-reducer.cc
index f0febc4d26..1e1d3a92ab 100644
--- a/deps/v8/src/compiler/js-call-reducer.cc
+++ b/deps/v8/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 {
@@ -123,9 +124,16 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
Node* arg_array = NodeProperties::GetValueInput(node, 3);
if (arg_array->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
for (Edge edge : arg_array->use_edges()) {
- if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
+ Node* const user = edge.from();
+ if (user == node) continue;
+ // Ignore uses as frame state's locals or parameters.
+ if (user->opcode() == IrOpcode::kStateValues) continue;
+ // Ignore uses as frame state's accumulator.
+ if (user->opcode() == IrOpcode::kFrameState &&
+ user->InputAt(2) == arg_array) {
+ continue;
+ }
if (!NodeProperties::IsValueEdge(edge)) continue;
- if (edge.from() == node) continue;
return NoChange();
}
// Check if the arguments can be handled in the fast case (i.e. we don't
@@ -165,7 +173,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
node->RemoveInput(0); // Function.prototype.apply
node->RemoveInput(2); // arguments
NodeProperties::ChangeOp(node, javascript()->CallForwardVarargs(
- start_index, p.tail_call_mode()));
+ 2, start_index, p.tail_call_mode()));
return Changed(node);
}
// Get to the actual frame state from which to extract the arguments;
@@ -272,94 +280,41 @@ 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;
-}
-
-// ES6 section B.2.2.1.1 get Object.prototype.__proto__
-Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
- DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
- Node* receiver = NodeProperties::GetValueInput(node, 1);
+Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) {
Node* effect = NodeProperties::GetEffectInput(node);
- // Try to determine the {receiver} map.
- ZoneHandleSet<Map> receiver_maps;
+ // Try to determine the {object} map.
+ ZoneHandleSet<Map> object_maps;
NodeProperties::InferReceiverMapsResult result =
- NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
+ NodeProperties::InferReceiverMaps(object, effect, &object_maps);
if (result != NodeProperties::kNoReceiverMaps) {
Handle<Map> candidate_map(
- receiver_maps[0]->GetPrototypeChainRootMap(isolate()));
+ object_maps[0]->GetPrototypeChainRootMap(isolate()));
Handle<Object> candidate_prototype(candidate_map->prototype(), isolate());
+ // We cannot deal with primitives here.
+ if (candidate_map->IsPrimitiveMap()) return NoChange();
+
// Check if we can constant-fold the {candidate_prototype}.
- for (size_t i = 0; i < receiver_maps.size(); ++i) {
- Handle<Map> const receiver_map(
- receiver_maps[i]->GetPrototypeChainRootMap(isolate()));
- if (receiver_map->IsJSProxyMap() ||
- receiver_map->has_hidden_prototype() ||
- receiver_map->is_access_check_needed() ||
- receiver_map->prototype() != *candidate_prototype) {
+ for (size_t i = 0; i < object_maps.size(); ++i) {
+ Handle<Map> const object_map(
+ object_maps[i]->GetPrototypeChainRootMap(isolate()));
+ if (object_map->IsSpecialReceiverMap() ||
+ object_map->has_hidden_prototype() ||
+ object_map->prototype() != *candidate_prototype) {
+ // We exclude special receivers, like JSProxy or API objects that
+ // might require access checks here; we also don't want to deal
+ // with hidden prototypes at this point.
return NoChange();
}
if (result == NodeProperties::kUnreliableReceiverMaps &&
- !receiver_map->is_stable()) {
+ !object_map->is_stable()) {
return NoChange();
}
}
if (result == NodeProperties::kUnreliableReceiverMaps) {
- for (size_t i = 0; i < receiver_maps.size(); ++i) {
- dependencies()->AssumeMapStable(receiver_maps[i]);
+ for (size_t i = 0; i < object_maps.size(); ++i) {
+ dependencies()->AssumeMapStable(object_maps[i]);
}
}
Node* value = jsgraph()->Constant(candidate_prototype);
@@ -370,65 +325,116 @@ Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
return NoChange();
}
+// ES6 section 19.1.2.11 Object.getPrototypeOf ( O )
+Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ Node* object = (node->op()->ValueInputCount() >= 3)
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ return ReduceObjectGetPrototype(node, object);
+}
+
+// ES6 section B.2.2.1.1 get Object.prototype.__proto__
+Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ Node* receiver = NodeProperties::GetValueInput(node, 1);
+ return ReduceObjectGetPrototype(node, receiver);
+}
+
+// ES6 section 26.1.7 Reflect.getPrototypeOf ( target )
+Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
+ DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
+ Node* target = (node->op()->ValueInputCount() >= 3)
+ ? NodeProperties::GetValueInput(node, 2)
+ : jsgraph()->UndefinedConstant();
+ return ReduceObjectGetPrototype(node, target);
+}
+
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, data->IsUndefined(isolate), 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);
}
@@ -448,62 +454,96 @@ Reduction JSCallReducer::ReduceSpreadCall(Node* node, int arity) {
// of spread (except for value uses in frame states).
if (spread->opcode() != IrOpcode::kJSCreateArguments) return NoChange();
for (Edge edge : spread->use_edges()) {
- if (edge.from()->opcode() == IrOpcode::kStateValues) continue;
+ Node* const user = edge.from();
+ if (user == node) continue;
+ // Ignore uses as frame state's locals or parameters.
+ if (user->opcode() == IrOpcode::kStateValues) continue;
+ // Ignore uses as frame state's accumulator.
+ if (user->opcode() == IrOpcode::kFrameState && user->InputAt(2) == spread) {
+ continue;
+ }
if (!NodeProperties::IsValueEdge(edge)) continue;
- if (edge.from() == node) continue;
return NoChange();
}
// Get to the actual frame state from which to extract the arguments;
// we can only optimize this in case the {node} was already inlined into
// some other function (and same for the {spread}).
- CreateArgumentsType type = CreateArgumentsTypeOf(spread->op());
+ CreateArgumentsType const type = CreateArgumentsTypeOf(spread->op());
Node* frame_state = NodeProperties::GetFrameStateInput(spread);
- Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
- if (outer_state->opcode() != IrOpcode::kFrameState) return NoChange();
- FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
- if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
- // Need to take the parameters from the arguments adaptor.
- frame_state = outer_state;
- }
FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
int start_index = 0;
+ // Determine the formal parameter count;
+ Handle<SharedFunctionInfo> shared;
+ if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
+ int formal_parameter_count = shared->internal_formal_parameter_count();
if (type == CreateArgumentsType::kMappedArguments) {
- // Mapped arguments (sloppy mode) cannot be handled if they are aliased.
- Handle<SharedFunctionInfo> shared;
- if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
- if (shared->internal_formal_parameter_count() != 0) return NoChange();
+ // Mapped arguments (sloppy mode) that are aliased can only be handled
+ // here if there's no side-effect between the {node} and the {arg_array}.
+ // TODO(turbofan): Further relax this constraint.
+ if (formal_parameter_count != 0) {
+ Node* effect = NodeProperties::GetEffectInput(node);
+ while (effect != spread) {
+ if (effect->op()->EffectInputCount() != 1 ||
+ !(effect->op()->properties() & Operator::kNoWrite)) {
+ return NoChange();
+ }
+ effect = NodeProperties::GetEffectInput(effect);
+ }
+ }
} else if (type == CreateArgumentsType::kRestParameter) {
- Handle<SharedFunctionInfo> shared;
- if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
- start_index = shared->internal_formal_parameter_count();
+ start_index = formal_parameter_count;
// Only check the array iterator protector when we have a rest object.
if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
- // Add a code dependency on the array iterator protector.
- dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
}
+ // Install appropriate code dependencies.
dependencies()->AssumeMapStable(
isolate()->initial_array_iterator_prototype_map());
-
+ if (type == CreateArgumentsType::kRestParameter) {
+ dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
+ }
+ // Remove the spread input from the {node}.
node->RemoveInput(arity--);
-
+ // Check if are spreading to inlined arguments or to the arguments of
+ // the outermost function.
+ Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
+ if (outer_state->opcode() != IrOpcode::kFrameState) {
+ Operator const* op =
+ (node->opcode() == IrOpcode::kJSCallWithSpread)
+ ? javascript()->CallForwardVarargs(arity + 1, start_index,
+ TailCallMode::kDisallow)
+ : javascript()->ConstructForwardVarargs(arity + 2, start_index);
+ NodeProperties::ChangeOp(node, op);
+ return Changed(node);
+ }
+ // Get to the actual frame state from which to extract the arguments;
+ // we can only optimize this in case the {node} was already inlined into
+ // some other function (and same for the {arg_array}).
+ FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
+ if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
+ // Need to take the parameters from the arguments adaptor.
+ frame_state = outer_state;
+ }
// Add the actual parameters to the {node}, skipping the receiver.
Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
- for (int i = start_index + 1; i < state_info.parameter_count(); ++i) {
+ for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
node->InsertInput(graph()->zone(), static_cast<int>(++arity),
parameters->InputAt(i));
}
+ // TODO(turbofan): Collect call counts on spread call/construct and thread it
+ // through here.
if (node->opcode() == IrOpcode::kJSCallWithSpread) {
- NodeProperties::ChangeOp(
- node, javascript()->Call(arity + 1, 7, VectorSlotPair()));
+ NodeProperties::ChangeOp(node, javascript()->Call(arity + 1));
+ Reduction const r = ReduceJSCall(node);
+ return r.Changed() ? r : Changed(node);
} else {
- NodeProperties::ChangeOp(
- node, javascript()->Construct(arity + 2, 7, VectorSlotPair()));
+ NodeProperties::ChangeOp(node, javascript()->Construct(arity + 2));
+ Reduction const r = ReduceJSConstruct(node);
+ return r.Changed() ? r : Changed(node);
}
- return Changed(node);
}
namespace {
@@ -570,8 +610,12 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
return ReduceFunctionPrototypeHasInstance(node);
case Builtins::kNumberConstructor:
return ReduceNumberConstructor(node);
+ case Builtins::kObjectGetPrototypeOf:
+ return ReduceObjectGetPrototypeOf(node);
case Builtins::kObjectPrototypeGetProto:
return ReduceObjectPrototypeGetProto(node);
+ case Builtins::kReflectGetPrototypeOf:
+ return ReduceReflectGetPrototypeOf(node);
default:
break;
}
@@ -581,10 +625,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 =
@@ -835,6 +879,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();
}