From 8066271fd2d4104e353e199b9427377e9509da0c Mon Sep 17 00:00:00 2001 From: "fschneider@chromium.org" Date: Mon, 21 Mar 2011 12:25:31 +0000 Subject: [PATCH] Optimize calls to object literal properties that are initialized with a function literal. This allows fast calls and inlining of functions like: var o = {f: function() { return "foo"; }} o.f(); Object literals that contain function literals are initially created a dictionary mode object and only transformed to fast properties once all properties are computed and added. This allows us to create constant function properties for functions declared inside the object literal. Function literals inside object literals are marked for pretenuring so that they work as contant function properties. Object literals without functions should just function as before. Review URL: http://codereview.chromium.org/6240012 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7283 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/full-codegen-arm.cc | 15 +++- src/arm/lithium-arm.cc | 7 ++ src/arm/lithium-arm.h | 12 +++ src/arm/lithium-codegen-arm.cc | 7 ++ src/ast.h | 14 +++- src/handles.cc | 30 -------- src/handles.h | 19 ----- src/hydrogen-instructions.h | 27 ++++++- src/hydrogen.cc | 17 ++++- src/ia32/full-codegen-ia32.cc | 14 +++- src/ia32/lithium-codegen-ia32.cc | 15 +++- src/ia32/lithium-ia32.cc | 7 ++ src/ia32/lithium-ia32.h | 12 +++ src/parser.cc | 11 ++- src/runtime.cc | 154 ++++++++++++++++++++++++--------------- src/x64/full-codegen-x64.cc | 14 +++- src/x64/lithium-codegen-x64.cc | 7 ++ src/x64/lithium-x64.cc | 7 ++ src/x64/lithium-x64.h | 12 +++ 19 files changed, 281 insertions(+), 120 deletions(-) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index a53a4eb..c308e3b 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -1397,7 +1397,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ ldr(r3, FieldMemOperand(r3, JSFunction::kLiteralsOffset)); __ mov(r2, Operand(Smi::FromInt(expr->literal_index()))); __ mov(r1, Operand(expr->constant_properties())); - __ mov(r0, Operand(Smi::FromInt(expr->fast_elements() ? 1 : 0))); + int flags = expr->fast_elements() + ? ObjectLiteral::kFastElements + : ObjectLiteral::kNoFlags; + flags |= expr->has_function() + ? ObjectLiteral::kHasFunction + : ObjectLiteral::kNoFlags; + __ mov(r0, Operand(Smi::FromInt(flags))); __ Push(r3, r2, r1, r0); if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); @@ -1476,6 +1482,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { } } + if (expr->has_function()) { + ASSERT(result_saved); + __ ldr(r0, MemOperand(sp)); + __ push(r0); + __ CallRuntime(Runtime::kToFastProperties, 1); + } + if (result_saved) { context()->PlugTOS(); } else { diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 4244cf5..153e795 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -1996,6 +1996,13 @@ LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { } +LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) { + LOperand* object = UseFixed(instr->value(), r0); + LToFastProperties* result = new LToFastProperties(object); + return MarkAsCall(DefineFixed(result, r0), instr); +} + + LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { LTypeof* result = new LTypeof(UseFixed(instr->value(), r0)); return MarkAsCall(DefineFixed(result, r0), instr); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index e865286..18b70e1 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -155,6 +155,7 @@ class LCodeGen; V(SubI) \ V(TaggedToI) \ V(Throw) \ + V(ToFastProperties) \ V(Typeof) \ V(TypeofIs) \ V(TypeofIsAndBranch) \ @@ -1767,6 +1768,17 @@ class LFunctionLiteral: public LTemplateInstruction<1, 0, 0> { }; +class LToFastProperties: public LTemplateInstruction<1, 1, 0> { + public: + explicit LToFastProperties(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties") + DECLARE_HYDROGEN_ACCESSOR(ToFastProperties) +}; + + class LTypeof: public LTemplateInstruction<1, 1, 0> { public: explicit LTypeof(LOperand* value) { diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index cb18a8d..6a16067 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -3674,6 +3674,13 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { } +void LCodeGen::DoToFastProperties(LToFastProperties* instr) { + ASSERT(ToRegister(instr->InputAt(0)).is(r0)); + __ push(r0); + CallRuntime(Runtime::kToFastProperties, 1, instr); +} + + void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) { Label materialized; // Registers will be used as follows: diff --git a/src/ast.h b/src/ast.h index ea36507..e9a06ec 100644 --- a/src/ast.h +++ b/src/ast.h @@ -985,11 +985,13 @@ class ObjectLiteral: public MaterializedLiteral { int literal_index, bool is_simple, bool fast_elements, - int depth) + int depth, + bool has_function) : MaterializedLiteral(literal_index, is_simple, depth), constant_properties_(constant_properties), properties_(properties), - fast_elements_(fast_elements) {} + fast_elements_(fast_elements), + has_function_(has_function) {} DECLARE_NODE_TYPE(ObjectLiteral) @@ -1000,16 +1002,24 @@ class ObjectLiteral: public MaterializedLiteral { bool fast_elements() const { return fast_elements_; } + bool has_function() { return has_function_; } // Mark all computed expressions that are bound to a key that // is shadowed by a later occurrence of the same key. For the // marked expressions, no store code is emitted. void CalculateEmitStore(); + enum Flags { + kNoFlags = 0, + kFastElements = 1, + kHasFunction = 1 << 1 + }; + private: Handle constant_properties_; ZoneList* properties_; bool fast_elements_; + bool has_function_; }; diff --git a/src/handles.cc b/src/handles.cc index 90ee560..bcd79b0 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -958,34 +958,4 @@ bool CompileOptimized(Handle function, return CompileLazyHelper(&info, flag); } - -OptimizedObjectForAddingMultipleProperties:: -OptimizedObjectForAddingMultipleProperties(Handle object, - int expected_additional_properties, - bool condition) { - object_ = object; - if (condition && object_->HasFastProperties() && !object->IsJSGlobalProxy()) { - // Normalize the properties of object to avoid n^2 behavior - // when extending the object multiple properties. Indicate the number of - // properties to be added. - unused_property_fields_ = object->map()->unused_property_fields(); - NormalizeProperties(object_, - KEEP_INOBJECT_PROPERTIES, - expected_additional_properties); - has_been_transformed_ = true; - - } else { - has_been_transformed_ = false; - } -} - - -OptimizedObjectForAddingMultipleProperties:: -~OptimizedObjectForAddingMultipleProperties() { - // Reoptimize the object to allow fast property access. - if (has_been_transformed_) { - TransformToFastProperties(object_, unused_property_fields_); - } -} - } } // namespace v8::internal diff --git a/src/handles.h b/src/handles.h index 53a51ce..a357a00 100644 --- a/src/handles.h +++ b/src/handles.h @@ -367,25 +367,6 @@ class NoHandleAllocation BASE_EMBEDDED { #endif }; - -// ---------------------------------------------------------------------------- - - -// Stack allocated wrapper call for optimizing adding multiple -// properties to an object. -class OptimizedObjectForAddingMultipleProperties BASE_EMBEDDED { - public: - OptimizedObjectForAddingMultipleProperties(Handle object, - int expected_property_count, - bool condition = true); - ~OptimizedObjectForAddingMultipleProperties(); - private: - bool has_been_transformed_; // Tells whether the object has been transformed. - int unused_property_fields_; // Captures the unused number of field. - Handle object_; // The object being optimized. -}; - - } } // namespace v8::internal #endif // V8_HANDLES_H_ diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 2397c5d..3ca76f7 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -156,6 +156,7 @@ class LChunkBuilder; V(Sub) \ V(Test) \ V(Throw) \ + V(ToFastProperties) \ V(Typeof) \ V(TypeofIs) \ V(UnaryMathOperation) \ @@ -3382,10 +3383,12 @@ class HObjectLiteral: public HMaterializedLiteral<1> { Handle constant_properties, bool fast_elements, int literal_index, - int depth) + int depth, + bool has_function) : HMaterializedLiteral<1>(literal_index, depth), constant_properties_(constant_properties), - fast_elements_(fast_elements) { + fast_elements_(fast_elements), + has_function_(has_function) { SetOperandAt(0, context); } @@ -3394,6 +3397,7 @@ class HObjectLiteral: public HMaterializedLiteral<1> { return constant_properties_; } bool fast_elements() const { return fast_elements_; } + bool has_function() const { return has_function_; } virtual Representation RequiredInputRepresentation(int index) const { return Representation::Tagged(); @@ -3404,6 +3408,7 @@ class HObjectLiteral: public HMaterializedLiteral<1> { private: Handle constant_properties_; bool fast_elements_; + bool has_function_; }; @@ -3467,6 +3472,24 @@ class HTypeof: public HUnaryOperation { }; +class HToFastProperties: public HUnaryOperation { + public: + explicit HToFastProperties(HValue* value) : HUnaryOperation(value) { + // This instruction is not marked as having side effects, but + // changes the map of the input operand. Use it only when creating + // object literals. + ASSERT(value->IsObjectLiteral()); + set_representation(Representation::Tagged()); + } + + virtual Representation RequiredInputRepresentation(int index) const { + return Representation::Tagged(); + } + + DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to_fast_properties") +}; + + class HValueOf: public HUnaryOperation { public: explicit HValueOf(HValue* value) : HUnaryOperation(value) { diff --git a/src/hydrogen.cc b/src/hydrogen.cc index f07a686..e9bfafd 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -2905,7 +2905,8 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { expr->constant_properties(), expr->fast_elements(), expr->literal_index(), - expr->depth())); + expr->depth(), + expr->has_function())); // The object is expected in the bailout environment during computation // of the property values and is the value of the entire expression. PushAndAdd(literal); @@ -2946,7 +2947,19 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { default: UNREACHABLE(); } } - ast_context()->ReturnValue(Pop()); + + if (expr->has_function()) { + // Return the result of the transformation to fast properties + // instead of the original since this operation changes the map + // of the object. This makes sure that the original object won't + // be used by other optimized code before it is transformed + // (e.g. because of code motion). + HToFastProperties* result = new HToFastProperties(Pop()); + AddInstruction(result); + ast_context()->ReturnValue(result); + } else { + ast_context()->ReturnValue(Pop()); + } } diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index 0516581..2e2890d 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -1343,7 +1343,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ push(FieldOperand(edi, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(expr->literal_index()))); __ push(Immediate(expr->constant_properties())); - __ push(Immediate(Smi::FromInt(expr->fast_elements() ? 1 : 0))); + int flags = expr->fast_elements() + ? ObjectLiteral::kFastElements + : ObjectLiteral::kNoFlags; + flags |= expr->has_function() + ? ObjectLiteral::kHasFunction + : ObjectLiteral::kNoFlags; + __ push(Immediate(Smi::FromInt(flags))); if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); } else { @@ -1415,6 +1421,12 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { } } + if (expr->has_function()) { + ASSERT(result_saved); + __ push(Operand(esp, 0)); + __ CallRuntime(Runtime::kToFastProperties, 1); + } + if (result_saved) { context()->PlugTOS(); } else { diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 4bb79e4..df2a8b3 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -3676,7 +3676,13 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { __ push(FieldOperand(eax, JSFunction::kLiteralsOffset)); __ push(Immediate(Smi::FromInt(instr->hydrogen()->literal_index()))); __ push(Immediate(instr->hydrogen()->constant_properties())); - __ push(Immediate(Smi::FromInt(instr->hydrogen()->fast_elements() ? 1 : 0))); + int flags = instr->hydrogen()->fast_elements() + ? ObjectLiteral::kFastElements + : ObjectLiteral::kNoFlags; + flags |= instr->hydrogen()->has_function() + ? ObjectLiteral::kHasFunction + : ObjectLiteral::kNoFlags; + __ push(Immediate(Smi::FromInt(flags))); // Pick the right runtime function to call. if (instr->hydrogen()->depth() > 1) { @@ -3687,6 +3693,13 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { } +void LCodeGen::DoToFastProperties(LToFastProperties* instr) { + ASSERT(ToRegister(instr->InputAt(0)).is(eax)); + __ push(eax); + CallRuntime(Runtime::kToFastProperties, 1, instr); +} + + void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) { NearLabel materialized; // Registers will be used as follows: diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 1f3ded4..96b7937 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -2030,6 +2030,13 @@ LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { } +LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) { + LOperand* object = UseFixed(instr->value(), eax); + LToFastProperties* result = new LToFastProperties(object); + return MarkAsCall(DefineFixed(result, eax), instr); +} + + LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { LTypeof* result = new LTypeof(UseAtStart(instr->value())); return MarkAsCall(DefineFixed(result, eax), instr); diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index 0c0dd5f..70ec962 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -157,6 +157,7 @@ class LCodeGen; V(SubI) \ V(TaggedToI) \ V(Throw) \ + V(ToFastProperties) \ V(Typeof) \ V(TypeofIs) \ V(TypeofIsAndBranch) \ @@ -1847,6 +1848,17 @@ class LFunctionLiteral: public LTemplateInstruction<1, 0, 0> { }; +class LToFastProperties: public LTemplateInstruction<1, 1, 0> { + public: + explicit LToFastProperties(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties") + DECLARE_HYDROGEN_ACCESSOR(ToFastProperties) +}; + + class LTypeof: public LTemplateInstruction<1, 1, 0> { public: explicit LTypeof(LOperand* value) { diff --git a/src/parser.cc b/src/parser.cc index 19dd24e..46d49f3 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -3340,6 +3340,7 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { ZoneList* properties = new ZoneList(4); int number_of_boilerplate_properties = 0; + bool has_function = false; ObjectLiteralPropertyChecker checker(this, top_scope_->is_strict_mode()); @@ -3428,6 +3429,13 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { ObjectLiteral::Property* property = new ObjectLiteral::Property(key, value); + // Mark object literals that contain function literals and pretenure the + // literal so it can be added as a constant function property. + if (value->AsFunctionLiteral() != NULL) { + has_function = true; + value->AsFunctionLiteral()->set_pretenure(true); + } + // Count CONSTANT or COMPUTED properties to maintain the enumeration order. if (IsBoilerplateProperty(property)) number_of_boilerplate_properties++; // Validate the property @@ -3463,7 +3471,8 @@ Expression* Parser::ParseObjectLiteral(bool* ok) { literal_index, is_simple, fast_elements, - depth); + depth, + has_function); } diff --git a/src/runtime.cc b/src/runtime.cc index 93445ec..363d799 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -304,7 +304,8 @@ static Handle CreateObjectLiteralBoilerplate( Isolate* isolate, Handle literals, Handle constant_properties, - bool should_have_fast_elements) { + bool should_have_fast_elements, + bool has_function_literal) { // Get the global context from the literals array. This is the // context in which the function was created and we use the object // function from this context to create the object literal. We do @@ -314,70 +315,90 @@ static Handle CreateObjectLiteralBoilerplate( Handle context = Handle(JSFunction::GlobalContextFromLiterals(*literals)); - bool is_result_from_cache; - Handle map = ComputeObjectLiteralMap(context, - constant_properties, - &is_result_from_cache); + // In case we have function literals, we want the object to be in + // slow properties mode for now. We don't go in the map cache because + // maps with constant functions can't be shared if the functions are + // not the same (which is the common case). + bool is_result_from_cache = false; + Handle map = has_function_literal + ? Handle(context->object_function()->initial_map()) + : ComputeObjectLiteralMap(context, + constant_properties, + &is_result_from_cache); Handle boilerplate = isolate->factory()->NewJSObjectFromMap(map); // Normalize the elements of the boilerplate to save space if needed. if (!should_have_fast_elements) NormalizeElements(boilerplate); - { // Add the constant properties to the boilerplate. - int length = constant_properties->length(); - OptimizedObjectForAddingMultipleProperties opt(boilerplate, - length / 2, - !is_result_from_cache); - for (int index = 0; index < length; index +=2) { - Handle key(constant_properties->get(index+0), isolate); - Handle value(constant_properties->get(index+1), isolate); - if (value->IsFixedArray()) { - // The value contains the constant_properties of a - // simple object literal. - Handle array = Handle::cast(value); - value = CreateLiteralBoilerplate(isolate, literals, array); - if (value.is_null()) return value; - } - Handle result; - uint32_t element_index = 0; - if (key->IsSymbol()) { - if (Handle::cast(key)->AsArrayIndex(&element_index)) { - // Array index as string (uint32). - result = SetOwnElement(boilerplate, - element_index, - value, - kNonStrictMode); - } else { - Handle name(String::cast(*key)); - ASSERT(!name->AsArrayIndex(&element_index)); - result = SetLocalPropertyIgnoreAttributes(boilerplate, name, - value, NONE); - } - } else if (key->ToArrayIndex(&element_index)) { - // Array index (uint32). + // Add the constant properties to the boilerplate. + int length = constant_properties->length(); + bool should_transform = + !is_result_from_cache && boilerplate->HasFastProperties(); + if (should_transform || has_function_literal) { + // Normalize the properties of object to avoid n^2 behavior + // when extending the object multiple properties. Indicate the number of + // properties to be added. + NormalizeProperties(boilerplate, KEEP_INOBJECT_PROPERTIES, length / 2); + } + + for (int index = 0; index < length; index +=2) { + Handle key(constant_properties->get(index+0), isolate); + Handle value(constant_properties->get(index+1), isolate); + if (value->IsFixedArray()) { + // The value contains the constant_properties of a + // simple object or array literal. + Handle array = Handle::cast(value); + value = CreateLiteralBoilerplate(isolate, literals, array); + if (value.is_null()) return value; + } + Handle result; + uint32_t element_index = 0; + if (key->IsSymbol()) { + if (Handle::cast(key)->AsArrayIndex(&element_index)) { + // Array index as string (uint32). result = SetOwnElement(boilerplate, element_index, value, kNonStrictMode); } else { - // Non-uint32 number. - ASSERT(key->IsNumber()); - double num = key->Number(); - char arr[100]; - Vector buffer(arr, ARRAY_SIZE(arr)); - const char* str = DoubleToCString(num, buffer); - Handle name = - isolate->factory()->NewStringFromAscii(CStrVector(str)); + Handle name(String::cast(*key)); + ASSERT(!name->AsArrayIndex(&element_index)); result = SetLocalPropertyIgnoreAttributes(boilerplate, name, value, NONE); } - // If setting the property on the boilerplate throws an - // exception, the exception is converted to an empty handle in - // the handle based operations. In that case, we need to - // convert back to an exception. - if (result.is_null()) return result; - } + } else if (key->ToArrayIndex(&element_index)) { + // Array index (uint32). + result = SetOwnElement(boilerplate, + element_index, + value, + kNonStrictMode); + } else { + // Non-uint32 number. + ASSERT(key->IsNumber()); + double num = key->Number(); + char arr[100]; + Vector buffer(arr, ARRAY_SIZE(arr)); + const char* str = DoubleToCString(num, buffer); + Handle name = + isolate->factory()->NewStringFromAscii(CStrVector(str)); + result = SetLocalPropertyIgnoreAttributes(boilerplate, name, + value, NONE); + } + // If setting the property on the boilerplate throws an + // exception, the exception is converted to an empty handle in + // the handle based operations. In that case, we need to + // convert back to an exception. + if (result.is_null()) return result; + } + + // Transform to fast properties if necessary. For object literals with + // containing function literals we defer this operation until after all + // computed properties have been assigned so that we can generate + // constant function properties. + if (should_transform && !has_function_literal) { + TransformToFastProperties(boilerplate, + boilerplate->map()->unused_property_fields()); } return boilerplate; @@ -410,7 +431,7 @@ static Handle CreateArrayLiteralBoilerplate( for (int i = 0; i < content->length(); i++) { if (content->get(i)->IsFixedArray()) { // The value contains the constant_properties of a - // simple object literal. + // simple object or array literal. Handle fa(FixedArray::cast(content->get(i))); Handle result = CreateLiteralBoilerplate(isolate, literals, fa); @@ -431,11 +452,20 @@ static Handle CreateLiteralBoilerplate( Handle literals, Handle array) { Handle elements = CompileTimeValue::GetElements(array); + const bool kHasNoFunctionLiteral = false; switch (CompileTimeValue::GetType(array)) { case CompileTimeValue::OBJECT_LITERAL_FAST_ELEMENTS: - return CreateObjectLiteralBoilerplate(isolate, literals, elements, true); + return CreateObjectLiteralBoilerplate(isolate, + literals, + elements, + true, + kHasNoFunctionLiteral); case CompileTimeValue::OBJECT_LITERAL_SLOW_ELEMENTS: - return CreateObjectLiteralBoilerplate(isolate, literals, elements, false); + return CreateObjectLiteralBoilerplate(isolate, + literals, + elements, + false, + kHasNoFunctionLiteral); case CompileTimeValue::ARRAY_LITERAL: return CreateArrayLiteralBoilerplate(isolate, literals, elements); default: @@ -476,8 +506,9 @@ static MaybeObject* Runtime_CreateObjectLiteral(RUNTIME_CALLING_CONVENTION) { CONVERT_ARG_CHECKED(FixedArray, literals, 0); CONVERT_SMI_CHECKED(literals_index, args[1]); CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2); - CONVERT_SMI_CHECKED(fast_elements, args[3]); - bool should_have_fast_elements = fast_elements == 1; + CONVERT_SMI_CHECKED(flags, args[3]); + bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0; + bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0; // Check if boilerplate exists. If not, create it first. Handle boilerplate(literals->get(literals_index), isolate); @@ -485,7 +516,8 @@ static MaybeObject* Runtime_CreateObjectLiteral(RUNTIME_CALLING_CONVENTION) { boilerplate = CreateObjectLiteralBoilerplate(isolate, literals, constant_properties, - should_have_fast_elements); + should_have_fast_elements, + has_function_literal); if (boilerplate.is_null()) return Failure::Exception(); // Update the functions literal and return the boilerplate. literals->set(literals_index, *boilerplate); @@ -502,8 +534,9 @@ static MaybeObject* Runtime_CreateObjectLiteralShallow( CONVERT_ARG_CHECKED(FixedArray, literals, 0); CONVERT_SMI_CHECKED(literals_index, args[1]); CONVERT_ARG_CHECKED(FixedArray, constant_properties, 2); - CONVERT_SMI_CHECKED(fast_elements, args[3]); - bool should_have_fast_elements = fast_elements == 1; + CONVERT_SMI_CHECKED(flags, args[3]); + bool should_have_fast_elements = (flags & ObjectLiteral::kFastElements) != 0; + bool has_function_literal = (flags & ObjectLiteral::kHasFunction) != 0; // Check if boilerplate exists. If not, create it first. Handle boilerplate(literals->get(literals_index), isolate); @@ -511,7 +544,8 @@ static MaybeObject* Runtime_CreateObjectLiteralShallow( boilerplate = CreateObjectLiteralBoilerplate(isolate, literals, constant_properties, - should_have_fast_elements); + should_have_fast_elements, + has_function_literal); if (boilerplate.is_null()) return Failure::Exception(); // Update the functions literal and return the boilerplate. literals->set(literals_index, *boilerplate); diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index 7713dfb..f04c3cb 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -1361,7 +1361,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { __ push(FieldOperand(rdi, JSFunction::kLiteralsOffset)); __ Push(Smi::FromInt(expr->literal_index())); __ Push(expr->constant_properties()); - __ Push(Smi::FromInt(expr->fast_elements() ? 1 : 0)); + int flags = expr->fast_elements() + ? ObjectLiteral::kFastElements + : ObjectLiteral::kNoFlags; + flags |= expr->has_function() + ? ObjectLiteral::kHasFunction + : ObjectLiteral::kNoFlags; + __ Push(Smi::FromInt(flags)); if (expr->depth() > 1) { __ CallRuntime(Runtime::kCreateObjectLiteral, 4); } else { @@ -1431,6 +1437,12 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) { } } + if (expr->has_function()) { + ASSERT(result_saved); + __ push(Operand(rsp, 0)); + __ CallRuntime(Runtime::kToFastProperties, 1); + } + if (result_saved) { context()->PlugTOS(); } else { diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 6885961..69eee58 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -3487,6 +3487,13 @@ void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { } +void LCodeGen::DoToFastProperties(LToFastProperties* instr) { + ASSERT(ToRegister(instr->InputAt(0)).is(rax)); + __ push(rax); + CallRuntime(Runtime::kToFastProperties, 1, instr); +} + + void LCodeGen::DoRegExpLiteral(LRegExpLiteral* instr) { NearLabel materialized; // Registers will be used as follows: diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 6832ba4..09b7b63 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -1990,6 +1990,13 @@ LInstruction* LChunkBuilder::DoAccessArgumentsAt(HAccessArgumentsAt* instr) { } +LInstruction* LChunkBuilder::DoToFastProperties(HToFastProperties* instr) { + LOperand* object = UseFixed(instr->value(), rax); + LToFastProperties* result = new LToFastProperties(object); + return MarkAsCall(DefineFixed(result, rax), instr); +} + + LInstruction* LChunkBuilder::DoTypeof(HTypeof* instr) { LTypeof* result = new LTypeof(UseAtStart(instr->value())); return MarkAsCall(DefineFixed(result, rax), instr); diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 7fbaba3..f5c3ddd 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -154,6 +154,7 @@ class LCodeGen; V(StringLength) \ V(SubI) \ V(TaggedToI) \ + V(ToFastProperties) \ V(Throw) \ V(Typeof) \ V(TypeofIs) \ @@ -1762,6 +1763,17 @@ class LFunctionLiteral: public LTemplateInstruction<1, 0, 0> { }; +class LToFastProperties: public LTemplateInstruction<1, 1, 0> { + public: + explicit LToFastProperties(LOperand* value) { + inputs_[0] = value; + } + + DECLARE_CONCRETE_INSTRUCTION(ToFastProperties, "to-fast-properties") + DECLARE_HYDROGEN_ACCESSOR(ToFastProperties) +}; + + class LTypeof: public LTemplateInstruction<1, 1, 0> { public: explicit LTypeof(LOperand* value) { -- 2.7.4