From 1dd2b094a52af14a619a3cf540957c172ef15cb7 Mon Sep 17 00:00:00 2001 From: "mstarzinger@chromium.org" Date: Thu, 16 Feb 2012 17:32:30 +0000 Subject: [PATCH] Implement fast literal support in Crankshaft. This extends the current support for nested object literals we already have in Crankshaft, to also support nested array literals and mixed nested literals containing arrays and objects. All three types are generated by the unified HFastLiteral instruction. All previous upper bounds on nested literal graphs remain unchanged, keeping the size of generated code in check. The main intention is to boost performance of two-dimensional array literals containing constant elements (aka. matrices). R=danno@chromium.org TEST=mjsunit/compiler/literals-optimized Review URL: https://chromiumcodereview.appspot.com/9403018 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@10734 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/lithium-arm.cc | 15 ++- src/arm/lithium-arm.h | 24 ++-- src/arm/lithium-codegen-arm.cc | 64 +++++++++-- src/hydrogen-instructions.cc | 13 ++- src/hydrogen-instructions.h | 92 +++++++-------- src/hydrogen.cc | 102 +++++++++++------ src/ia32/lithium-codegen-ia32.cc | 63 ++++++++-- src/ia32/lithium-ia32.cc | 19 ++- src/ia32/lithium-ia32.h | 38 +++--- src/mips/lithium-codegen-mips.cc | 65 +++++++++-- src/mips/lithium-mips.cc | 15 ++- src/mips/lithium-mips.h | 24 ++-- src/x64/lithium-codegen-x64.cc | 64 +++++++++-- src/x64/lithium-x64.cc | 15 ++- src/x64/lithium-x64.h | 24 ++-- test/mjsunit/compiler/literals-optimized.js | 121 ++++++++++++++++++++ 16 files changed, 533 insertions(+), 225 deletions(-) create mode 100644 test/mjsunit/compiler/literals-optimized.js diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index ec119d80d5..0e5ac0f361 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -2093,19 +2093,18 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { + return MarkAsCall(DefineFixed(new LFastLiteral, r0), instr); +} + + LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { return MarkAsCall(DefineFixed(new LArrayLiteral, r0), instr); } -LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteralFast, r0), instr); -} - - -LInstruction* LChunkBuilder::DoObjectLiteralGeneric( - HObjectLiteralGeneric* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, r0), instr); +LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteral, r0), instr); } diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index f22a49fe9c..d26643a6ab 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -93,6 +93,7 @@ class LCodeGen; V(DivI) \ V(DoubleToI) \ V(ElementsKind) \ + V(FastLiteral) \ V(FixedArrayBaseLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ @@ -135,8 +136,7 @@ class LCodeGen; V(NumberTagD) \ V(NumberTagI) \ V(NumberUntagD) \ - V(ObjectLiteralFast) \ - V(ObjectLiteralGeneric) \ + V(ObjectLiteral) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ @@ -1917,6 +1917,13 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> { }; +class LFastLiteral: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal") + DECLARE_HYDROGEN_ACCESSOR(FastLiteral) +}; + + class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { public: DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal") @@ -1924,17 +1931,10 @@ class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { }; -class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> { +class LObjectLiteral: public LTemplateInstruction<1, 0, 0> { public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) -}; - - -class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral) }; diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index f845f4709a..97e743a4bd 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -4380,26 +4380,35 @@ void LCodeGen::EmitDeepCopy(Handle object, ASSERT(!source.is(r2)); ASSERT(!result.is(r2)); + // Only elements backing stores for non-COW arrays need to be copied. + Handle elements(object->elements()); + bool has_elements = elements->length() > 0 && + elements->map() != isolate()->heap()->fixed_cow_array_map(); + // Increase the offset so that subsequent objects end up right after - // this one. - int current_offset = *offset; - int size = object->map()->instance_size(); - *offset += size; + // this object and its backing store. + int object_offset = *offset; + int object_size = object->map()->instance_size(); + int elements_offset = *offset + object_size; + int elements_size = has_elements ? elements->Size() : 0; + *offset += object_size + elements_size; // Copy object header. ASSERT(object->properties()->length() == 0); - ASSERT(object->elements()->length() == 0 || - object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); int inobject_properties = object->map()->inobject_properties(); - int header_size = size - inobject_properties * kPointerSize; + int header_size = object_size - inobject_properties * kPointerSize; for (int i = 0; i < header_size; i += kPointerSize) { - __ ldr(r2, FieldMemOperand(source, i)); - __ str(r2, FieldMemOperand(result, current_offset + i)); + if (has_elements && i == JSObject::kElementsOffset) { + __ add(r2, result, Operand(elements_offset)); + } else { + __ ldr(r2, FieldMemOperand(source, i)); + } + __ str(r2, FieldMemOperand(result, object_offset + i)); } // Copy in-object properties. for (int i = 0; i < inobject_properties; i++) { - int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + int total_offset = object_offset + object->GetInObjectPropertyOffset(i); Handle value = Handle(object->InObjectPropertyAt(i)); if (value->IsJSObject()) { Handle value_object = Handle::cast(value); @@ -4415,10 +4424,41 @@ void LCodeGen::EmitDeepCopy(Handle object, __ str(r2, FieldMemOperand(result, total_offset)); } } + + // Copy elements backing store header. + ASSERT(!has_elements || elements->IsFixedArray()); + if (has_elements) { + __ LoadHeapObject(source, elements); + for (int i = 0; i < FixedArray::kHeaderSize; i += kPointerSize) { + __ ldr(r2, FieldMemOperand(source, i)); + __ str(r2, FieldMemOperand(result, elements_offset + i)); + } + } + + // Copy elements backing store content. + ASSERT(!has_elements || elements->IsFixedArray()); + int elements_length = has_elements ? elements->length() : 0; + for (int i = 0; i < elements_length; i++) { + int total_offset = elements_offset + FixedArray::OffsetOfElementAt(i); + Handle value = JSObject::GetElement(object, i); + if (value->IsJSObject()) { + Handle value_object = Handle::cast(value); + __ add(r2, result, Operand(*offset)); + __ str(r2, FieldMemOperand(result, total_offset)); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(r2, Handle::cast(value)); + __ str(r2, FieldMemOperand(result, total_offset)); + } else { + __ mov(r2, Operand(value)); + __ str(r2, FieldMemOperand(result, total_offset)); + } + } } -void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { +void LCodeGen::DoFastLiteral(LFastLiteral* instr) { int size = instr->hydrogen()->total_size(); // Allocate all objects that are part of the literal in one big @@ -4440,7 +4480,7 @@ void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { } -void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { +void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { Handle literals(instr->environment()->closure()->literals()); Handle constant_properties = instr->hydrogen()->constant_properties(); diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index cdc3e233f2..43aeeed396 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -1841,17 +1841,18 @@ HType HStringCharFromCode::CalculateInferredType() { } +HType HFastLiteral::CalculateInferredType() { + // TODO(mstarzinger): Be smarter, could also be JSArray here. + return HType::JSObject(); +} + + HType HArrayLiteral::CalculateInferredType() { return HType::JSArray(); } -HType HObjectLiteralFast::CalculateInferredType() { - return HType::JSObject(); -} - - -HType HObjectLiteralGeneric::CalculateInferredType() { +HType HObjectLiteral::CalculateInferredType() { return HType::JSObject(); } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index c864eb2aaf..3c4867266a 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -103,6 +103,7 @@ class LChunkBuilder; V(Div) \ V(ElementsKind) \ V(EnterInlined) \ + V(FastLiteral) \ V(FixedArrayBaseLength) \ V(ForceRepresentation) \ V(FunctionLiteral) \ @@ -140,8 +141,7 @@ class LChunkBuilder; V(LoadNamedGeneric) \ V(Mod) \ V(Mul) \ - V(ObjectLiteralFast) \ - V(ObjectLiteralGeneric) \ + V(ObjectLiteral) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ @@ -4348,6 +4348,41 @@ class HMaterializedLiteral: public HTemplateInstruction { }; +class HFastLiteral: public HMaterializedLiteral<1> { + public: + HFastLiteral(HValue* context, + Handle boilerplate, + int total_size, + int literal_index, + int depth) + : HMaterializedLiteral<1>(literal_index, depth), + boilerplate_(boilerplate), + total_size_(total_size) { + SetOperandAt(0, context); + } + + // Maximum depth and total number of elements and properties for literal + // graphs to be considered for fast deep-copying. + static const int kMaxLiteralDepth = 3; + static const int kMaxLiteralProperties = 8; + + HValue* context() { return OperandAt(0); } + Handle boilerplate() const { return boilerplate_; } + int total_size() const { return total_size_; } + + virtual Representation RequiredInputRepresentation(int index) { + return Representation::Tagged(); + } + virtual HType CalculateInferredType(); + + DECLARE_CONCRETE_INSTRUCTION(FastLiteral) + + private: + Handle boilerplate_; + int total_size_; +}; + + class HArrayLiteral: public HMaterializedLiteral<1> { public: HArrayLiteral(HValue* context, @@ -4386,49 +4421,14 @@ class HArrayLiteral: public HMaterializedLiteral<1> { }; -class HObjectLiteralFast: public HMaterializedLiteral<1> { +class HObjectLiteral: public HMaterializedLiteral<1> { public: - HObjectLiteralFast(HValue* context, - Handle boilerplate, - int total_size, - int literal_index, - int depth) - : HMaterializedLiteral<1>(literal_index, depth), - boilerplate_(boilerplate), - total_size_(total_size) { - SetOperandAt(0, context); - } - - // Maximum depth and total number of properties for object literal - // graphs to be considered for fast deep-copying. - static const int kMaxObjectLiteralDepth = 3; - static const int kMaxObjectLiteralProperties = 8; - - HValue* context() { return OperandAt(0); } - Handle boilerplate() const { return boilerplate_; } - int total_size() const { return total_size_; } - - virtual Representation RequiredInputRepresentation(int index) { - return Representation::Tagged(); - } - virtual HType CalculateInferredType(); - - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast) - - private: - Handle boilerplate_; - int total_size_; -}; - - -class HObjectLiteralGeneric: public HMaterializedLiteral<1> { - public: - HObjectLiteralGeneric(HValue* context, - Handle constant_properties, - bool fast_elements, - int literal_index, - int depth, - bool has_function) + HObjectLiteral(HValue* context, + Handle constant_properties, + bool fast_elements, + int literal_index, + int depth, + bool has_function) : HMaterializedLiteral<1>(literal_index, depth), constant_properties_(constant_properties), fast_elements_(fast_elements), @@ -4448,7 +4448,7 @@ class HObjectLiteralGeneric: public HMaterializedLiteral<1> { } virtual HType CalculateInferredType(); - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral) private: Handle constant_properties_; @@ -4543,7 +4543,7 @@ class HToFastProperties: public HUnaryOperation { // 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->IsObjectLiteralGeneric() || value->IsObjectLiteralFast()); + ASSERT(value->IsObjectLiteral() || value->IsFastLiteral()); set_representation(Representation::Tagged()); } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 076d5c2e59..de7d4236f6 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -3458,19 +3458,35 @@ void HGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { } -// Determines whether the given object literal boilerplate satisfies all -// limits to be considered for fast deep-copying and computes the total +// Determines whether the given array or object literal boilerplate satisfies +// all limits to be considered for fast deep-copying and computes the total // size of all objects that are part of the graph. -static bool IsFastObjectLiteral(Handle boilerplate, - int max_depth, - int* max_properties, - int* total_size) { - if (max_depth <= 0) return false; +static bool IsFastLiteral(Handle boilerplate, + int max_depth, + int* max_properties, + int* total_size) { + ASSERT(max_depth >= 0 && *max_properties >= 0); + if (max_depth == 0) return false; Handle elements(boilerplate->elements()); if (elements->length() > 0 && - elements->map() != HEAP->fixed_cow_array_map()) { - return false; + elements->map() != boilerplate->GetHeap()->fixed_cow_array_map()) { + if (!boilerplate->HasFastElements()) return false; + int length = elements->length(); + for (int i = 0; i < length; i++) { + if ((*max_properties)-- == 0) return false; + Handle value = JSObject::GetElement(boilerplate, i); + if (value->IsJSObject()) { + Handle value_object = Handle::cast(value); + if (!IsFastLiteral(value_object, + max_depth - 1, + max_properties, + total_size)) { + return false; + } + } + } + *total_size += FixedArray::SizeFor(length); } Handle properties(boilerplate->properties()); @@ -3479,14 +3495,14 @@ static bool IsFastObjectLiteral(Handle boilerplate, } else { int nof = boilerplate->map()->inobject_properties(); for (int i = 0; i < nof; i++) { - if ((*max_properties)-- <= 0) return false; + if ((*max_properties)-- == 0) return false; Handle value(boilerplate->InObjectPropertyAt(i)); if (value->IsJSObject()) { Handle value_object = Handle::cast(value); - if (!IsFastObjectLiteral(value_object, - max_depth - 1, - max_properties, - total_size)) { + if (!IsFastLiteral(value_object, + max_depth - 1, + max_properties, + total_size)) { return false; } } @@ -3508,26 +3524,26 @@ void HGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) { // Check whether to use fast or slow deep-copying for boilerplate. int total_size = 0; - int max_properties = HObjectLiteralFast::kMaxObjectLiteralProperties; + int max_properties = HFastLiteral::kMaxLiteralProperties; Handle boilerplate(closure->literals()->get(expr->literal_index())); if (boilerplate->IsJSObject() && - IsFastObjectLiteral(Handle::cast(boilerplate), - HObjectLiteralFast::kMaxObjectLiteralDepth, - &max_properties, - &total_size)) { + IsFastLiteral(Handle::cast(boilerplate), + HFastLiteral::kMaxLiteralDepth, + &max_properties, + &total_size)) { Handle boilerplate_object = Handle::cast(boilerplate); - literal = new(zone()) HObjectLiteralFast(context, - boilerplate_object, - total_size, - expr->literal_index(), - expr->depth()); + literal = new(zone()) HFastLiteral(context, + boilerplate_object, + total_size, + expr->literal_index(), + expr->depth()); } else { - literal = new(zone()) HObjectLiteralGeneric(context, - expr->constant_properties(), - expr->fast_elements(), - expr->literal_index(), - expr->depth(), - expr->has_function()); + literal = new(zone()) HObjectLiteral(context, + expr->constant_properties(), + expr->fast_elements(), + expr->literal_index(), + expr->depth(), + expr->has_function()); } // The object is expected in the bailout environment during computation @@ -3598,6 +3614,7 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { ZoneList* subexprs = expr->values(); int length = subexprs->length(); HValue* context = environment()->LookupContext(); + HInstruction* literal; Handle literals(environment()->closure()->literals()); Handle raw_boilerplate(literals->get(expr->literal_index())); @@ -3619,12 +3636,25 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) { ElementsKind boilerplate_elements_kind = Handle::cast(boilerplate)->GetElementsKind(); - HArrayLiteral* literal = new(zone()) HArrayLiteral( - context, - boilerplate, - length, - expr->literal_index(), - expr->depth()); + // Check whether to use fast or slow deep-copying for boilerplate. + int total_size = 0; + int max_properties = HFastLiteral::kMaxLiteralProperties; + if (IsFastLiteral(boilerplate, + HFastLiteral::kMaxLiteralDepth, + &max_properties, + &total_size)) { + literal = new(zone()) HFastLiteral(context, + boilerplate, + total_size, + expr->literal_index(), + expr->depth()); + } else { + literal = new(zone()) HArrayLiteral(context, + boilerplate, + length, + expr->literal_index(), + expr->depth()); + } // The array is expected in the bailout environment during computation // of the property values and is the value of the entire expression. diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 538645a712..3f003540f1 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -4259,26 +4259,35 @@ void LCodeGen::EmitDeepCopy(Handle object, __ Assert(equal, "Unexpected object literal boilerplate"); } + // Only elements backing stores for non-COW arrays need to be copied. + Handle elements(object->elements()); + bool has_elements = elements->length() > 0 && + elements->map() != isolate()->heap()->fixed_cow_array_map(); + // Increase the offset so that subsequent objects end up right after - // this one. - int current_offset = *offset; - int size = object->map()->instance_size(); - *offset += size; + // this object and its backing store. + int object_offset = *offset; + int object_size = object->map()->instance_size(); + int elements_offset = *offset + object_size; + int elements_size = has_elements ? elements->Size() : 0; + *offset += object_size + elements_size; // Copy object header. ASSERT(object->properties()->length() == 0); - ASSERT(object->elements()->length() == 0 || - object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); int inobject_properties = object->map()->inobject_properties(); - int header_size = size - inobject_properties * kPointerSize; + int header_size = object_size - inobject_properties * kPointerSize; for (int i = 0; i < header_size; i += kPointerSize) { - __ mov(ecx, FieldOperand(source, i)); - __ mov(FieldOperand(result, current_offset + i), ecx); + if (has_elements && i == JSObject::kElementsOffset) { + __ lea(ecx, Operand(result, elements_offset)); + } else { + __ mov(ecx, FieldOperand(source, i)); + } + __ mov(FieldOperand(result, object_offset + i), ecx); } // Copy in-object properties. for (int i = 0; i < inobject_properties; i++) { - int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + int total_offset = object_offset + object->GetInObjectPropertyOffset(i); Handle value = Handle(object->InObjectPropertyAt(i)); if (value->IsJSObject()) { Handle value_object = Handle::cast(value); @@ -4293,10 +4302,40 @@ void LCodeGen::EmitDeepCopy(Handle object, __ mov(FieldOperand(result, total_offset), Immediate(value)); } } + + // Copy elements backing store header. + ASSERT(!has_elements || elements->IsFixedArray()); + if (has_elements) { + __ LoadHeapObject(source, elements); + for (int i = 0; i < FixedArray::kHeaderSize; i += kPointerSize) { + __ mov(ecx, FieldOperand(source, i)); + __ mov(FieldOperand(result, elements_offset + i), ecx); + } + } + + // Copy elements backing store content. + ASSERT(!has_elements || elements->IsFixedArray()); + int elements_length = has_elements ? elements->length() : 0; + for (int i = 0; i < elements_length; i++) { + int total_offset = elements_offset + FixedArray::OffsetOfElementAt(i); + Handle value = JSObject::GetElement(object, i); + if (value->IsJSObject()) { + Handle value_object = Handle::cast(value); + __ lea(ecx, Operand(result, *offset)); + __ mov(FieldOperand(result, total_offset), ecx); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(ecx, Handle::cast(value)); + __ mov(FieldOperand(result, total_offset), ecx); + } else { + __ mov(FieldOperand(result, total_offset), Immediate(value)); + } + } } -void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { +void LCodeGen::DoFastLiteral(LFastLiteral* instr) { ASSERT(ToRegister(instr->context()).is(esi)); int size = instr->hydrogen()->total_size(); @@ -4318,7 +4357,7 @@ void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { } -void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { +void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { ASSERT(ToRegister(instr->context()).is(esi)); Handle literals(instr->environment()->closure()->literals()); Handle constant_properties = diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index b2d1b361d6..abf2f02bca 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -2212,6 +2212,13 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { + LOperand* context = UseFixed(instr->context(), esi); + return MarkAsCall( + DefineFixed(new(zone()) LFastLiteral(context), eax), instr); +} + + LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { LOperand* context = UseFixed(instr->context(), esi); return MarkAsCall( @@ -2219,18 +2226,10 @@ LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { } -LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { +LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { LOperand* context = UseFixed(instr->context(), esi); return MarkAsCall( - DefineFixed(new(zone()) LObjectLiteralFast(context), eax), instr); -} - - -LInstruction* LChunkBuilder::DoObjectLiteralGeneric( - HObjectLiteralGeneric* instr) { - LOperand* context = UseFixed(instr->context(), esi); - return MarkAsCall( - DefineFixed(new(zone()) LObjectLiteralGeneric(context), eax), instr); + DefineFixed(new(zone()) LObjectLiteral(context), eax), instr); } diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index fb24754021..a8da5f6afa 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -87,6 +87,7 @@ class LCodeGen; V(DivI) \ V(DoubleToI) \ V(ElementsKind) \ + V(FastLiteral) \ V(FixedArrayBaseLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ @@ -130,8 +131,7 @@ class LCodeGen; V(NumberTagD) \ V(NumberTagI) \ V(NumberUntagD) \ - V(ObjectLiteralFast) \ - V(ObjectLiteralGeneric) \ + V(ObjectLiteral) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ @@ -1991,6 +1991,19 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> { }; +class LFastLiteral: public LTemplateInstruction<1, 1, 0> { + public: + explicit LFastLiteral(LOperand* context) { + inputs_[0] = context; + } + + LOperand* context() { return inputs_[0]; } + + DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal") + DECLARE_HYDROGEN_ACCESSOR(FastLiteral) +}; + + class LArrayLiteral: public LTemplateInstruction<1, 1, 0> { public: explicit LArrayLiteral(LOperand* context) { @@ -2004,29 +2017,16 @@ class LArrayLiteral: public LTemplateInstruction<1, 1, 0> { }; -class LObjectLiteralFast: public LTemplateInstruction<1, 1, 0> { +class LObjectLiteral: public LTemplateInstruction<1, 1, 0> { public: - explicit LObjectLiteralFast(LOperand* context) { + explicit LObjectLiteral(LOperand* context) { inputs_[0] = context; } LOperand* context() { return inputs_[0]; } - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) -}; - - -class LObjectLiteralGeneric: public LTemplateInstruction<1, 1, 0> { - public: - explicit LObjectLiteralGeneric(LOperand* context) { - inputs_[0] = context; - } - - LOperand* context() { return inputs_[0]; } - - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral) }; diff --git a/src/mips/lithium-codegen-mips.cc b/src/mips/lithium-codegen-mips.cc index 34e1a8225d..aa12cde0da 100644 --- a/src/mips/lithium-codegen-mips.cc +++ b/src/mips/lithium-codegen-mips.cc @@ -4272,26 +4272,35 @@ void LCodeGen::EmitDeepCopy(Handle object, ASSERT(!source.is(a2)); ASSERT(!result.is(a2)); + // Only elements backing stores for non-COW arrays need to be copied. + Handle elements(object->elements()); + bool has_elements = elements->length() > 0 && + elements->map() != isolate()->heap()->fixed_cow_array_map(); + // Increase the offset so that subsequent objects end up right after - // this one. - int current_offset = *offset; - int size = object->map()->instance_size(); - *offset += size; + // this object and its backing store. + int object_offset = *offset; + int object_size = object->map()->instance_size(); + int elements_offset = *offset + object_size; + int elements_size = has_elements ? elements->Size() : 0; + *offset += object_size + elements_size; // Copy object header. ASSERT(object->properties()->length() == 0); - ASSERT(object->elements()->length() == 0 || - object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); int inobject_properties = object->map()->inobject_properties(); - int header_size = size - inobject_properties * kPointerSize; + int header_size = object_size - inobject_properties * kPointerSize; for (int i = 0; i < header_size; i += kPointerSize) { - __ lw(a2, FieldMemOperand(source, i)); - __ sw(a2, FieldMemOperand(result, current_offset + i)); + if (has_elements && i == JSObject::kElementsOffset) { + __ Addu(a2, result, Operand(elements_offset)); + } else { + __ lw(a2, FieldMemOperand(source, i)); + } + __ sw(a2, FieldMemOperand(result, object_offset + i)); } // Copy in-object properties. for (int i = 0; i < inobject_properties; i++) { - int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + int total_offset = object_offset + object->GetInObjectPropertyOffset(i); Handle value = Handle(object->InObjectPropertyAt(i)); if (value->IsJSObject()) { Handle value_object = Handle::cast(value); @@ -4307,10 +4316,42 @@ void LCodeGen::EmitDeepCopy(Handle object, __ sw(a2, FieldMemOperand(result, total_offset)); } } + + + // Copy elements backing store header. + ASSERT(!has_elements || elements->IsFixedArray()); + if (has_elements) { + __ LoadHeapObject(source, elements); + for (int i = 0; i < FixedArray::kHeaderSize; i += kPointerSize) { + __ lw(a2, FieldMemOperand(source, i)); + __ sw(a2, FieldMemOperand(result, elements_offset + i)); + } + } + + // Copy elements backing store content. + ASSERT(!has_elements || elements->IsFixedArray()); + int elements_length = has_elements ? elements->length() : 0; + for (int i = 0; i < elements_length; i++) { + int total_offset = elements_offset + FixedArray::OffsetOfElementAt(i); + Handle value = JSObject::GetElement(object, i); + if (value->IsJSObject()) { + Handle value_object = Handle::cast(value); + __ Addu(a2, result, Operand(*offset)); + __ sw(a2, FieldMemOperand(result, total_offset)); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(a2, Handle::cast(value)); + __ sw(a2, FieldMemOperand(result, total_offset)); + } else { + __ li(a2, Operand(value)); + __ sw(a2, FieldMemOperand(result, total_offset)); + } + } } -void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { +void LCodeGen::DoFastLiteral(LFastLiteral* instr) { int size = instr->hydrogen()->total_size(); // Allocate all objects that are part of the literal in one big @@ -4332,7 +4373,7 @@ void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { } -void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { +void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { ASSERT(ToRegister(instr->result()).is(v0)); Handle literals(instr->environment()->closure()->literals()); Handle constant_properties = diff --git a/src/mips/lithium-mips.cc b/src/mips/lithium-mips.cc index 0bc222339f..3a2f3d964e 100644 --- a/src/mips/lithium-mips.cc +++ b/src/mips/lithium-mips.cc @@ -2093,19 +2093,18 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { + return MarkAsCall(DefineFixed(new LFastLiteral, v0), instr); +} + + LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { return MarkAsCall(DefineFixed(new LArrayLiteral, v0), instr); } -LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteralFast, v0), instr); -} - - -LInstruction* LChunkBuilder::DoObjectLiteralGeneric( - HObjectLiteralGeneric* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, v0), instr); +LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteral, v0), instr); } diff --git a/src/mips/lithium-mips.h b/src/mips/lithium-mips.h index 0a21649fa8..cd1214f528 100644 --- a/src/mips/lithium-mips.h +++ b/src/mips/lithium-mips.h @@ -92,6 +92,7 @@ class LCodeGen; V(DivI) \ V(DoubleToI) \ V(ElementsKind) \ + V(FastLiteral) \ V(FixedArrayBaseLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ @@ -134,8 +135,7 @@ class LCodeGen; V(NumberTagD) \ V(NumberTagI) \ V(NumberUntagD) \ - V(ObjectLiteralFast) \ - V(ObjectLiteralGeneric) \ + V(ObjectLiteral) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ @@ -1909,6 +1909,13 @@ class LClampTToUint8: public LTemplateInstruction<1, 1, 1> { }; +class LFastLiteral: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal") + DECLARE_HYDROGEN_ACCESSOR(FastLiteral) +}; + + class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { public: DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal") @@ -1916,17 +1923,10 @@ class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { }; -class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> { +class LObjectLiteral: public LTemplateInstruction<1, 0, 0> { public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) -}; - - -class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral) }; diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 4129db69db..9f649317a9 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -3975,26 +3975,35 @@ void LCodeGen::EmitDeepCopy(Handle object, ASSERT(!source.is(rcx)); ASSERT(!result.is(rcx)); + // Only elements backing stores for non-COW arrays need to be copied. + Handle elements(object->elements()); + bool has_elements = elements->length() > 0 && + elements->map() != isolate()->heap()->fixed_cow_array_map(); + // Increase the offset so that subsequent objects end up right after - // this one. - int current_offset = *offset; - int size = object->map()->instance_size(); - *offset += size; + // this object and its backing store. + int object_offset = *offset; + int object_size = object->map()->instance_size(); + int elements_offset = *offset + object_size; + int elements_size = has_elements ? elements->Size() : 0; + *offset += object_size + elements_size; // Copy object header. ASSERT(object->properties()->length() == 0); - ASSERT(object->elements()->length() == 0 || - object->elements()->map() == isolate()->heap()->fixed_cow_array_map()); int inobject_properties = object->map()->inobject_properties(); - int header_size = size - inobject_properties * kPointerSize; + int header_size = object_size - inobject_properties * kPointerSize; for (int i = 0; i < header_size; i += kPointerSize) { - __ movq(rcx, FieldOperand(source, i)); - __ movq(FieldOperand(result, current_offset + i), rcx); + if (has_elements && i == JSObject::kElementsOffset) { + __ lea(rcx, Operand(result, elements_offset)); + } else { + __ movq(rcx, FieldOperand(source, i)); + } + __ movq(FieldOperand(result, object_offset + i), rcx); } // Copy in-object properties. for (int i = 0; i < inobject_properties; i++) { - int total_offset = current_offset + object->GetInObjectPropertyOffset(i); + int total_offset = object_offset + object->GetInObjectPropertyOffset(i); Handle value = Handle(object->InObjectPropertyAt(i)); if (value->IsJSObject()) { Handle value_object = Handle::cast(value); @@ -4010,10 +4019,41 @@ void LCodeGen::EmitDeepCopy(Handle object, __ movq(FieldOperand(result, total_offset), rcx); } } + + // Copy elements backing store header. + ASSERT(!has_elements || elements->IsFixedArray()); + if (has_elements) { + __ LoadHeapObject(source, elements); + for (int i = 0; i < FixedArray::kHeaderSize; i += kPointerSize) { + __ movq(rcx, FieldOperand(source, i)); + __ movq(FieldOperand(result, elements_offset + i), rcx); + } + } + + // Copy elements backing store content. + ASSERT(!has_elements || elements->IsFixedArray()); + int elements_length = has_elements ? elements->length() : 0; + for (int i = 0; i < elements_length; i++) { + int total_offset = elements_offset + FixedArray::OffsetOfElementAt(i); + Handle value = JSObject::GetElement(object, i); + if (value->IsJSObject()) { + Handle value_object = Handle::cast(value); + __ lea(rcx, Operand(result, *offset)); + __ movq(FieldOperand(result, total_offset), rcx); + __ LoadHeapObject(source, value_object); + EmitDeepCopy(value_object, result, source, offset); + } else if (value->IsHeapObject()) { + __ LoadHeapObject(rcx, Handle::cast(value)); + __ movq(FieldOperand(result, total_offset), rcx); + } else { + __ movq(rcx, value, RelocInfo::NONE); + __ movq(FieldOperand(result, total_offset), rcx); + } + } } -void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { +void LCodeGen::DoFastLiteral(LFastLiteral* instr) { int size = instr->hydrogen()->total_size(); // Allocate all objects that are part of the literal in one big @@ -4034,7 +4074,7 @@ void LCodeGen::DoObjectLiteralFast(LObjectLiteralFast* instr) { } -void LCodeGen::DoObjectLiteralGeneric(LObjectLiteralGeneric* instr) { +void LCodeGen::DoObjectLiteral(LObjectLiteral* instr) { Handle literals(instr->environment()->closure()->literals()); Handle constant_properties = instr->hydrogen()->constant_properties(); diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 9ffa8aecfe..3e30a4a784 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -2094,19 +2094,18 @@ LInstruction* LChunkBuilder::DoStringLength(HStringLength* instr) { } +LInstruction* LChunkBuilder::DoFastLiteral(HFastLiteral* instr) { + return MarkAsCall(DefineFixed(new LFastLiteral, rax), instr); +} + + LInstruction* LChunkBuilder::DoArrayLiteral(HArrayLiteral* instr) { return MarkAsCall(DefineFixed(new LArrayLiteral, rax), instr); } -LInstruction* LChunkBuilder::DoObjectLiteralFast(HObjectLiteralFast* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteralFast, rax), instr); -} - - -LInstruction* LChunkBuilder::DoObjectLiteralGeneric( - HObjectLiteralGeneric* instr) { - return MarkAsCall(DefineFixed(new LObjectLiteralGeneric, rax), instr); +LInstruction* LChunkBuilder::DoObjectLiteral(HObjectLiteral* instr) { + return MarkAsCall(DefineFixed(new LObjectLiteral, rax), instr); } diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index db1177029a..ccae35c129 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -93,6 +93,7 @@ class LCodeGen; V(DivI) \ V(DoubleToI) \ V(ElementsKind) \ + V(FastLiteral) \ V(FixedArrayBaseLength) \ V(FunctionLiteral) \ V(GetCachedArrayIndex) \ @@ -135,8 +136,7 @@ class LCodeGen; V(NumberTagD) \ V(NumberTagI) \ V(NumberUntagD) \ - V(ObjectLiteralFast) \ - V(ObjectLiteralGeneric) \ + V(ObjectLiteral) \ V(OsrEntry) \ V(OuterContext) \ V(Parameter) \ @@ -1906,6 +1906,13 @@ class LCheckNonSmi: public LTemplateInstruction<0, 1, 0> { }; +class LFastLiteral: public LTemplateInstruction<1, 0, 0> { + public: + DECLARE_CONCRETE_INSTRUCTION(FastLiteral, "fast-literal") + DECLARE_HYDROGEN_ACCESSOR(FastLiteral) +}; + + class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { public: DECLARE_CONCRETE_INSTRUCTION(ArrayLiteral, "array-literal") @@ -1913,17 +1920,10 @@ class LArrayLiteral: public LTemplateInstruction<1, 0, 0> { }; -class LObjectLiteralFast: public LTemplateInstruction<1, 0, 0> { +class LObjectLiteral: public LTemplateInstruction<1, 0, 0> { public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralFast, "object-literal-fast") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralFast) -}; - - -class LObjectLiteralGeneric: public LTemplateInstruction<1, 0, 0> { - public: - DECLARE_CONCRETE_INSTRUCTION(ObjectLiteralGeneric, "object-literal-generic") - DECLARE_HYDROGEN_ACCESSOR(ObjectLiteralGeneric) + DECLARE_CONCRETE_INSTRUCTION(ObjectLiteral, "object-literal") + DECLARE_HYDROGEN_ACCESSOR(ObjectLiteral) }; diff --git a/test/mjsunit/compiler/literals-optimized.js b/test/mjsunit/compiler/literals-optimized.js new file mode 100644 index 0000000000..049e21a3a5 --- /dev/null +++ b/test/mjsunit/compiler/literals-optimized.js @@ -0,0 +1,121 @@ +// Copyright 2012 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. + +// Flags: --allow-natives-syntax + +// Test optimized versions of array and object literals. + +function TestOptimizedLiteral(create, verify) { + verify(create(1, 2, 3), 1, 2, 3); + verify(create(3, 5, 7), 3, 5, 7); + %OptimizeFunctionOnNextCall(create); + verify(create(11, 23, 42), 11, 23, 42); +} + + +// Test shallow array literal. +function create_arr_shallow(a, b, c) { + return [0, a, 0, b, 0, c]; +} +function verify_arr_shallow(array, a, b, c) { + assertSame(6, array.length); + assertSame(0, array[0]); + assertSame(a, array[1]); + assertSame(0, array[2]); + assertSame(b, array[3]); + assertSame(0, array[4]); + assertSame(c, array[5]); +} +TestOptimizedLiteral(create_arr_shallow, verify_arr_shallow); + + +// Test nested array literal. +function create_arr_nested(a, b, c) { + return [[0, a], [b, c], [1, 2], 3]; +} +function verify_arr_nested(array, a, b, c) { + assertSame(4, array.length); + assertSame(2, array[0].length); + assertSame(0, array[0][0]); + assertSame(a, array[0][1]); + assertSame(2, array[1].length); + assertSame(b, array[1][0]); + assertSame(c, array[1][1]); + assertSame(2, array[2].length); + assertSame(1, array[2][0]); + assertSame(2, array[2][1]); + assertSame(3, array[3]); +} +TestOptimizedLiteral(create_arr_nested, verify_arr_nested); + + +// Test shallow object literal. +function create_obj_shallow(a, b, c) { + return { x:a, y:b, z:c, v:'foo', 9:'bar' }; +} +function verify_obj_shallow(object, a, b, c) { + assertSame(a, object.x); + assertSame(b, object.y); + assertSame(c, object.z); + assertSame('foo', object.v); + assertSame('bar', object[9]); +} +TestOptimizedLiteral(create_obj_shallow, verify_obj_shallow); + + +// Test nested object literal. +function create_obj_nested(a, b, c) { + return { x:{ v:a, w:b }, y:{ v:1, w:2 }, z:c, v:'foo', 9:'bar' }; +} +function verify_obj_nested(object, a, b, c) { + assertSame(a, object.x.v); + assertSame(b, object.x.w); + assertSame(1, object.y.v); + assertSame(2, object.y.w); + assertSame(c, object.z); + assertSame('foo', object.v); + assertSame('bar', object[9]); +} +TestOptimizedLiteral(create_obj_nested, verify_obj_nested); + + +// Test mixed array and object literal. +function create_mixed_nested(a, b, c) { + return { x:[1, 2], y:[a, b], z:c, v:{ v:'foo' }, 9:'bar' }; +} +function verify_mixed_nested(object, a, b, c) { + assertSame(2, object.x.length); + assertSame(1, object.x[0]); + assertSame(2, object.x[1]); + assertSame(2, object.y.length); + assertSame(a, object.y[0]); + assertSame(b, object.y[1]); + assertSame(c, object.z); + assertSame('foo', object.v.v); + assertSame('bar', object[9]); +} +TestOptimizedLiteral(create_mixed_nested, verify_mixed_nested);