[turbofan] Optimize inlining and direct function calls.
This adds optimized lowering for JSConvertReceiver (in the general case) and JSToObject in typed lowering. It also uses JSConvertReceiver for direct calls in typed lowering. R=mstarzinger@chromium.org BUG=v8:4493 LOG=n Review URL: https://codereview.chromium.org/1431543002 Cr-Commit-Position: refs/heads/master@{#31676}
This commit is contained in:
parent
0df0e254a3
commit
47c0cb1d14
@ -844,6 +844,85 @@ Reduction JSTypedLowering::ReduceJSToString(Node* node) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Reduction JSTypedLowering::ReduceJSToObject(Node* node) {
|
||||||
|
DCHECK_EQ(IrOpcode::kJSToObject, node->opcode());
|
||||||
|
Node* receiver = NodeProperties::GetValueInput(node, 0);
|
||||||
|
Type* receiver_type = NodeProperties::GetType(receiver);
|
||||||
|
Node* context = NodeProperties::GetContextInput(node);
|
||||||
|
Node* frame_state = NodeProperties::GetFrameStateInput(node, 0);
|
||||||
|
Node* effect = NodeProperties::GetEffectInput(node);
|
||||||
|
Node* control = NodeProperties::GetControlInput(node);
|
||||||
|
if (!receiver_type->Is(Type::Receiver())) {
|
||||||
|
// TODO(bmeurer/mstarzinger): Add support for lowering inside try blocks.
|
||||||
|
if (receiver_type->Maybe(Type::NullOrUndefined()) &&
|
||||||
|
NodeProperties::IsExceptionalCall(node)) {
|
||||||
|
// ToObject throws for null or undefined inputs.
|
||||||
|
return NoChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether {receiver} is a Smi.
|
||||||
|
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
|
||||||
|
Node* branch0 =
|
||||||
|
graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
|
||||||
|
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||||
|
Node* etrue0 = effect;
|
||||||
|
|
||||||
|
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
||||||
|
Node* efalse0 = effect;
|
||||||
|
|
||||||
|
// Determine the instance type of {receiver}.
|
||||||
|
Node* receiver_map = efalse0 =
|
||||||
|
graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
||||||
|
receiver, efalse0, if_false0);
|
||||||
|
Node* receiver_instance_type = efalse0 = graph()->NewNode(
|
||||||
|
simplified()->LoadField(AccessBuilder::ForMapInstanceType()),
|
||||||
|
receiver_map, efalse0, if_false0);
|
||||||
|
|
||||||
|
// Check whether {receiver} is a spec object.
|
||||||
|
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
|
||||||
|
Node* check1 =
|
||||||
|
graph()->NewNode(machine()->Uint32LessThanOrEqual(),
|
||||||
|
jsgraph()->Uint32Constant(FIRST_JS_RECEIVER_TYPE),
|
||||||
|
receiver_instance_type);
|
||||||
|
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
|
||||||
|
check1, if_false0);
|
||||||
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
|
Node* etrue1 = efalse0;
|
||||||
|
|
||||||
|
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
||||||
|
Node* efalse1 = efalse0;
|
||||||
|
|
||||||
|
// Convert {receiver} using the ToObjectStub.
|
||||||
|
Node* if_convert =
|
||||||
|
graph()->NewNode(common()->Merge(2), if_true0, if_false1);
|
||||||
|
Node* econvert =
|
||||||
|
graph()->NewNode(common()->EffectPhi(2), etrue0, efalse1, if_convert);
|
||||||
|
Node* rconvert;
|
||||||
|
{
|
||||||
|
Callable callable = CodeFactory::ToObject(isolate());
|
||||||
|
CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
|
||||||
|
isolate(), graph()->zone(), callable.descriptor(), 0,
|
||||||
|
CallDescriptor::kNeedsFrameState, node->op()->properties());
|
||||||
|
rconvert = econvert = graph()->NewNode(
|
||||||
|
common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
|
||||||
|
receiver, context, frame_state, econvert, if_convert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The {receiver} is already a spec object.
|
||||||
|
Node* if_done = if_true1;
|
||||||
|
Node* edone = etrue1;
|
||||||
|
Node* rdone = receiver;
|
||||||
|
|
||||||
|
control = graph()->NewNode(common()->Merge(2), if_convert, if_done);
|
||||||
|
effect = graph()->NewNode(common()->EffectPhi(2), econvert, edone, control);
|
||||||
|
receiver = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), rconvert,
|
||||||
|
rdone, control);
|
||||||
|
}
|
||||||
|
ReplaceWithValue(node, receiver, effect, control);
|
||||||
|
return Changed(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) {
|
Reduction JSTypedLowering::ReduceJSLoadNamed(Node* node) {
|
||||||
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
|
DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode());
|
||||||
Node* receiver = NodeProperties::GetValueInput(node, 0);
|
Node* receiver = NodeProperties::GetValueInput(node, 0);
|
||||||
@ -1181,7 +1260,62 @@ Reduction JSTypedLowering::ReduceJSConvertReceiver(Node* node) {
|
|||||||
graph()->NewNode(javascript()->ToObject(), receiver, context,
|
graph()->NewNode(javascript()->ToObject(), receiver, context,
|
||||||
frame_state, effect, control);
|
frame_state, effect, control);
|
||||||
} else {
|
} else {
|
||||||
return NoChange();
|
// Check {receiver} for undefined.
|
||||||
|
Node* check0 =
|
||||||
|
graph()->NewNode(simplified()->ReferenceEqual(receiver_type),
|
||||||
|
receiver, jsgraph()->UndefinedConstant());
|
||||||
|
Node* branch0 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
|
||||||
|
check0, control);
|
||||||
|
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||||
|
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
||||||
|
|
||||||
|
// Check {receiver} for null.
|
||||||
|
Node* check1 =
|
||||||
|
graph()->NewNode(simplified()->ReferenceEqual(receiver_type),
|
||||||
|
receiver, jsgraph()->NullConstant());
|
||||||
|
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
|
||||||
|
check1, if_false0);
|
||||||
|
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||||
|
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
||||||
|
|
||||||
|
// Convert {receiver} using ToObject.
|
||||||
|
Node* if_convert = if_false1;
|
||||||
|
Node* econvert = effect;
|
||||||
|
Node* rconvert;
|
||||||
|
{
|
||||||
|
rconvert = econvert =
|
||||||
|
graph()->NewNode(javascript()->ToObject(), receiver, context,
|
||||||
|
frame_state, econvert, if_convert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace {receiver} with global proxy of {context}.
|
||||||
|
Node* if_global =
|
||||||
|
graph()->NewNode(common()->Merge(2), if_true0, if_true1);
|
||||||
|
Node* eglobal = effect;
|
||||||
|
Node* rglobal;
|
||||||
|
{
|
||||||
|
if (context_type->IsConstant()) {
|
||||||
|
Handle<JSObject> global_proxy(
|
||||||
|
Handle<Context>::cast(context_type->AsConstant()->Value())
|
||||||
|
->global_proxy(),
|
||||||
|
isolate());
|
||||||
|
rglobal = jsgraph()->Constant(global_proxy);
|
||||||
|
} else {
|
||||||
|
Node* global_object = eglobal = graph()->NewNode(
|
||||||
|
javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true),
|
||||||
|
context, context, eglobal);
|
||||||
|
rglobal = eglobal =
|
||||||
|
graph()->NewNode(simplified()->LoadField(
|
||||||
|
AccessBuilder::ForGlobalObjectGlobalProxy()),
|
||||||
|
global_object, eglobal, if_global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
control = graph()->NewNode(common()->Merge(2), if_convert, if_global);
|
||||||
|
effect =
|
||||||
|
graph()->NewNode(common()->EffectPhi(2), econvert, eglobal, control);
|
||||||
|
receiver = graph()->NewNode(common()->Phi(kMachAnyTagged, 2), rconvert,
|
||||||
|
rglobal, control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ReplaceWithValue(node, receiver, effect, control);
|
ReplaceWithValue(node, receiver, effect, control);
|
||||||
@ -1554,10 +1688,14 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) {
|
|||||||
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
|
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
|
||||||
CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
|
CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
|
||||||
int const arity = static_cast<int>(p.arity() - 2);
|
int const arity = static_cast<int>(p.arity() - 2);
|
||||||
|
ConvertReceiverMode const convert_mode = p.convert_mode();
|
||||||
Node* target = NodeProperties::GetValueInput(node, 0);
|
Node* target = NodeProperties::GetValueInput(node, 0);
|
||||||
Type* target_type = NodeProperties::GetType(target);
|
Type* target_type = NodeProperties::GetType(target);
|
||||||
Node* receiver = NodeProperties::GetValueInput(node, 1);
|
Node* receiver = NodeProperties::GetValueInput(node, 1);
|
||||||
Type* receiver_type = NodeProperties::GetType(receiver);
|
Type* receiver_type = NodeProperties::GetType(receiver);
|
||||||
|
Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
|
||||||
|
Node* effect = NodeProperties::GetEffectInput(node);
|
||||||
|
Node* control = NodeProperties::GetControlInput(node);
|
||||||
|
|
||||||
// Check if {target} is a known JSFunction.
|
// Check if {target} is a known JSFunction.
|
||||||
if (target_type->IsConstant() &&
|
if (target_type->IsConstant() &&
|
||||||
@ -1566,28 +1704,27 @@ Reduction JSTypedLowering::ReduceJSCallFunction(Node* node) {
|
|||||||
Handle<JSFunction>::cast(target_type->AsConstant()->Value());
|
Handle<JSFunction>::cast(target_type->AsConstant()->Value());
|
||||||
Handle<SharedFunctionInfo> shared(function->shared(), isolate());
|
Handle<SharedFunctionInfo> shared(function->shared(), isolate());
|
||||||
if (shared->internal_formal_parameter_count() == arity) {
|
if (shared->internal_formal_parameter_count() == arity) {
|
||||||
// Check if we need to wrap the {receiver}.
|
|
||||||
if (is_sloppy(shared->language_mode()) && !shared->native()) {
|
|
||||||
if (receiver_type->Is(Type::NullOrUndefined())) {
|
|
||||||
// Change {receiver} to global proxy of {function}.
|
|
||||||
receiver =
|
|
||||||
jsgraph()->Constant(handle(function->global_proxy(), isolate()));
|
|
||||||
} else if (!receiver_type->Is(Type::Receiver())) {
|
|
||||||
// TODO(bmeurer): Add support for wrapping abitrary receivers.
|
|
||||||
return NoChange();
|
|
||||||
}
|
|
||||||
NodeProperties::ReplaceValueInput(node, receiver, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grab the context from the {function}.
|
// Grab the context from the {function}.
|
||||||
Node* context =
|
Node* context =
|
||||||
jsgraph()->Constant(handle(function->context(), isolate()));
|
jsgraph()->Constant(handle(function->context(), isolate()));
|
||||||
NodeProperties::ReplaceContextInput(node, context);
|
NodeProperties::ReplaceContextInput(node, context);
|
||||||
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
|
|
||||||
if (p.AllowTailCalls()) {
|
// Check if we need to convert the {receiver}.
|
||||||
flags |= CallDescriptor::kSupportsTailCalls;
|
if (is_sloppy(shared->language_mode()) && !shared->native() &&
|
||||||
|
!receiver_type->Is(Type::Receiver())) {
|
||||||
|
receiver = effect =
|
||||||
|
graph()->NewNode(javascript()->ConvertReceiver(convert_mode),
|
||||||
|
receiver, context, frame_state, effect, control);
|
||||||
|
NodeProperties::ReplaceEffectInput(node, effect);
|
||||||
|
NodeProperties::ReplaceValueInput(node, receiver, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the eager bailout frame state.
|
||||||
NodeProperties::RemoveFrameStateInput(node, 1);
|
NodeProperties::RemoveFrameStateInput(node, 1);
|
||||||
|
|
||||||
|
// Patch {node} to a direct call.
|
||||||
|
CallDescriptor::Flags flags = CallDescriptor::kNeedsFrameState;
|
||||||
|
if (p.AllowTailCalls()) flags |= CallDescriptor::kSupportsTailCalls;
|
||||||
NodeProperties::ChangeOp(node,
|
NodeProperties::ChangeOp(node,
|
||||||
common()->Call(Linkage::GetJSCallDescriptor(
|
common()->Call(Linkage::GetJSCallDescriptor(
|
||||||
graph()->zone(), false, 1 + arity, flags)));
|
graph()->zone(), false, 1 + arity, flags)));
|
||||||
@ -1943,6 +2080,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
|
|||||||
return ReduceJSToNumber(node);
|
return ReduceJSToNumber(node);
|
||||||
case IrOpcode::kJSToString:
|
case IrOpcode::kJSToString:
|
||||||
return ReduceJSToString(node);
|
return ReduceJSToString(node);
|
||||||
|
case IrOpcode::kJSToObject:
|
||||||
|
return ReduceJSToObject(node);
|
||||||
case IrOpcode::kJSLoadNamed:
|
case IrOpcode::kJSLoadNamed:
|
||||||
return ReduceJSLoadNamed(node);
|
return ReduceJSLoadNamed(node);
|
||||||
case IrOpcode::kJSLoadProperty:
|
case IrOpcode::kJSLoadProperty:
|
||||||
|
@ -56,6 +56,7 @@ class JSTypedLowering final : public AdvancedReducer {
|
|||||||
Reduction ReduceJSToNumber(Node* node);
|
Reduction ReduceJSToNumber(Node* node);
|
||||||
Reduction ReduceJSToStringInput(Node* input);
|
Reduction ReduceJSToStringInput(Node* input);
|
||||||
Reduction ReduceJSToString(Node* node);
|
Reduction ReduceJSToString(Node* node);
|
||||||
|
Reduction ReduceJSToObject(Node* node);
|
||||||
Reduction ReduceJSConvertReceiver(Node* node);
|
Reduction ReduceJSConvertReceiver(Node* node);
|
||||||
Reduction ReduceJSCreateArguments(Node* node);
|
Reduction ReduceJSCreateArguments(Node* node);
|
||||||
Reduction ReduceJSCreateClosure(Node* node);
|
Reduction ReduceJSCreateClosure(Node* node);
|
||||||
|
@ -433,6 +433,36 @@ TEST_F(JSTypedLoweringTest, JSToNumberWithPlainPrimitive) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// JSToObject
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(JSTypedLoweringTest, JSToObjectWithAny) {
|
||||||
|
Node* const input = Parameter(Type::Any(), 0);
|
||||||
|
Node* const context = Parameter(Type::Any(), 1);
|
||||||
|
Node* const frame_state = EmptyFrameState();
|
||||||
|
Node* const effect = graph()->start();
|
||||||
|
Node* const control = graph()->start();
|
||||||
|
Reduction r = Reduce(graph()->NewNode(javascript()->ToObject(), input,
|
||||||
|
context, frame_state, effect, control));
|
||||||
|
ASSERT_TRUE(r.Changed());
|
||||||
|
EXPECT_THAT(r.replacement(), IsPhi(kMachAnyTagged, _, _, _));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(JSTypedLoweringTest, JSToObjectWithReceiver) {
|
||||||
|
Node* const input = Parameter(Type::Receiver(), 0);
|
||||||
|
Node* const context = Parameter(Type::Any(), 1);
|
||||||
|
Node* const frame_state = EmptyFrameState();
|
||||||
|
Node* const effect = graph()->start();
|
||||||
|
Node* const control = graph()->start();
|
||||||
|
Reduction r = Reduce(graph()->NewNode(javascript()->ToObject(), input,
|
||||||
|
context, frame_state, effect, control));
|
||||||
|
ASSERT_TRUE(r.Changed());
|
||||||
|
EXPECT_EQ(input, r.replacement());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// JSStrictEqual
|
// JSStrictEqual
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user