From 4e03645dc42125f92a0f1f19fd5b4ad9a96e3f40 Mon Sep 17 00:00:00 2001 From: "sgjesse@chromium.org" Date: Wed, 19 Aug 2009 07:30:20 +0000 Subject: [PATCH] Analyze functions for assignment to this properties. During parsing functions are analyzed for statements of the form this.x = ...;. These assignments are categorized in two types: simple and non simple. The simple ones are where the right hand side is known to be either a constant or an argument to the function. If a function only contains statements of this type the property names are collected and for the simple assignments the index of the argument or the constant value assigned are stored as well. When the initial map for a function is created and the function consists of only this type of assignemnts the initial map is created with a descriptor array describing these properties which will be known to always exist in an object created from the function. The information on this property assignments is not collected during pre-parsing so if compiling using pre-parse data these optimization hints are not available. Next step will be to use the information collected for the simple assignments to generate constructor code which will create and initialize the object from this information without calling the code for the function. Review URL: http://codereview.chromium.org/172088 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2710 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/ast.h | 20 +++ src/codegen.cc | 4 + src/heap.cc | 53 ++++-- src/ia32/builtins-ia32.cc | 7 +- src/objects-debug.cc | 13 +- src/objects-inl.h | 34 +++- src/objects.cc | 44 +++++ src/objects.h | 52 +++++- src/parser.cc | 271 +++++++++++++++++++++++++++-- src/runtime.cc | 2 + src/v8-counters.h | 2 + src/x64/builtins-x64.cc | 7 +- test/mjsunit/simple-constructor.js | 78 +++++++++ 13 files changed, 547 insertions(+), 40 deletions(-) create mode 100755 test/mjsunit/simple-constructor.js diff --git a/src/ast.h b/src/ast.h index 3a309ac96a..ea83712137 100644 --- a/src/ast.h +++ b/src/ast.h @@ -111,6 +111,7 @@ AST_NODE_LIST(DEF_FORWARD_DECLARATION) // Typedef only introduced to avoid unreadable code. // Please do appreciate the required space in "> >". typedef ZoneList > ZoneStringList; +typedef ZoneList > ZoneObjectList; class AstNode: public ZoneObject { @@ -1226,6 +1227,9 @@ class FunctionLiteral: public Expression { int materialized_literal_count, bool contains_array_literal, int expected_property_count, + bool has_only_this_property_assignments, + bool has_only_simple_this_property_assignments, + Handle this_property_assignments, int num_parameters, int start_position, int end_position, @@ -1236,6 +1240,10 @@ class FunctionLiteral: public Expression { materialized_literal_count_(materialized_literal_count), contains_array_literal_(contains_array_literal), expected_property_count_(expected_property_count), + has_only_this_property_assignments_(has_only_this_property_assignments), + has_only_simple_this_property_assignments_( + has_only_simple_this_property_assignments), + this_property_assignments_(this_property_assignments), num_parameters_(num_parameters), start_position_(start_position), end_position_(end_position), @@ -1265,6 +1273,15 @@ class FunctionLiteral: public Expression { int materialized_literal_count() { return materialized_literal_count_; } bool contains_array_literal() { return contains_array_literal_; } int expected_property_count() { return expected_property_count_; } + bool has_only_this_property_assignments() { + return has_only_this_property_assignments_; + } + bool has_only_simple_this_property_assignments() { + return has_only_simple_this_property_assignments_; + } + Handle this_property_assignments() { + return this_property_assignments_; + } int num_parameters() { return num_parameters_; } bool AllowsLazyCompilation(); @@ -1291,6 +1308,9 @@ class FunctionLiteral: public Expression { int materialized_literal_count_; bool contains_array_literal_; int expected_property_count_; + bool has_only_this_property_assignments_; + bool has_only_simple_this_property_assignments_; + Handle this_property_assignments_; int num_parameters_; int start_position_; int end_position_; diff --git a/src/codegen.cc b/src/codegen.cc index c80d906b34..8e516c0b04 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -255,6 +255,10 @@ void CodeGenerator::SetFunctionInfo(Handle fun, fun->shared()->set_is_expression(lit->is_expression()); fun->shared()->set_is_toplevel(is_toplevel); fun->shared()->set_inferred_name(*lit->inferred_name()); + fun->shared()->SetThisPropertyAssignmentsInfo( + lit->has_only_this_property_assignments(), + lit->has_only_simple_this_property_assignments(), + *lit->this_property_assignments()); } diff --git a/src/heap.cc b/src/heap.cc index 01dfe574af..7d6e442b87 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -1054,6 +1054,7 @@ Object* Heap::AllocateMap(InstanceType instance_type, int instance_size) { map->set_constructor(null_value()); map->set_instance_size(instance_size); map->set_inobject_properties(0); + map->set_pre_allocated_property_fields(0); map->set_instance_descriptors(empty_descriptor_array()); map->set_code_cache(empty_fixed_array()); map->set_unused_property_fields(0); @@ -1611,6 +1612,9 @@ Object* Heap::AllocateSharedFunctionInfo(Object* name) { share->set_start_position_and_type(0); share->set_debug_info(undefined_value()); share->set_inferred_name(empty_string()); + share->set_compiler_hints(0); + share->set_this_property_assignments_count(0); + share->set_this_property_assignments(undefined_value()); return result; } @@ -2050,16 +2054,10 @@ Object* Heap::AllocateArgumentsObject(Object* callee, int length) { Object* Heap::AllocateInitialMap(JSFunction* fun) { ASSERT(!fun->has_initial_map()); - // First create a new map with the expected number of properties being - // allocated in-object. - int expected_nof_properties = fun->shared()->expected_nof_properties(); - int instance_size = JSObject::kHeaderSize + - expected_nof_properties * kPointerSize; - if (instance_size > JSObject::kMaxInstanceSize) { - instance_size = JSObject::kMaxInstanceSize; - expected_nof_properties = (instance_size - JSObject::kHeaderSize) / - kPointerSize; - } + // First create a new map with the size and number of in-object properties + // suggested by the function. + int instance_size = fun->shared()->CalculateInstanceSize(); + int in_object_properties = fun->shared()->CalculateInObjectProperties(); Object* map_obj = Heap::AllocateMap(JS_OBJECT_TYPE, instance_size); if (map_obj->IsFailure()) return map_obj; @@ -2072,9 +2070,33 @@ Object* Heap::AllocateInitialMap(JSFunction* fun) { if (prototype->IsFailure()) return prototype; } Map* map = Map::cast(map_obj); - map->set_inobject_properties(expected_nof_properties); - map->set_unused_property_fields(expected_nof_properties); + map->set_inobject_properties(in_object_properties); + map->set_unused_property_fields(in_object_properties); map->set_prototype(prototype); + + // If the function has only simple this property assignments add field + // descriptors for these to the initial map as the object cannot be + // constructed without having these properties. + ASSERT(in_object_properties <= Map::kMaxPreAllocatedPropertyFields); + if (fun->shared()->has_only_this_property_assignments() && + fun->shared()->this_property_assignments_count() > 0) { + int count = fun->shared()->this_property_assignments_count(); + if (count > in_object_properties) { + count = in_object_properties; + } + DescriptorArray* descriptors = *Factory::NewDescriptorArray(count); + if (descriptors->IsFailure()) return descriptors; + for (int i = 0; i < count; i++) { + String* name = fun->shared()->GetThisPropertyAssignmentName(i); + ASSERT(name->IsSymbol()); + FieldDescriptor field(name, i, NONE); + descriptors->Set(i, &field); + } + descriptors->Sort(); + map->set_instance_descriptors(descriptors); + map->set_pre_allocated_property_fields(count); + map->set_unused_property_fields(in_object_properties - count); + } return map; } @@ -2106,7 +2128,11 @@ Object* Heap::AllocateJSObjectFromMap(Map* map, PretenureFlag pretenure) { ASSERT(map->instance_type() != JS_BUILTINS_OBJECT_TYPE); // Allocate the backing storage for the properties. - int prop_size = map->unused_property_fields() - map->inobject_properties(); + int prop_size = + map->pre_allocated_property_fields() + + map->unused_property_fields() - + map->inobject_properties(); + ASSERT(prop_size >= 0); Object* properties = AllocateFixedArray(prop_size, pretenure); if (properties->IsFailure()) return properties; @@ -2599,6 +2625,7 @@ Object* Heap::CopyFixedArray(FixedArray* src) { Object* Heap::AllocateFixedArray(int length) { + ASSERT(length >= 0); if (length == 0) return empty_fixed_array(); Object* result = AllocateRawFixedArray(length); if (!result->IsFailure()) { diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index bd87f8b96f..6de9de6ee5 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -180,12 +180,16 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // eax: initial map // ebx: JSObject // edi: start of next object + // Calculate the total number of properties described by the map. __ movzx_b(edx, FieldOperand(eax, Map::kUnusedPropertyFieldsOffset)); - __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset)); + __ movzx_b(ecx, FieldOperand(eax, Map::kPreAllocatedPropertyFieldsOffset)); + __ add(edx, Operand(ecx)); // Calculate unused properties past the end of the in-object properties. + __ movzx_b(ecx, FieldOperand(eax, Map::kInObjectPropertiesOffset)); __ sub(edx, Operand(ecx)); // Done if no extra properties are to be allocated. __ j(zero, &allocated); + __ Assert(positive, "Property allocation count failed."); // Scale the number of elements by pointer size and add the header for // FixedArrays to the start of the next object calculation from above. @@ -321,6 +325,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ pop(ecx); __ lea(esp, Operand(esp, ebx, times_2, 1 * kPointerSize)); // 1 ~ receiver __ push(ecx); + __ IncrementCounter(&Counters::constructed_objects, 1); __ ret(0); } diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 40001f9619..e97b207bac 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -371,9 +371,9 @@ void JSObject::JSObjectVerify() { VerifyHeapPointer(properties()); VerifyHeapPointer(elements()); if (HasFastProperties()) { - CHECK(map()->unused_property_fields() == - (map()->inobject_properties() + properties()->length() - - map()->NextFreePropertyIndex())); + CHECK_EQ(map()->unused_property_fields(), + (map()->inobject_properties() + properties()->length() - + map()->NextFreePropertyIndex())); } } @@ -462,6 +462,7 @@ void Map::MapPrint() { HeapObject::PrintHeader("Map"); PrintF(" - type: %s\n", TypeToString(instance_type())); PrintF(" - instance size: %d\n", instance_size()); + PrintF(" - inobject properties: %d\n", inobject_properties()); PrintF(" - unused property fields: %d\n", unused_property_fields()); if (is_hidden_prototype()) { PrintF(" - hidden_prototype\n"); @@ -619,6 +620,12 @@ void SharedFunctionInfo::SharedFunctionInfoPrint() { PrintF("\n - debug info = "); debug_info()->ShortPrint(); PrintF("\n - length = %d", length()); + PrintF("\n - has_only_this_property_assignments = %d", + has_only_this_property_assignments()); + PrintF("\n - has_only_simple_this_property_assignments = %d", + has_only_simple_this_property_assignments()); + PrintF("\n - this_property_assignments = "); + this_property_assignments()->ShortPrint(); PrintF("\n"); } diff --git a/src/objects-inl.h b/src/objects-inl.h index a3bd3ce1ca..7b750b61ea 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -91,7 +91,13 @@ PropertyDetails PropertyDetails::AsDeleted() { } -#define BOOL_ACCESSORS(holder, field, name, offset) \ +#define BOOL_GETTER(holder, field, name, offset) \ + bool holder::name() { \ + return BooleanBit::get(field(), offset); \ + } \ + + +#define BOOL_ACCESSORS(holder, field, name, offset) \ bool holder::name() { \ return BooleanBit::get(field(), offset); \ } \ @@ -1937,6 +1943,11 @@ int Map::inobject_properties() { } +int Map::pre_allocated_property_fields() { + return READ_BYTE_FIELD(this, kPreAllocatedPropertyFieldsOffset); +} + + int HeapObject::SizeFromMap(Map* map) { InstanceType instance_type = map->instance_type(); // Only inline the most frequent cases. @@ -1969,6 +1980,14 @@ void Map::set_inobject_properties(int value) { } +void Map::set_pre_allocated_property_fields(int value) { + ASSERT(0 <= value && value < 256); + WRITE_BYTE_FIELD(this, + kPreAllocatedPropertyFieldsOffset, + static_cast(value)); +} + + InstanceType Map::instance_type() { return static_cast(READ_BYTE_FIELD(this, kInstanceTypeOffset)); } @@ -2298,6 +2317,8 @@ ACCESSORS(SharedFunctionInfo, function_data, Object, ACCESSORS(SharedFunctionInfo, script, Object, kScriptOffset) ACCESSORS(SharedFunctionInfo, debug_info, Object, kDebugInfoOffset) ACCESSORS(SharedFunctionInfo, inferred_name, String, kInferredNameOffset) +ACCESSORS(SharedFunctionInfo, this_property_assignments, Object, + kThisPropertyAssignmentsOffset) BOOL_ACCESSORS(FunctionTemplateInfo, flag, hidden_prototype, kHiddenPrototypeBit) @@ -2308,6 +2329,13 @@ BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_expression, kIsExpressionBit) BOOL_ACCESSORS(SharedFunctionInfo, start_position_and_type, is_toplevel, kIsTopLevelBit) +BOOL_GETTER(SharedFunctionInfo, compiler_hints, + has_only_this_property_assignments, + kHasOnlyThisPropertyAssignments) +BOOL_GETTER(SharedFunctionInfo, compiler_hints, + has_only_simple_this_property_assignments, + kHasOnlySimpleThisPropertyAssignments) + INT_ACCESSORS(SharedFunctionInfo, length, kLengthOffset) INT_ACCESSORS(SharedFunctionInfo, formal_parameter_count, @@ -2319,6 +2347,10 @@ INT_ACCESSORS(SharedFunctionInfo, start_position_and_type, INT_ACCESSORS(SharedFunctionInfo, end_position, kEndPositionOffset) INT_ACCESSORS(SharedFunctionInfo, function_token_position, kFunctionTokenPositionOffset) +INT_ACCESSORS(SharedFunctionInfo, compiler_hints, + kCompilerHintsOffset) +INT_ACCESSORS(SharedFunctionInfo, this_property_assignments_count, + kThisPropertyAssignmentsCountOffset) void SharedFunctionInfo::DontAdaptArguments() { diff --git a/src/objects.cc b/src/objects.cc index c3051b811c..e4a3a67a57 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -4780,6 +4780,48 @@ Object* SharedFunctionInfo::GetSourceCode() { } +int SharedFunctionInfo::CalculateInstanceSize() { + int instance_size = + JSObject::kHeaderSize + + expected_nof_properties() * kPointerSize; + if (instance_size > JSObject::kMaxInstanceSize) { + instance_size = JSObject::kMaxInstanceSize; + } + return instance_size; +} + + +int SharedFunctionInfo::CalculateInObjectProperties() { + return (CalculateInstanceSize() - JSObject::kHeaderSize) / kPointerSize; +} + + +void SharedFunctionInfo::SetThisPropertyAssignmentsInfo( + bool only_this_property_assignments, + bool only_simple_this_property_assignments, + FixedArray* assignments) { + ASSERT(this_property_assignments()->IsUndefined()); + set_compiler_hints(BooleanBit::set(compiler_hints(), + kHasOnlyThisPropertyAssignments, + only_this_property_assignments)); + set_compiler_hints(BooleanBit::set(compiler_hints(), + kHasOnlySimpleThisPropertyAssignments, + only_simple_this_property_assignments)); + set_this_property_assignments(assignments); + set_this_property_assignments_count(assignments->length() / 3); +} + + +String* SharedFunctionInfo::GetThisPropertyAssignmentName(int index) { + Object* obj = this_property_assignments(); + ASSERT(obj->IsFixedArray()); + ASSERT(index < this_property_assignments_count()); + obj = FixedArray::cast(obj)->get(index * 3); + ASSERT(obj->IsString()); + return String::cast(obj); +} + + // Support function for printing the source code to a StringStream // without any allocation in the heap. void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator, @@ -4826,6 +4868,8 @@ void SharedFunctionInfo::SharedFunctionInfoIterateBody(ObjectVisitor* v) { IteratePointers(v, kNameOffset, kConstructStubOffset + kPointerSize); IteratePointers(v, kInstanceClassNameOffset, kScriptOffset + kPointerSize); IteratePointers(v, kDebugInfoOffset, kInferredNameOffset + kPointerSize); + IteratePointers(v, kThisPropertyAssignmentsOffset, + kThisPropertyAssignmentsOffset + kPointerSize); } diff --git a/src/objects.h b/src/objects.h index 03f0f3dd6b..a4029614e8 100644 --- a/src/objects.h +++ b/src/objects.h @@ -2704,6 +2704,10 @@ class Map: public HeapObject { inline int inobject_properties(); inline void set_inobject_properties(int value); + // Count of property fields pre-allocated in the object when first allocated. + inline int pre_allocated_property_fields(); + inline void set_pre_allocated_property_fields(int value); + // Instance type. inline InstanceType instance_type(); inline void set_instance_type(InstanceType value); @@ -2869,6 +2873,8 @@ class Map: public HeapObject { void MapVerify(); #endif + static const int kMaxPreAllocatedPropertyFields = 255; + // Layout description. static const int kInstanceSizesOffset = HeapObject::kHeaderSize; static const int kInstanceAttributesOffset = kInstanceSizesOffset + kIntSize; @@ -2882,7 +2888,8 @@ class Map: public HeapObject { // Byte offsets within kInstanceSizesOffset. static const int kInstanceSizeOffset = kInstanceSizesOffset + 0; static const int kInObjectPropertiesOffset = kInstanceSizesOffset + 1; - // The bytes at positions 2 and 3 are not in use at the moment. + static const int kPreAllocatedPropertyFieldsOffset = kInstanceSizesOffset + 2; + // The byte at position 3 is not in use at the moment. // Byte offsets within kInstanceAttributesOffset attributes. static const int kInstanceTypeOffset = kInstanceAttributesOffset + 0; @@ -3090,10 +3097,42 @@ class SharedFunctionInfo: public HeapObject { inline bool is_toplevel(); inline void set_is_toplevel(bool value); + // Bit field containing various information collected by the compiler to + // drive optimization. + inline int compiler_hints(); + inline void set_compiler_hints(int value); + + // Add information on assignments of the form this.x = ...; + void SetThisPropertyAssignmentsInfo( + bool has_only_this_property_assignments, + bool has_only_simple_this_property_assignments, + FixedArray* this_property_assignments); + + // Indicate that this function only consists of assignments of the form + // this.x = ...;. + inline bool has_only_this_property_assignments(); + + // Indicate that this function only consists of assignments of the form + // this.x = y; where y is either a constant or refers to an argument. + inline bool has_only_simple_this_property_assignments(); + + // For functions which only contains this property assignments this provides + // access to the names for the properties assigned. + DECL_ACCESSORS(this_property_assignments, Object) + inline int this_property_assignments_count(); + inline void set_this_property_assignments_count(int value); + String* GetThisPropertyAssignmentName(int index); + // [source code]: Source code for the function. bool HasSourceCode(); Object* GetSourceCode(); + // Calculate the instance size. + int CalculateInstanceSize(); + + // Calculate the number of in-object properties. + int CalculateInObjectProperties(); + // Dispatched behavior. void SharedFunctionInfoIterateBody(ObjectVisitor* v); // Set max_length to -1 for unlimited length. @@ -3129,7 +3168,12 @@ class SharedFunctionInfo: public HeapObject { static const int kScriptOffset = kExternalReferenceDataOffset + kPointerSize; static const int kDebugInfoOffset = kScriptOffset + kPointerSize; static const int kInferredNameOffset = kDebugInfoOffset + kPointerSize; - static const int kSize = kInferredNameOffset + kPointerSize; + static const int kCompilerHintsOffset = kInferredNameOffset + kPointerSize; + static const int kThisPropertyAssignmentsOffset = + kCompilerHintsOffset + kPointerSize; + static const int kThisPropertyAssignmentsCountOffset = + kThisPropertyAssignmentsOffset + kPointerSize; + static const int kSize = kThisPropertyAssignmentsCountOffset + kPointerSize; private: // Bit positions in length_and_flg. @@ -3146,6 +3190,10 @@ class SharedFunctionInfo: public HeapObject { static const int kStartPositionShift = 2; static const int kStartPositionMask = ~((1 << kStartPositionShift) - 1); + // Bit positions in compiler_hints. + static const int kHasOnlyThisPropertyAssignments = 0; + static const int kHasOnlySimpleThisPropertyAssignments = 1; + DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo); }; diff --git a/src/parser.cc b/src/parser.cc index e3b72a3848..5cc6341f9a 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -678,6 +678,25 @@ class TemporaryScope BASE_EMBEDDED { void set_contains_array_literal() { contains_array_literal_ = true; } bool contains_array_literal() { return contains_array_literal_; } + void SetThisPropertyAssignmentInfo( + bool only_this_property_assignments, + bool only_simple_this_property_assignments, + Handle this_property_assignments) { + only_this_property_assignments_ = only_this_property_assignments; + only_simple_this_property_assignments_ = + only_simple_this_property_assignments; + this_property_assignments_ = this_property_assignments; + } + bool only_this_property_assignments() { + return only_this_property_assignments_; + } + bool only_simple_this_property_assignments() { + return only_simple_this_property_assignments_; + } + Handle this_property_assignments() { + return this_property_assignments_; + } + void AddProperty() { expected_property_count_++; } int expected_property_count() { return expected_property_count_; } private: @@ -695,6 +714,10 @@ class TemporaryScope BASE_EMBEDDED { // Properties count estimation. int expected_property_count_; + bool only_this_property_assignments_; + bool only_simple_this_property_assignments_; + Handle this_property_assignments_; + // Bookkeeping Parser* parser_; TemporaryScope* parent_; @@ -707,6 +730,9 @@ TemporaryScope::TemporaryScope(Parser* parser) : materialized_literal_count_(0), contains_array_literal_(false), expected_property_count_(0), + only_this_property_assignments_(false), + only_simple_this_property_assignments_(false), + this_property_assignments_(Factory::empty_fixed_array()), parser_(parser), parent_(parser->temp_scope_) { parser->temp_scope_ = this; @@ -1218,12 +1244,20 @@ FunctionLiteral* Parser::ParseProgram(Handle source, bool ok = true; ParseSourceElements(&body, Token::EOS, &ok); if (ok) { - result = NEW(FunctionLiteral(no_name, top_scope_, - body.elements(), - temp_scope.materialized_literal_count(), - temp_scope.contains_array_literal(), - temp_scope.expected_property_count(), - 0, 0, source->length(), false)); + result = NEW(FunctionLiteral( + no_name, + top_scope_, + body.elements(), + temp_scope.materialized_literal_count(), + temp_scope.contains_array_literal(), + temp_scope.expected_property_count(), + temp_scope.only_this_property_assignments(), + temp_scope.only_simple_this_property_assignments(), + temp_scope.this_property_assignments(), + 0, + 0, + source->length(), + false)); } else if (scanner().stack_overflow()) { Top::StackOverflow(); } @@ -1314,9 +1348,23 @@ void PreParser::ReportMessageAt(Scanner::Location source_location, } +// Base class containing common code for the different finder classes used by +// the parser. +class ParserFinder { + protected: + ParserFinder() {} + static Assignment* AsAssignment(Statement* stat) { + if (stat == NULL) return NULL; + ExpressionStatement* exp_stat = stat->AsExpressionStatement(); + if (exp_stat == NULL) return NULL; + return exp_stat->expression()->AsAssignment(); + } +}; + + // An InitializationBlockFinder finds and marks sequences of statements of the // form x.y.z.a = ...; x.y.z.b = ...; etc. -class InitializationBlockFinder { +class InitializationBlockFinder : public ParserFinder { public: InitializationBlockFinder() : first_in_block_(NULL), last_in_block_(NULL), block_size_(0) {} @@ -1341,13 +1389,6 @@ class InitializationBlockFinder { } private: - static Assignment* AsAssignment(Statement* stat) { - if (stat == NULL) return NULL; - ExpressionStatement* exp_stat = stat->AsExpressionStatement(); - if (exp_stat == NULL) return NULL; - return exp_stat->expression()->AsAssignment(); - } - // Returns true if the expressions appear to denote the same object. // In the context of initialization blocks, we only consider expressions // of the form 'x.y.z'. @@ -1418,6 +1459,161 @@ class InitializationBlockFinder { }; +// A ThisNamedPropertyAssigmentFinder finds and marks statements of the form +// this.x = ...;, where x is a named property. It also determines whether a +// function contains only assignments of this type. +class ThisNamedPropertyAssigmentFinder : public ParserFinder { + public: + ThisNamedPropertyAssigmentFinder() + : only_this_property_assignments_(true), + only_simple_this_property_assignments_(true), + names_(NULL), + assigned_arguments_(NULL), + assigned_constants_(NULL) {} + + void Update(Scope* scope, Statement* stat) { + // Bail out if function already has non this property assignment + // statements. + if (!only_this_property_assignments_) { + return; + } + + // Check whether this statement is of the form this.x = ...; + Assignment* assignment = AsAssignment(stat); + if (IsThisPropertyAssignment(assignment)) { + HandleThisPropertyAssignment(scope, assignment); + } else { + only_this_property_assignments_ = false; + only_simple_this_property_assignments_ = false; + } + } + + // Returns whether only statements of the form this.x = ...; was encountered. + bool only_this_property_assignments() { + return only_this_property_assignments_; + } + + // Returns whether only statements of the form this.x = y; where y is either a + // constant or a function argument was encountered. + bool only_simple_this_property_assignments() { + return only_simple_this_property_assignments_; + } + + // Returns a fixed array containing three elements for each assignment of the + // form this.x = y; + Handle GetThisPropertyAssignments() { + if (names_ == NULL) { + return Factory::empty_fixed_array(); + } + ASSERT(names_ != NULL); + ASSERT(assigned_arguments_ != NULL); + ASSERT_EQ(names_->length(), assigned_arguments_->length()); + ASSERT_EQ(names_->length(), assigned_constants_->length()); + Handle assignments = + Factory::NewFixedArray(names_->length() * 3); + for (int i = 0; i < names_->length(); i++) { + assignments->set(i * 3, *names_->at(i)); + assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_->at(i))); + assignments->set(i * 3 + 2, *assigned_constants_->at(i)); + } + return assignments; + } + + private: + bool IsThisPropertyAssignment(Assignment* assignment) { + if (assignment != NULL) { + Property* property = assignment->target()->AsProperty(); + return assignment->op() == Token::ASSIGN + && property != NULL + && property->obj()->AsVariableProxy() != NULL + && property->obj()->AsVariableProxy()->is_this(); + } + return false; + } + + void HandleThisPropertyAssignment(Scope* scope, Assignment* assignment) { + // Check that the property assigned to is a named property. + Property* property = assignment->target()->AsProperty(); + ASSERT(property != NULL); + Literal* literal = property->key()->AsLiteral(); + uint32_t dummy; + if (literal != NULL && + literal->handle()->IsString() && + !String::cast(*(literal->handle()))->AsArrayIndex(&dummy)) { + Handle key = Handle::cast(literal->handle()); + + // Check whether the value assigned is either a constant or matches the + // name of one of the arguments to the function. + if (assignment->value()->AsLiteral() != NULL) { + // Constant assigned. + Literal* literal = assignment->value()->AsLiteral(); + AssignmentFromConstant(key, literal->handle()); + } else if (assignment->value()->AsVariableProxy() != NULL) { + // Variable assigned. + Handle name = + assignment->value()->AsVariableProxy()->name(); + // Check whether the variable assigned matches an argument name. + int index = -1; + for (int i = 0; i < scope->num_parameters(); i++) { + if (*scope->parameter(i)->name() == *name) { + // Assigned from function argument. + index = i; + break; + } + } + if (index != -1) { + AssignmentFromParameter(key, index); + } else { + AssignmentFromSomethingElse(key); + } + } else { + AssignmentFromSomethingElse(key); + } + } + } + + void AssignmentFromParameter(Handle name, int index) { + EnsureAllocation(); + names_->Add(name); + assigned_arguments_->Add(index); + assigned_constants_->Add(Factory::undefined_value()); + } + + void AssignmentFromConstant(Handle name, Handle value) { + EnsureAllocation(); + names_->Add(name); + assigned_arguments_->Add(-1); + assigned_constants_->Add(value); + } + + void AssignmentFromSomethingElse(Handle name) { + EnsureAllocation(); + names_->Add(name); + assigned_arguments_->Add(-1); + assigned_constants_->Add(Factory::undefined_value()); + + // The this assignment is not a simple one. + only_simple_this_property_assignments_ = false; + } + + void EnsureAllocation() { + if (names_ == NULL) { + ASSERT(assigned_arguments_ == NULL); + ASSERT(assigned_constants_ == NULL); + names_ = new ZoneStringList(4); + assigned_arguments_ = new ZoneList(4); + assigned_constants_ = new ZoneObjectList(4); + } + } + + bool only_this_property_assignments_; + bool only_simple_this_property_assignments_; + ZoneStringList* names_; + ZoneList* assigned_arguments_; + ZoneObjectList* assigned_constants_; +}; + + void* Parser::ParseSourceElements(ZoneListWrapper* processor, int end_token, bool* ok) { @@ -1432,15 +1628,33 @@ void* Parser::ParseSourceElements(ZoneListWrapper* processor, ASSERT(processor != NULL); InitializationBlockFinder block_finder; + ThisNamedPropertyAssigmentFinder this_property_assignment_finder; while (peek() != end_token) { Statement* stat = ParseStatement(NULL, CHECK_OK); if (stat == NULL || stat->IsEmpty()) continue; // We find and mark the initialization blocks on top level code only. // This is because the optimization prevents reuse of the map transitions, // so it should be used only for code that will only be run once. - if (top_scope_->is_global_scope()) block_finder.Update(stat); + if (top_scope_->is_global_scope()) { + block_finder.Update(stat); + } + // Find and mark all assignments to named properties in this (this.x =) + if (top_scope_->is_function_scope()) { + this_property_assignment_finder.Update(top_scope_, stat); + } processor->Add(stat); } + + // Propagate the collected information on this property assignments. + if (top_scope_->is_function_scope()) { + if (this_property_assignment_finder.only_this_property_assignments()) { + temp_scope_->SetThisPropertyAssignmentInfo( + this_property_assignment_finder.only_this_property_assignments(), + this_property_assignment_finder. + only_simple_this_property_assignments(), + this_property_assignment_finder.GetThisPropertyAssignments()); + } + } return 0; } @@ -3507,6 +3721,9 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle var_name, int materialized_literal_count; int expected_property_count; bool contains_array_literal; + bool only_this_property_assignments; + bool only_simple_this_property_assignments; + Handle this_property_assignments; if (is_lazily_compiled && pre_data() != NULL) { FunctionEntry entry = pre_data()->GetFunctionEnd(start_pos); int end_pos = entry.end_pos(); @@ -3514,12 +3731,20 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle var_name, scanner_.SeekForward(end_pos); materialized_literal_count = entry.literal_count(); expected_property_count = entry.property_count(); + only_this_property_assignments = false; + only_simple_this_property_assignments = false; + this_property_assignments = Factory::empty_fixed_array(); contains_array_literal = entry.contains_array_literal(); } else { ParseSourceElements(&body, Token::RBRACE, CHECK_OK); materialized_literal_count = temp_scope.materialized_literal_count(); expected_property_count = temp_scope.expected_property_count(); contains_array_literal = temp_scope.contains_array_literal(); + only_this_property_assignments = + temp_scope.only_this_property_assignments(); + only_simple_this_property_assignments = + temp_scope.only_simple_this_property_assignments(); + this_property_assignments = temp_scope.this_property_assignments(); } Expect(Token::RBRACE, CHECK_OK); @@ -3534,10 +3759,18 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle var_name, } FunctionLiteral* function_literal = - NEW(FunctionLiteral(name, top_scope_, - body.elements(), materialized_literal_count, - contains_array_literal, expected_property_count, - num_parameters, start_pos, end_pos, + NEW(FunctionLiteral(name, + top_scope_, + body.elements(), + materialized_literal_count, + contains_array_literal, + expected_property_count, + only_this_property_assignments, + only_simple_this_property_assignments, + this_property_assignments, + num_parameters, + start_pos, + end_pos, function_name->length() > 0)); if (!is_pre_parsing_) { function_literal->set_function_token_position(function_token_position); diff --git a/src/runtime.cc b/src/runtime.cc index 36fdd65000..b3e8aa477d 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -4380,6 +4380,8 @@ static Object* Runtime_NewObject(Arguments args) { Handle stub = ComputeConstructStub(map); function->shared()->set_construct_stub(*stub); } + Counters::constructed_objects.Increment(); + Counters::constructed_objects_runtime.Increment(); return *result; } diff --git a/src/v8-counters.h b/src/v8-counters.h index a62cd74465..43cd5e3d60 100644 --- a/src/v8-counters.h +++ b/src/v8-counters.h @@ -139,6 +139,8 @@ namespace internal { SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \ SC(call_global_inline, V8.CallGlobalInline) \ SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \ + SC(constructed_objects, V8.ConstructedObjects) \ + SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \ SC(for_in, V8.ForIn) \ SC(enum_cache_hits, V8.EnumCacheHits) \ SC(enum_cache_misses, V8.EnumCacheMisses) \ diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 823be33564..98b456df22 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -585,12 +585,16 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { // rax: initial map // rbx: JSObject // rdi: start of next object + // Calculate total properties described map. __ movzxbq(rdx, FieldOperand(rax, Map::kUnusedPropertyFieldsOffset)); - __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset)); + __ movzxbq(rcx, FieldOperand(rax, Map::kPreAllocatedPropertyFieldsOffset)); + __ addq(rdx, rcx); // Calculate unused properties past the end of the in-object properties. + __ movzxbq(rcx, FieldOperand(rax, Map::kInObjectPropertiesOffset)); __ subq(rdx, rcx); // Done if no extra properties are to be allocated. __ j(zero, &allocated); + __ Assert(positive, "Property allocation count failed."); // Scale the number of elements by pointer size and add the header for // FixedArrays to the start of the next object calculation from above. @@ -726,6 +730,7 @@ void Builtins::Generate_JSConstructStubGeneric(MacroAssembler* masm) { __ pop(rcx); __ lea(rsp, Operand(rsp, rbx, times_4, 1 * kPointerSize)); // 1 ~ receiver __ push(rcx); + __ IncrementCounter(&Counters::constructed_objects, 1); __ ret(0); } diff --git a/test/mjsunit/simple-constructor.js b/test/mjsunit/simple-constructor.js new file mode 100755 index 0000000000..b26d6519b0 --- /dev/null +++ b/test/mjsunit/simple-constructor.js @@ -0,0 +1,78 @@ +// Copyright 2008 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +function props(x) { + var array = []; + for (var p in x) array.push(p); + return array.sort(); +} + +function f1() { + this.x = 1; +} + +function f2(x) { + this.x = x; +} + +function f3(x) { + this.x = x; + this.y = 1; + this.z = f1; +} + +function f4(x) { + this.x = x; + this.y = 1; + if (x == 1) return; + this.z = f1; +} + +o1_1 = new f1(); +o1_2 = new f1(); +assertArrayEquals(["x"], props(o1_1)); +assertArrayEquals(["x"], props(o1_2)); + +o2_1 = new f2(0); +o2_2 = new f2(0); +assertArrayEquals(["x"], props(o2_1)); +assertArrayEquals(["x"], props(o2_2)); + +o3_1 = new f3(0); +o3_2 = new f3(0); +assertArrayEquals(["x", "y", "z"], props(o3_1)); +assertArrayEquals(["x", "y", "z"], props(o3_2)); + +o4_0_1 = new f4(0); +o4_0_2 = new f4(0); +assertArrayEquals(["x", "y", "z"], props(o4_0_1)); +assertArrayEquals(["x", "y", "z"], props(o4_0_2)); + +o4_1_1 = new f4(1); +o4_1_2 = new f4(1); +assertArrayEquals(["x", "y"], props(o4_1_1)); +assertArrayEquals(["x", "y"], props(o4_1_2));