Commit ea264012 authored by mstarzinger's avatar mstarzinger Committed by Commit bot

[turbofan] Call FastNewContextStub for function context.

This lowers JSCreateFunctionContext nodes to call the above stub for
help with allocating function contexts when possible. It also contains
an implementation for inlined allocations of such contexts, which is
still behind a flag until inlined allocations are ready for prime time.

TEST=unittests/JSTypedLoweringTest.JSCreateFunctionContext
R=mvstanton@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#31068}
parent aa6a654a
......@@ -226,6 +226,13 @@ Callable CodeFactory::FastCloneShallowObject(Isolate* isolate, int length) {
}
// static
Callable CodeFactory::FastNewContext(Isolate* isolate, int slot_count) {
FastNewContextStub stub(isolate, slot_count);
return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor());
}
// static
Callable CodeFactory::FastNewClosure(Isolate* isolate,
LanguageMode language_mode,
......
......@@ -85,6 +85,7 @@ class CodeFactory final {
static Callable FastCloneShallowArray(Isolate* isolate);
static Callable FastCloneShallowObject(Isolate* isolate, int length);
static Callable FastNewContext(Isolate* isolate, int slot_count);
static Callable FastNewClosure(Isolate* isolate, LanguageMode language_mode,
FunctionKind kind);
......
......@@ -535,10 +535,10 @@ bool AstGraphBuilder::CreateGraph(bool stack_check) {
env.RawParameterBind(0, patched_receiver);
}
// Build function context only if there are context allocated variables.
// Build local context only if there are context allocated variables.
if (info()->num_heap_slots() > 0) {
// Push a new inner context scope for the function.
Node* inner_context = BuildLocalFunctionContext(GetFunctionContext());
// Push a new inner context scope for the current activation.
Node* inner_context = BuildLocalActivationContext(GetFunctionContext());
ContextScope top_context(this, scope, inner_context);
CreateGraphBody(stack_check);
} else {
......@@ -3093,15 +3093,13 @@ Node* AstGraphBuilder::BuildPatchReceiverToGlobalProxy(Node* receiver) {
}
Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context) {
Node* AstGraphBuilder::BuildLocalActivationContext(Node* context) {
Scope* scope = info()->scope();
Node* closure = GetFunctionClosure();
// Allocate a new local context.
Node* local_context =
scope->is_script_scope()
? BuildLocalScriptContext(scope)
: NewNode(javascript()->CreateFunctionContext(), closure);
Node* local_context = scope->is_script_scope()
? BuildLocalScriptContext(scope)
: BuildLocalFunctionContext(scope);
if (scope->has_this_declaration() && scope->receiver()->IsContextSlot()) {
Node* receiver = environment()->RawParameterLookup(0);
......@@ -3128,6 +3126,18 @@ Node* AstGraphBuilder::BuildLocalFunctionContext(Node* context) {
}
Node* AstGraphBuilder::BuildLocalFunctionContext(Scope* scope) {
DCHECK(scope->is_function_scope());
// Allocate a new local context.
int slot_count = scope->num_heap_slots() - Context::MIN_CONTEXT_SLOTS;
const Operator* op = javascript()->CreateFunctionContext(slot_count);
Node* local_context = NewNode(op, GetFunctionClosure());
return local_context;
}
Node* AstGraphBuilder::BuildLocalScriptContext(Scope* scope) {
DCHECK(scope->is_script_scope());
......
......@@ -249,7 +249,8 @@ class AstGraphBuilder : public AstVisitor {
Node* BuildPatchReceiverToGlobalProxy(Node* receiver);
// Builders to create local function, script and block contexts.
Node* BuildLocalFunctionContext(Node* context);
Node* BuildLocalActivationContext(Node* context);
Node* BuildLocalFunctionContext(Scope* scope);
Node* BuildLocalScriptContext(Scope* scope);
Node* BuildLocalBlockContext(Scope* scope);
......
......@@ -472,7 +472,6 @@ const CreateClosureParameters& CreateClosureParametersOf(const Operator* op) {
V(ForInPrepare, Operator::kNoProperties, 1, 3) \
V(ForInStep, Operator::kPure, 1, 1) \
V(StackCheck, Operator::kNoProperties, 0, 0) \
V(CreateFunctionContext, Operator::kNoProperties, 1, 1) \
V(CreateWithContext, Operator::kNoProperties, 2, 1) \
V(CreateModuleContext, Operator::kNoProperties, 2, 1)
......@@ -773,6 +772,15 @@ const Operator* JSOperatorBuilder::CreateLiteralObject(int literal_flags) {
}
const Operator* JSOperatorBuilder::CreateFunctionContext(int slot_count) {
return new (zone()) Operator1<int>( // --
IrOpcode::kJSCreateFunctionContext, Operator::kNoProperties, // opcode
"JSCreateFunctionContext", // name
1, 1, 1, 1, 1, 2, // counts
slot_count); // parameter
}
const Operator* JSOperatorBuilder::CreateCatchContext(
const Handle<String>& name) {
return new (zone()) Operator1<Handle<String>, Handle<String>::equal_to,
......
......@@ -559,7 +559,7 @@ class JSOperatorBuilder final : public ZoneObject {
const Operator* StackCheck();
const Operator* CreateFunctionContext();
const Operator* CreateFunctionContext(int slot_count);
const Operator* CreateCatchContext(const Handle<String>& name);
const Operator* CreateWithContext();
const Operator* CreateBlockContext(const Handle<ScopeInfo>& scpope_info);
......
......@@ -1260,12 +1260,69 @@ Reduction JSTypedLowering::ReduceJSCreateLiteralObject(Node* node) {
}
Reduction JSTypedLowering::ReduceJSCreateFunctionContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateFunctionContext, node->opcode());
int slot_count = OpParameter<int>(node->op());
// Use inline allocation for function contexts up to a size limit.
if (FLAG_turbo_allocate && slot_count < kFunctionContextAllocationLimit) {
// JSCreateFunctionContext[slot_count < limit]](fun)
Node* const effect = NodeProperties::GetEffectInput(node);
Node* const control = NodeProperties::GetControlInput(node);
Node* const closure = NodeProperties::GetValueInput(node, 0);
Node* const context = NodeProperties::GetContextInput(node);
Node* const extension = jsgraph()->ZeroConstant();
Node* const load = graph()->NewNode(
simplified()->LoadField(
AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX)),
context, effect, control);
AllocationBuilder a(jsgraph(), simplified(), effect, control);
STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == 4); // Ensure fully covered.
int context_length = slot_count + Context::MIN_CONTEXT_SLOTS;
a.AllocateArray(context_length, factory()->function_context_map());
a.Store(AccessBuilder::ForContextSlot(Context::CLOSURE_INDEX), closure);
a.Store(AccessBuilder::ForContextSlot(Context::PREVIOUS_INDEX), context);
a.Store(AccessBuilder::ForContextSlot(Context::EXTENSION_INDEX), extension);
a.Store(AccessBuilder::ForContextSlot(Context::GLOBAL_OBJECT_INDEX), load);
for (int i = Context::MIN_CONTEXT_SLOTS; i < context_length; ++i) {
a.Store(AccessBuilder::ForContextSlot(i), jsgraph()->TheHoleConstant());
}
// TODO(mstarzinger): We could mutate {node} into the allocation instead.
NodeProperties::SetType(a.allocation(), NodeProperties::GetType(node));
ReplaceWithValue(node, node, a.effect());
node->ReplaceInput(0, a.allocation());
node->ReplaceInput(1, a.effect());
node->TrimInputCount(2);
NodeProperties::ChangeOp(node, common()->Finish(1));
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();
}
Reduction JSTypedLowering::ReduceJSCreateWithContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateWithContext, node->opcode());
Node* const input = NodeProperties::GetValueInput(node, 0);
Type* input_type = NodeProperties::GetType(input);
// Use inline allocation for with contexts for regular objects.
if (FLAG_turbo_allocate && input_type->Is(Type::Receiver())) {
// JSCreateWithContext(o:receiver, f)
// JSCreateWithContext(o:receiver, fun)
Node* const effect = NodeProperties::GetEffectInput(node);
Node* const control = NodeProperties::GetControlInput(node);
Node* const closure = NodeProperties::GetValueInput(node, 1);
......@@ -1290,6 +1347,7 @@ Reduction JSTypedLowering::ReduceJSCreateWithContext(Node* node) {
NodeProperties::ChangeOp(node, common()->Finish(1));
return Changed(node);
}
return NoChange();
}
......@@ -1298,8 +1356,10 @@ Reduction JSTypedLowering::ReduceJSCreateBlockContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSCreateBlockContext, node->opcode());
Handle<ScopeInfo> scope_info = OpParameter<Handle<ScopeInfo>>(node);
int context_length = scope_info->ContextLength();
// Use inline allocation for block contexts up to a size limit.
if (FLAG_turbo_allocate && context_length < kBlockContextAllocationLimit) {
// JSCreateBlockContext(s:scope[length < limit], f)
// JSCreateBlockContext[scope[length < limit]](fun)
Node* const effect = NodeProperties::GetEffectInput(node);
Node* const control = NodeProperties::GetControlInput(node);
Node* const closure = NodeProperties::GetValueInput(node, 1);
......@@ -1328,6 +1388,7 @@ Reduction JSTypedLowering::ReduceJSCreateBlockContext(Node* node) {
NodeProperties::ChangeOp(node, common()->Finish(1));
return Changed(node);
}
return NoChange();
}
......@@ -1735,6 +1796,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSCreateLiteralArray(node);
case IrOpcode::kJSCreateLiteralObject:
return ReduceJSCreateLiteralObject(node);
case IrOpcode::kJSCreateFunctionContext:
return ReduceJSCreateFunctionContext(node);
case IrOpcode::kJSCreateWithContext:
return ReduceJSCreateWithContext(node);
case IrOpcode::kJSCreateBlockContext:
......
......@@ -61,6 +61,7 @@ class JSTypedLowering final : public AdvancedReducer {
Reduction ReduceJSCreateClosure(Node* node);
Reduction ReduceJSCreateLiteralArray(Node* node);
Reduction ReduceJSCreateLiteralObject(Node* node);
Reduction ReduceJSCreateFunctionContext(Node* node);
Reduction ReduceJSCreateWithContext(Node* node);
Reduction ReduceJSCreateBlockContext(Node* node);
Reduction ReduceJSCallFunction(Node* node);
......@@ -85,6 +86,7 @@ class JSTypedLowering final : public AdvancedReducer {
MachineOperatorBuilder* machine() const;
// Limits up to which context allocations are inlined.
static const int kFunctionContextAllocationLimit = 16;
static const int kBlockContextAllocationLimit = 16;
JSGraph* jsgraph_;
......
......@@ -274,7 +274,7 @@ TEST_F(JSContextRelaxationTest,
Node* const input1 = Parameter(1);
Node* const context = Parameter(2);
Node* const outer_context = Parameter(3);
const Operator* op = javascript()->CreateFunctionContext();
const Operator* op = javascript()->CreateFunctionContext(0);
Node* const effect = graph()->start();
Node* const control = graph()->start();
Node* nested_context =
......
......@@ -82,7 +82,6 @@ const SharedOperator kSharedOperators[] = {
SHARED(HasProperty, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(TypeOf, Operator::kEliminatable, 1, 0, 1, 0, 1, 1, 0),
SHARED(InstanceOf, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(CreateFunctionContext, Operator::kNoProperties, 1, 0, 1, 1, 1, 1, 2),
SHARED(CreateWithContext, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(CreateModuleContext, Operator::kNoProperties, 2, 0, 1, 1, 1, 1, 2),
#undef SHARED
......
......@@ -1086,12 +1086,50 @@ TEST_F(JSTypedLoweringTest, JSCreateLiteralObject) {
}
// -----------------------------------------------------------------------------
// JSCreateFunctionContext
TEST_F(JSTypedLoweringTest, JSCreateFunctionContextViaInlinedAllocation) {
if (!FLAG_turbo_allocate) return;
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(8), closure,
context, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsFinish(IsAllocate(IsNumberConstant(Context::SizeFor(
8 + Context::MIN_CONTEXT_SLOTS)),
effect, control),
_));
}
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
TEST_F(JSTypedLoweringTest, JSCreateWithContext) {
FLAG_turbo_allocate = true;
if (!FLAG_turbo_allocate) return;
Node* const object = Parameter(Type::Receiver());
Node* const closure = Parameter(Type::Any());
Node* const context = Parameter(Type::Any());
......
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