From a38f9dddd19a90039f0914b88ab37a3837a3c2f6 Mon Sep 17 00:00:00 2001 From: mstarzinger Date: Mon, 27 Apr 2015 02:08:20 -0700 Subject: [PATCH] [turbofan] Use FastNewClosureStub if possible. This introduces a JSCreateClosure operator which can be lowered by the typed pipeline to the aforementioned stub. It also allows for further optimizations of closure creation. R=titzer@chromium.org Review URL: https://codereview.chromium.org/1105513002 Cr-Commit-Position: refs/heads/master@{#28058} --- src/code-factory.cc | 9 +++++ src/code-factory.h | 3 ++ src/compiler/ast-graph-builder.cc | 7 ++-- src/compiler/js-generic-lowering.cc | 8 +++++ src/compiler/js-operator.cc | 42 ++++++++++++++++++++++ src/compiler/js-operator.h | 28 +++++++++++++++ src/compiler/js-typed-lowering.cc | 30 ++++++++++++++++ src/compiler/js-typed-lowering.h | 1 + src/compiler/opcodes.h | 1 + src/compiler/typer.cc | 5 +++ src/compiler/verifier.cc | 4 +++ .../compiler/js-typed-lowering-unittest.cc | 26 ++++++++++++++ 12 files changed, 160 insertions(+), 4 deletions(-) diff --git a/src/code-factory.cc b/src/code-factory.cc index c4063b9..7277448 100644 --- a/src/code-factory.cc +++ b/src/code-factory.cc @@ -159,6 +159,15 @@ Callable CodeFactory::FastCloneShallowObject(Isolate* isolate, int length) { // static +Callable CodeFactory::FastNewClosure(Isolate* isolate, + LanguageMode language_mode, + FunctionKind kind) { + FastNewClosureStub stub(isolate, language_mode, kind); + return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor()); +} + + +// static Callable CodeFactory::AllocateHeapNumber(Isolate* isolate) { AllocateHeapNumberStub stub(isolate); return Callable(stub.GetCode(), stub.GetCallInterfaceDescriptor()); diff --git a/src/code-factory.h b/src/code-factory.h index 858cfba..16ef6d5 100644 --- a/src/code-factory.h +++ b/src/code-factory.h @@ -69,6 +69,9 @@ class CodeFactory final { static Callable FastCloneShallowArray(Isolate* isolate); static Callable FastCloneShallowObject(Isolate* isolate, int length); + static Callable FastNewClosure(Isolate* isolate, LanguageMode language_mode, + FunctionKind kind); + static Callable AllocateHeapNumber(Isolate* isolate); static Callable CallFunction(Isolate* isolate, int argc, diff --git a/src/compiler/ast-graph-builder.cc b/src/compiler/ast-graph-builder.cc index 4e480d6..03fee58 100644 --- a/src/compiler/ast-graph-builder.cc +++ b/src/compiler/ast-graph-builder.cc @@ -1476,10 +1476,9 @@ void AstGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { } // Create node to instantiate a new closure. - Node* info = jsgraph()->Constant(shared_info); - Node* pretenure = jsgraph()->BooleanConstant(expr->pretenure()); - const Operator* op = javascript()->CallRuntime(Runtime::kNewClosure, 3); - Node* value = NewNode(op, context, info, pretenure); + PretenureFlag pretenure = expr->pretenure() ? TENURED : NOT_TENURED; + const Operator* op = javascript()->CreateClosure(shared_info, pretenure); + Node* value = NewNode(op, context); ast_context()->ProduceValue(value); } diff --git a/src/compiler/js-generic-lowering.cc b/src/compiler/js-generic-lowering.cc index e8eab0f..611b5c9 100644 --- a/src/compiler/js-generic-lowering.cc +++ b/src/compiler/js-generic-lowering.cc @@ -414,6 +414,14 @@ void JSGenericLowering::LowerJSStoreContext(Node* node) { } +void JSGenericLowering::LowerJSCreateClosure(Node* node) { + CreateClosureParameters p = CreateClosureParametersOf(node->op()); + node->InsertInput(zone(), 1, jsgraph()->HeapConstant(p.shared_info())); + node->InsertInput(zone(), 2, jsgraph()->BooleanConstant(p.pretenure())); + ReplaceWithRuntimeCall(node, Runtime::kNewClosure); +} + + void JSGenericLowering::LowerJSCreateCatchContext(Node* node) { Unique name = OpParameter>(node); node->InsertInput(zone(), 0, jsgraph()->HeapConstant(name)); diff --git a/src/compiler/js-operator.cc b/src/compiler/js-operator.cc index 716592b..8812b6e 100644 --- a/src/compiler/js-operator.cc +++ b/src/compiler/js-operator.cc @@ -208,6 +208,37 @@ const StoreNamedParameters& StoreNamedParametersOf(const Operator* op) { } +bool operator==(CreateClosureParameters const& lhs, + CreateClosureParameters const& rhs) { + return lhs.pretenure() == rhs.pretenure() && + lhs.shared_info().is_identical_to(rhs.shared_info()); +} + + +bool operator!=(CreateClosureParameters const& lhs, + CreateClosureParameters const& rhs) { + return !(lhs == rhs); +} + + +size_t hash_value(CreateClosureParameters const& p) { + // TODO(mstarzinger): Include hash of the SharedFunctionInfo here. + base::hash h; + return h(p.pretenure()); +} + + +std::ostream& operator<<(std::ostream& os, CreateClosureParameters const& p) { + return os << p.pretenure() << ", " << Brief(*p.shared_info()); +} + + +const CreateClosureParameters& CreateClosureParametersOf(const Operator* op) { + DCHECK_EQ(IrOpcode::kJSCreateClosure, op->opcode()); + return OpParameter(op); +} + + #define CACHED_OP_LIST(V) \ V(Equal, Operator::kNoProperties, 2, 1) \ V(NotEqual, Operator::kNoProperties, 2, 1) \ @@ -437,6 +468,17 @@ const Operator* JSOperatorBuilder::StoreContext(size_t depth, size_t index) { } +const Operator* JSOperatorBuilder::CreateClosure( + Handle shared_info, PretenureFlag pretenure) { + CreateClosureParameters parameters(shared_info, pretenure); + return new (zone()) Operator1( // -- + IrOpcode::kJSCreateClosure, Operator::kNoThrow, // opcode + "JSCreateClosure", // name + 1, 1, 1, 1, 1, 0, // counts + parameters); // parameter +} + + const Operator* JSOperatorBuilder::CreateCatchContext( const Unique& name) { return new (zone()) Operator1>( // -- diff --git a/src/compiler/js-operator.h b/src/compiler/js-operator.h index 0b6ec9c..bdb0c9c 100644 --- a/src/compiler/js-operator.h +++ b/src/compiler/js-operator.h @@ -206,6 +206,32 @@ std::ostream& operator<<(std::ostream&, StoreNamedParameters const&); const StoreNamedParameters& StoreNamedParametersOf(const Operator* op); +// Defines shared information for the closure that should be created. This is +// used as a parameter by JSCreateClosure operators. +class CreateClosureParameters final { + public: + CreateClosureParameters(Handle shared_info, + PretenureFlag pretenure) + : shared_info_(shared_info), pretenure_(pretenure) {} + + Handle shared_info() const { return shared_info_; } + PretenureFlag pretenure() const { return pretenure_; } + + private: + const Handle shared_info_; + const PretenureFlag pretenure_; +}; + +bool operator==(CreateClosureParameters const&, CreateClosureParameters const&); +bool operator!=(CreateClosureParameters const&, CreateClosureParameters const&); + +size_t hash_value(CreateClosureParameters const&); + +std::ostream& operator<<(std::ostream&, CreateClosureParameters const&); + +const CreateClosureParameters& CreateClosureParametersOf(const Operator* op); + + // Interface for building JavaScript-level operators, e.g. directly from the // AST. Most operators have no parameters, thus can be globally shared for all // graphs. @@ -242,6 +268,8 @@ class JSOperatorBuilder final : public ZoneObject { const Operator* Yield(); const Operator* Create(); + const Operator* CreateClosure(Handle shared_info, + PretenureFlag pretenure); const Operator* CallFunction(size_t arity, CallFunctionFlags flags); const Operator* CallRuntime(Runtime::FunctionId id, size_t arity); diff --git a/src/compiler/js-typed-lowering.cc b/src/compiler/js-typed-lowering.cc index a3fbb66..554ef75 100644 --- a/src/compiler/js-typed-lowering.cc +++ b/src/compiler/js-typed-lowering.cc @@ -2,9 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/code-factory.h" #include "src/compiler/access-builder.h" #include "src/compiler/js-graph.h" #include "src/compiler/js-typed-lowering.h" +#include "src/compiler/linkage.h" #include "src/compiler/node-matchers.h" #include "src/compiler/node-properties.h" #include "src/compiler/operator-properties.h" @@ -923,6 +925,32 @@ Reduction JSTypedLowering::ReduceJSStoreContext(Node* node) { } +Reduction JSTypedLowering::ReduceJSCreateClosure(Node* node) { + DCHECK_EQ(IrOpcode::kJSCreateClosure, node->opcode()); + CreateClosureParameters const& p = CreateClosureParametersOf(node->op()); + Handle 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->ReplaceInput(0, jsgraph()->HeapConstant(shared)); + node->InsertInput(graph()->zone(), 0, stub_code); + node->set_op(new_op); + return Changed(node); + } + + return NoChange(); +} + + Reduction JSTypedLowering::Reduce(Node* node) { // Check if the output type is a singleton. In that case we already know the // result value and can simply replace the node if it's eliminable. @@ -1009,6 +1037,8 @@ Reduction JSTypedLowering::Reduce(Node* node) { return ReduceJSLoadContext(node); case IrOpcode::kJSStoreContext: return ReduceJSStoreContext(node); + case IrOpcode::kJSCreateClosure: + return ReduceJSCreateClosure(node); default: break; } diff --git a/src/compiler/js-typed-lowering.h b/src/compiler/js-typed-lowering.h index d9f2552..7f4b49e 100644 --- a/src/compiler/js-typed-lowering.h +++ b/src/compiler/js-typed-lowering.h @@ -54,6 +54,7 @@ class JSTypedLowering final : public Reducer { Reduction ReduceJSToNumber(Node* node); Reduction ReduceJSToStringInput(Node* input); Reduction ReduceJSToString(Node* node); + Reduction ReduceJSCreateClosure(Node* node); Reduction ReduceNumberBinop(Node* node, const Operator* numberOp); Reduction ReduceInt32Binop(Node* node, const Operator* intOp); Reduction ReduceUI32Shift(Node* node, Signedness left_signedness, diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index e79e911..39f7cdb 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -106,6 +106,7 @@ #define JS_OBJECT_OP_LIST(V) \ V(JSCreate) \ + V(JSCreateClosure) \ V(JSLoadProperty) \ V(JSLoadNamed) \ V(JSStoreProperty) \ diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc index bcf315d..0e7d31c 100644 --- a/src/compiler/typer.cc +++ b/src/compiler/typer.cc @@ -1311,6 +1311,11 @@ Bounds Typer::Visitor::TypeJSCreate(Node* node) { } +Bounds Typer::Visitor::TypeJSCreateClosure(Node* node) { + return Bounds(Type::None(), Type::OtherObject()); +} + + Type* Typer::Visitor::JSLoadPropertyTyper(Type* object, Type* name, Typer* t) { // TODO(rossberg): Use range types and sized array types to filter undefined. if (object->IsArray() && name->Is(Type::Integral32())) { diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index e216428..733f8d7 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -492,6 +492,10 @@ void Verifier::Visitor::Check(Node* node) { // Type is Object. CheckUpperIs(node, Type::Object()); break; + case IrOpcode::kJSCreateClosure: + // Type is Function. + CheckUpperIs(node, Type::OtherObject()); + break; case IrOpcode::kJSLoadProperty: case IrOpcode::kJSLoadNamed: // Type can be anything. diff --git a/test/unittests/compiler/js-typed-lowering-unittest.cc b/test/unittests/compiler/js-typed-lowering-unittest.cc index 8652ed8..e269db0 100644 --- a/test/unittests/compiler/js-typed-lowering-unittest.cc +++ b/test/unittests/compiler/js-typed-lowering-unittest.cc @@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "src/code-factory.h" #include "src/compiler/access-builder.h" #include "src/compiler/js-graph.h" #include "src/compiler/js-operator.h" @@ -14,6 +15,7 @@ #include "test/unittests/compiler/node-test-utils.h" #include "testing/gmock-support.h" +using testing::_; using testing::BitEq; using testing::IsNaN; @@ -880,6 +882,30 @@ TEST_F(JSTypedLoweringTest, JSLoadNamedGlobalConstants) { } } + +// ----------------------------------------------------------------------------- +// JSCreateClosure + + +TEST_F(JSTypedLoweringTest, JSCreateClosure) { + Node* const context = UndefinedConstant(); + Node* const effect = graph()->start(); + Node* const control = graph()->start(); + Handle shared(isolate()->object_function()->shared()); + Reduction r = + Reduce(graph()->NewNode(javascript()->CreateClosure(shared, NOT_TENURED), + context, context, effect, control)); + ASSERT_TRUE(r.Changed()); + EXPECT_THAT( + r.replacement(), + IsCall(_, + IsHeapConstant(Unique::CreateImmovable( + CodeFactory::FastNewClosure(isolate(), shared->language_mode(), + shared->kind()).code())), + IsHeapConstant(Unique::CreateImmovable(shared)), + effect, control)); +} + } // namespace compiler } // namespace internal } // namespace v8 -- 2.7.4