Commit 7be58299 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Move creation stub fallbacks to JSGenericLowering.

Move all the code that deals with falling back to object creation via
stubs to JSGenericLowering, where we can already deal well with stub
calls. This includes JSCreateLiteralArray, JSCreateLiteralObject,
JSCreateClosure, JSCreateFunctionContext and JSCreateArray.

R=mstarzinger@chromium.org

Review URL: https://codereview.chromium.org/1653353002

Cr-Commit-Position: refs/heads/master@{#33682}
parent 20de3090
......@@ -116,7 +116,6 @@ REPLACE_COMPARE_IC_CALL_WITH_LANGUAGE_MODE(JSGreaterThanOrEqual, Token::GTE)
void JSGenericLowering::Lower##op(Node* node) { \
ReplaceWithRuntimeCall(node, fun); \
}
REPLACE_RUNTIME_CALL(JSCreateFunctionContext, Runtime::kNewFunctionContext)
REPLACE_RUNTIME_CALL(JSCreateWithContext, Runtime::kPushWithContext)
REPLACE_RUNTIME_CALL(JSCreateModuleContext, Runtime::kPushModuleContext)
REPLACE_RUNTIME_CALL(JSConvertReceiver, Runtime::kConvertReceiver)
......@@ -475,7 +474,8 @@ void JSGenericLowering::LowerJSCreateArguments(Node* node) {
void JSGenericLowering::LowerJSCreateArray(Node* node) {
CreateArrayParameters const& p = CreateArrayParametersOf(node->op());
int const arity = static_cast<int>(p.arity());
Node* new_target = node->InputAt(1);
Handle<AllocationSite> const site = p.site();
// TODO(turbofan): We embed the AllocationSite from the Operator at this
// point, which we should not do once we want to both consume the feedback
// but at the same time shared the optimized code across native contexts,
......@@ -483,21 +483,93 @@ void JSGenericLowering::LowerJSCreateArray(Node* node) {
// stored in the type feedback vector after all). Once we go for cross
// context code generation, we should somehow find a way to get to the
// allocation site for the actual native context at runtime.
Node* type_info = p.site().is_null() ? jsgraph()->UndefinedConstant()
: jsgraph()->HeapConstant(p.site());
node->RemoveInput(1);
node->InsertInput(zone(), 1 + arity, new_target);
node->InsertInput(zone(), 2 + arity, type_info);
ReplaceWithRuntimeCall(node, Runtime::kNewArray, arity + 3);
if (!site.is_null()) {
// Reduce {node} to the appropriate ArrayConstructorStub backend.
// Note that these stubs "behave" like JSFunctions, which means they
// expect a receiver on the stack, which they remove. We just push
// undefined for the receiver.
ElementsKind elements_kind = site->GetElementsKind();
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
if (arity == 0) {
ArrayNoArgumentConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
} else if (arity == 1) {
// TODO(bmeurer): Optimize for the 0 length non-holey case?
ArraySingleArgumentConstructorStub stub(
isolate(), GetHoleyElementsKind(elements_kind), override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(1));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
} else {
ArrayNArgumentsConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(),
arity + 1, CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(arity));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
}
} else {
Node* new_target = node->InputAt(1);
Node* type_info = site.is_null() ? jsgraph()->UndefinedConstant()
: jsgraph()->HeapConstant(site);
node->RemoveInput(1);
node->InsertInput(zone(), 1 + arity, new_target);
node->InsertInput(zone(), 2 + arity, type_info);
ReplaceWithRuntimeCall(node, Runtime::kNewArray, arity + 3);
}
}
void JSGenericLowering::LowerJSCreateClosure(Node* node) {
CreateClosureParameters p = CreateClosureParametersOf(node->op());
node->InsertInput(zone(), 0, jsgraph()->HeapConstant(p.shared_info()));
ReplaceWithRuntimeCall(node, (p.pretenure() == TENURED)
? Runtime::kNewClosure_Tenured
: Runtime::kNewClosure);
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
Handle<SharedFunctionInfo> const shared_info = p.shared_info();
node->InsertInput(zone(), 0, jsgraph()->HeapConstant(shared_info));
// Use the FastNewClosureStub that allocates in new space only for nested
// functions that don't need literals cloning.
if (p.pretenure() == NOT_TENURED && shared_info->num_literals() == 0) {
Callable callable = CodeFactory::FastNewClosure(
isolate(), shared_info->language_mode(), shared_info->kind());
ReplaceWithStubCall(node, callable, flags);
} else {
ReplaceWithRuntimeCall(node, (p.pretenure() == TENURED)
? Runtime::kNewClosure_Tenured
: Runtime::kNewClosure);
}
}
void JSGenericLowering::LowerJSCreateFunctionContext(Node* node) {
int const slot_count = OpParameter<int>(node->op());
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
// Use the FastNewContextStub only for function contexts up maximum size.
if (slot_count <= FastNewContextStub::kMaximumSlots) {
Callable callable = CodeFactory::FastNewContext(isolate(), slot_count);
ReplaceWithStubCall(node, callable, flags);
} else {
ReplaceWithRuntimeCall(node, Runtime::kNewFunctionContext);
}
}
......@@ -508,19 +580,42 @@ void JSGenericLowering::LowerJSCreateIterResultObject(Node* node) {
void JSGenericLowering::LowerJSCreateLiteralArray(Node* node) {
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
int const length = Handle<FixedArray>::cast(p.constant())->length();
node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.index()));
node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.constant()));
node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
ReplaceWithRuntimeCall(node, Runtime::kCreateArrayLiteral);
// Use the FastCloneShallowArrayStub only for shallow boilerplates up to the
// initial length limit for arrays with "fast" elements kind.
if ((p.flags() & ArrayLiteral::kShallowElements) != 0 &&
(p.flags() & ArrayLiteral::kIsStrong) == 0 &&
length < JSArray::kInitialMaxFastElementArray) {
Callable callable = CodeFactory::FastCloneShallowArray(isolate());
ReplaceWithStubCall(node, callable, flags);
} else {
node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
ReplaceWithRuntimeCall(node, Runtime::kCreateArrayLiteral);
}
}
void JSGenericLowering::LowerJSCreateLiteralObject(Node* node) {
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
CallDescriptor::Flags flags = AdjustFrameStatesForCall(node);
int const length = Handle<FixedArray>::cast(p.constant())->length();
node->InsertInput(zone(), 1, jsgraph()->SmiConstant(p.index()));
node->InsertInput(zone(), 2, jsgraph()->HeapConstant(p.constant()));
node->InsertInput(zone(), 3, jsgraph()->SmiConstant(p.flags()));
ReplaceWithRuntimeCall(node, Runtime::kCreateObjectLiteral);
// Use the FastCloneShallowObjectStub only for shallow boilerplates without
// elements up to the number of properties that the stubs can handle.
if ((p.flags() & ObjectLiteral::kShallowProperties) != 0 &&
length <= FastCloneShallowObjectStub::kMaximumClonedProperties) {
Callable callable = CodeFactory::FastCloneShallowObject(isolate(), length);
ReplaceWithStubCall(node, callable, flags);
} else {
ReplaceWithRuntimeCall(node, Runtime::kCreateObjectLiteral);
}
}
......
......@@ -1773,78 +1773,6 @@ Reduction JSTypedLowering::ReduceJSCreateArray(Node* node) {
}
}
// Reduce {node} to the appropriate ArrayConstructorStub backend.
// Note that these stubs "behave" like JSFunctions, which means they
// expect a receiver on the stack, which they remove. We just push
// undefined for the receiver.
ElementsKind elements_kind = site->GetElementsKind();
AllocationSiteOverrideMode override_mode =
(AllocationSite::GetMode(elements_kind) == TRACK_ALLOCATION_SITE)
? DISABLE_ALLOCATION_SITES
: DONT_OVERRIDE;
if (p.arity() == 0) {
ArrayNoArgumentConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 1,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
} else if (p.arity() == 1) {
// TODO(bmeurer): Optimize for the 0 length non-holey case?
ArraySingleArgumentConstructorStub stub(
isolate(), GetHoleyElementsKind(elements_kind), override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(), 2,
CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(1));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
} else {
int const arity = static_cast<int>(p.arity());
ArrayNArgumentsConstructorStub stub(isolate(), elements_kind,
override_mode);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), graph()->zone(), stub.GetCallInterfaceDescriptor(),
arity + 1, CallDescriptor::kNeedsFrameState);
node->ReplaceInput(0, jsgraph()->HeapConstant(stub.GetCode()));
node->InsertInput(graph()->zone(), 2, jsgraph()->HeapConstant(site));
node->InsertInput(graph()->zone(), 3, jsgraph()->Int32Constant(arity));
node->InsertInput(graph()->zone(), 4, jsgraph()->UndefinedConstant());
NodeProperties::ChangeOp(node, common()->Call(desc));
return Changed(node);
}
}
Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode());
CreateClosureParameters const& p = CreateClosureParametersOf(node->op());
Handle<SharedFunctionInfo> shared = p.shared_info();
// Use the FastNewClosureStub that allocates in new space only for nested
// functions that don't need literals cloning.
if (p.pretenure() == NOT_TENURED && shared->num_literals() == 0) {
Isolate* isolate = jsgraph()->isolate();
Callable callable = CodeFactory::FastNewClosure(
isolate, shared->language_mode(), shared->kind());
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate, graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags);
const Operator* new_op = common()->Call(desc);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
node->InsertInput(graph()->zone(), 0, stub_code);
node->InsertInput(graph()->zone(), 1, jsgraph()->HeapConstant(shared));
NodeProperties::ChangeOp(node, new_op);
return Changed(node);
}
return NoChange();
}
......@@ -1880,77 +1808,6 @@ Reduction JSTypedLowering::ReduceJSCreateIterResultObject(Node* node) {
}
Reduction JSTypedLowering::ReduceJSCreateLiteralArray(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateLiteralArray, node->opcode());
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
Handle<FixedArray> const constants = Handle<FixedArray>::cast(p.constant());
int const length = constants->length();
int const flags = p.flags();
// Use the FastCloneShallowArrayStub only for shallow boilerplates up to the
// initial length limit for arrays with "fast" elements kind.
// TODO(rossberg): Teach strong mode to FastCloneShallowArrayStub.
if ((flags & ArrayLiteral::kShallowElements) != 0 &&
(flags & ArrayLiteral::kIsStrong) == 0 &&
length < JSArray::kInitialMaxFastElementArray) {
Isolate* isolate = jsgraph()->isolate();
Callable callable = CodeFactory::FastCloneShallowArray(isolate);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate, graph()->zone(), callable.descriptor(), 0,
(OperatorProperties::GetFrameStateInputCount(node->op()) != 0)
? CallDescriptor::kNeedsFrameState
: CallDescriptor::kNoFlags);
const Operator* new_op = common()->Call(desc);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* literal_index = jsgraph()->SmiConstant(p.index());
Node* constant_elements = jsgraph()->HeapConstant(constants);
node->InsertInput(graph()->zone(), 0, stub_code);
node->InsertInput(graph()->zone(), 2, literal_index);
node->InsertInput(graph()->zone(), 3, constant_elements);
NodeProperties::ChangeOp(node, new_op);
return Changed(node);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSCreateLiteralObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateLiteralObject, node->opcode());
CreateLiteralParameters const& p = CreateLiteralParametersOf(node->op());
Handle<FixedArray> const constants = Handle<FixedArray>::cast(p.constant());
// Constants are pairs, see ObjectLiteral::properties_count().
int const length = constants->length() / 2;
int const flags = p.flags();
// Use the FastCloneShallowObjectStub only for shallow boilerplates without
// elements up to the number of properties that the stubs can handle.
if ((flags & ObjectLiteral::kShallowProperties) != 0 &&
length <= FastCloneShallowObjectStub::kMaximumClonedProperties) {
Isolate* isolate = jsgraph()->isolate();
Callable callable = CodeFactory::FastCloneShallowObject(isolate, length);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate, graph()->zone(), callable.descriptor(), 0,
(OperatorProperties::GetFrameStateInputCount(node->op()) != 0)
? CallDescriptor::kNeedsFrameState
: CallDescriptor::kNoFlags);
const Operator* new_op = common()->Call(desc);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
Node* literal_index = jsgraph()->SmiConstant(p.index());
Node* literal_flags = jsgraph()->SmiConstant(flags);
Node* constant_elements = jsgraph()->HeapConstant(constants);
node->InsertInput(graph()->zone(), 0, stub_code);
node->InsertInput(graph()->zone(), 2, literal_index);
node->InsertInput(graph()->zone(), 3, constant_elements);
node->InsertInput(graph()->zone(), 4, literal_flags);
NodeProperties::ChangeOp(node, new_op);
return Changed(node);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode());
int slot_count = OpParameter<int>(node->op());
......@@ -1983,20 +1840,6 @@ Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) {
return Changed(node);
}
// Use the FastNewContextStub only for function contexts up maximum size.
if (slot_count <= FastNewContextStub::kMaximumSlots) {
Isolate* isolate = jsgraph()->isolate();
Callable callable = CodeFactory::FastNewContext(isolate, slot_count);
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate, graph()->zone(), callable.descriptor(), 0,
CallDescriptor::kNoFlags);
const Operator* new_op = common()->Call(desc);
Node* stub_code = jsgraph()->HeapConstant(callable.code());
node->InsertInput(graph()->zone(), 0, stub_code);
NodeProperties::ChangeOp(node, new_op);
return Changed(node);
}
return NoChange();
}
......@@ -2477,14 +2320,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSCreateArguments(node);
case IrOpcode::kJSCreateArray:
return ReduceJSCreateArray(node);
case IrOpcode::kJSCreateClosure:
return ReduceJSCreateClosure(node);
case IrOpcode::kJSCreateIterResultObject:
return ReduceJSCreateIterResultObject(node);
case IrOpcode::kJSCreateLiteralArray:
return ReduceJSCreateLiteralArray(node);
case IrOpcode::kJSCreateLiteralObject:
return ReduceJSCreateLiteralObject(node);
case IrOpcode::kJSCreateFunctionContext:
return ReduceJSCreateFunctionContext(node);
case IrOpcode::kJSCreateWithContext:
......
......@@ -71,10 +71,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSCreate(Node* node);
Reduction ReduceJSCreateArguments(Node* node);
Reduction ReduceJSCreateArray(Node* node);
Reduction ReduceJSCreateClosure(Node* node);
Reduction ReduceJSCreateIterResultObject(Node* node);
Reduction ReduceJSCreateLiteralArray(Node* node);
Reduction ReduceJSCreateLiteralObject(Node* node);
Reduction ReduceJSCreateFunctionContext(Node* node);
Reduction ReduceJSCreateWithContext(Node* node);
Reduction ReduceJSCreateCatchContext(Node* node);
......
......@@ -1031,84 +1031,6 @@ TEST_F(JSTypedLoweringTest, JSCreateArgumentsInlinedRestArray) {
}
// -----------------------------------------------------------------------------
// JSCreateClosure
TEST_F(JSTypedLoweringTest, JSCreateClosure) {
Node* const context = UndefinedConstant();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Handle<SharedFunctionInfo> shared(isolate()->object_function()->shared());
Reduction r =
Reduce(graph()->NewNode(javascript()->CreateClosure(shared, NOT_TENURED),
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsCall(_, IsHeapConstant(CodeFactory::FastNewClosure(
isolate(), shared->language_mode(),
shared->kind()).code()),
IsHeapConstant(shared), effect, control));
}
// -----------------------------------------------------------------------------
// JSCreateLiteralArray
TEST_F(JSTypedLoweringTest, JSCreateLiteralArray) {
Handle<FixedArray> const constant_elements = factory()->NewFixedArray(12);
int const literal_flags = ArrayLiteral::kShallowElements;
int const literal_index = 1;
Node* const closure = Parameter(0);
Node* const context = Parameter(1);
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction const r = Reduce(
graph()->NewNode(javascript()->CreateLiteralArray(
constant_elements, literal_flags, literal_index),
closure, context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsCall(_, IsHeapConstant(
CodeFactory::FastCloneShallowArray(isolate()).code()),
closure, IsNumberConstant(literal_index),
IsHeapConstant(constant_elements), context, frame_state, effect,
control));
}
// -----------------------------------------------------------------------------
// JSCreateLiteralObject
TEST_F(JSTypedLoweringTest, JSCreateLiteralObject) {
Handle<FixedArray> const constant_properties =
factory()->NewFixedArray(6 * 2);
int const literal_flags = ObjectLiteral::kShallowProperties;
int const literal_index = 1;
Node* const closure = Parameter(0);
Node* const context = Parameter(1);
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction const r = Reduce(
graph()->NewNode(javascript()->CreateLiteralObject(
constant_properties, literal_flags, literal_index),
closure, context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
IsCall(_, IsHeapConstant(
CodeFactory::FastCloneShallowObject(isolate(), 6).code()),
closure, IsNumberConstant(literal_index),
IsHeapConstant(constant_properties), _, context, frame_state,
effect, control));
}
// -----------------------------------------------------------------------------
// JSCreateFunctionContext
......@@ -1130,22 +1052,6 @@ TEST_F(JSTypedLoweringTest, JSCreateFunctionContextViaInlinedAllocation) {
}
TEST_F(JSTypedLoweringTest, JSCreateFunctionContextViaStub) {
Node* const closure = Parameter(Type::Any());
Node* const context = Parameter(Type::Any());
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction const r =
Reduce(graph()->NewNode(javascript()->CreateFunctionContext(32), closure,
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsCall(_, IsHeapConstant(
CodeFactory::FastNewContext(isolate(), 32).code()),
closure, context, effect, control));
}
// -----------------------------------------------------------------------------
// JSCreateWithContext
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment