From: bmeurer@chromium.org Date: Tue, 12 Nov 2013 10:21:08 +0000 (+0000) Subject: Add initial hydrogenized NewStringAddStub. X-Git-Tag: upstream/4.7.83~11795 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6f75e92902f5fa74f6e105e61e386e51601a5512;p=platform%2Fupstream%2Fv8.git Add initial hydrogenized NewStringAddStub. The new stub is enabled via the --new-string-add flag, which is disabled by default. For now, it's only a stripped down version of the native StringAddStub, it's still work-in-progress. BUG=v8:2990 R=mvstanton@chromium.org Review URL: https://codereview.chromium.org/61893009 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17635 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc index 95a04f7..2607f9e 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -302,6 +302,17 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor( } +void NewStringAddStub::InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) { + static Register registers[] = { r1, r0 }; + descriptor->register_param_count_ = 2; + descriptor->register_params_ = registers; + descriptor->deoptimization_handler_ = + Runtime::FunctionForId(Runtime::kStringAdd)->entry; +} + + #define __ ACCESS_MASM(masm) diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 8fb1e15..28d8184 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -3705,11 +3705,21 @@ void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) { void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { ZoneList* args = expr->arguments(); ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); - StringAddStub stub(STRING_ADD_CHECK_BOTH); - __ CallStub(&stub); + if (FLAG_new_string_add) { + VisitForStackValue(args->at(0)); + VisitForAccumulatorValue(args->at(1)); + + __ pop(r1); + NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED); + __ CallStub(&stub); + } else { + VisitForStackValue(args->at(0)); + VisitForStackValue(args->at(1)); + + StringAddStub stub(STRING_ADD_CHECK_BOTH); + __ CallStub(&stub); + } context()->Plug(r0); } diff --git a/src/arm/lithium-arm.cc b/src/arm/lithium-arm.cc index 5fb0e53..6d02ba1 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -2413,8 +2413,12 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) { LOperand* context = UseFixed(instr->context(), cp); - LOperand* left = UseRegisterAtStart(instr->left()); - LOperand* right = UseRegisterAtStart(instr->right()); + LOperand* left = FLAG_new_string_add + ? UseFixed(instr->left(), r1) + : UseRegisterAtStart(instr->left()); + LOperand* right = FLAG_new_string_add + ? UseFixed(instr->right(), r0) + : UseRegisterAtStart(instr->right()); return MarkAsCall( DefineFixed(new(zone()) LStringAdd(context, left, right), r0), instr); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 8049b68..348b8ce 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -4562,10 +4562,18 @@ void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) { void LCodeGen::DoStringAdd(LStringAdd* instr) { ASSERT(ToRegister(instr->context()).is(cp)); - __ push(ToRegister(instr->left())); - __ push(ToRegister(instr->right())); - StringAddStub stub(instr->hydrogen()->flags()); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + if (FLAG_new_string_add) { + ASSERT(ToRegister(instr->left()).is(r1)); + ASSERT(ToRegister(instr->right()).is(r0)); + NewStringAddStub stub(instr->hydrogen()->flags(), + isolate()->heap()->GetPretenureMode()); + CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + } else { + __ push(ToRegister(instr->left())); + __ push(ToRegister(instr->right())); + StringAddStub stub(instr->hydrogen()->flags()); + CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + } } diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc index 19b6088..76786d1 100644 --- a/src/code-stubs-hydrogen.cc +++ b/src/code-stubs-hydrogen.cc @@ -972,6 +972,38 @@ Handle BinaryOpStub::GenerateCode(Isolate* isolate) { template <> +HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { + NewStringAddStub* stub = casted_stub(); + StringAddFlags flags = stub->flags(); + PretenureFlag pretenure_flag = stub->pretenure_flag(); + + HValue* left = GetParameter(NewStringAddStub::kLeft); + HValue* right = GetParameter(NewStringAddStub::kRight); + + // Make sure that both arguments are strings if not known in advance. + if ((flags & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) { + IfBuilder if_leftnotstring(this); + if_leftnotstring.IfNot(left); + if_leftnotstring.Then(); + if_leftnotstring.Deopt("Expected string for LHS of string addition"); + } + if ((flags & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) { + IfBuilder if_rightnotstring(this); + if_rightnotstring.IfNot(right); + if_rightnotstring.Then(); + if_rightnotstring.Deopt("Expected string for RHS of string addition"); + } + + return BuildStringAdd(left, right, pretenure_flag); +} + + +Handle NewStringAddStub::GenerateCode(Isolate* isolate) { + return DoGenerateCode(isolate, this); +} + + +template <> HValue* CodeStubGraphBuilder::BuildCodeInitializedStub() { ToBooleanStub* stub = casted_stub(); diff --git a/src/code-stubs.cc b/src/code-stubs.cc index afa3cd0..e76bed3 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -682,6 +682,21 @@ Handle BinaryOpStub::GetResultType(Isolate* isolate) const { } +void NewStringAddStub::PrintBaseName(StringStream* stream) { + stream->Add("NewStringAddStub"); + if ((flags() & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) { + stream->Add("_CheckBoth"); + } else if ((flags() & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) { + stream->Add("_CheckLeft"); + } else if ((flags() & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) { + stream->Add("_CheckRight"); + } + if (pretenure_flag() == TENURED) { + stream->Add("_Tenured"); + } +} + + InlineCacheState ICCompareStub::GetICState() { CompareIC::State state = Max(left_, right_); switch (state) { @@ -1134,6 +1149,13 @@ void FastNewClosureStub::InstallDescriptors(Isolate* isolate) { } +// static +void NewStringAddStub::InstallDescriptors(Isolate* isolate) { + NewStringAddStub stub(STRING_ADD_CHECK_NONE, NOT_TENURED); + InstallDescriptor(isolate, &stub); +} + + ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate) : argument_count_(ANY) { ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate); diff --git a/src/code-stubs.h b/src/code-stubs.h index 3dc32e8..b56a3ec 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -43,6 +43,7 @@ namespace internal { V(CallConstruct) \ V(BinaryOp) \ V(StringAdd) \ + V(NewStringAdd) \ V(SubString) \ V(StringCompare) \ V(Compare) \ @@ -1179,6 +1180,47 @@ class BinaryOpStub: public HydrogenCodeStub { }; +// TODO(bmeurer): Rename to StringAddStub once we dropped the old StringAddStub. +class NewStringAddStub V8_FINAL : public HydrogenCodeStub { + public: + NewStringAddStub(StringAddFlags flags, PretenureFlag pretenure_flag) + : bit_field_(StringAddFlagsBits::encode(flags) | + PretenureFlagBits::encode(pretenure_flag)) {} + + StringAddFlags flags() const { + return StringAddFlagsBits::decode(bit_field_); + } + + PretenureFlag pretenure_flag() const { + return PretenureFlagBits::decode(bit_field_); + } + + virtual Handle GenerateCode(Isolate* isolate) V8_OVERRIDE; + + virtual void InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) V8_OVERRIDE; + + static void InstallDescriptors(Isolate* isolate); + + // Parameters accessed via CodeStubGraphBuilder::GetParameter() + static const int kLeft = 0; + static const int kRight = 1; + + private: + class StringAddFlagsBits: public BitField {}; + class PretenureFlagBits: public BitField {}; + uint32_t bit_field_; + + virtual Major MajorKey() V8_OVERRIDE { return NewStringAdd; } + virtual int NotMissMinorKey() V8_OVERRIDE { return bit_field_; } + + virtual void PrintBaseName(StringStream* stream) V8_OVERRIDE; + + DISALLOW_COPY_AND_ASSIGN(NewStringAddStub); +}; + + class ICCompareStub: public PlatformCodeStub { public: ICCompareStub(Token::Value op, diff --git a/src/flag-definitions.h b/src/flag-definitions.h index f0444d1..1781f7f 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -338,6 +338,8 @@ DEFINE_bool(omit_map_checks_for_leaf_maps, true, "do not emit check maps for constant values that have a leaf map, " "deoptimize the optimized code if the layout of the maps changes.") +DEFINE_bool(new_string_add, false, "enable new string addition") + // Experimental profiler changes. DEFINE_bool(experimental_profiler, true, "enable all profiler experiments") DEFINE_bool(watch_ic_patching, false, "profiler considers IC stability") diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index b9c0ca9..d2ae975 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -5808,6 +5808,12 @@ class HObjectAccess V8_FINAL { FLAG_track_fields ? Representation::Smi() : Representation::Tagged()); } + static HObjectAccess ForStringHashField() { + return HObjectAccess(kInobject, + String::kHashFieldOffset, + Representation::Integer32()); + } + static HObjectAccess ForStringLength() { STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue); return HObjectAccess( @@ -5816,6 +5822,14 @@ class HObjectAccess V8_FINAL { FLAG_track_fields ? Representation::Smi() : Representation::Tagged()); } + static HObjectAccess ForConsStringFirst() { + return HObjectAccess(kInobject, ConsString::kFirstOffset); + } + + static HObjectAccess ForConsStringSecond() { + return HObjectAccess(kInobject, ConsString::kSecondOffset); + } + static HObjectAccess ForPropertiesPointer() { return HObjectAccess(kInobject, JSObject::kPropertiesOffset); } @@ -5863,6 +5877,12 @@ class HObjectAccess V8_FINAL { Representation::UInteger8()); } + static HObjectAccess ForMapInstanceType() { + return HObjectAccess(kInobject, + Map::kInstanceTypeOffset, + Representation::UInteger8()); + } + static HObjectAccess ForPropertyCellValue() { return HObjectAccess(kInobject, PropertyCell::kValueOffset); } diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 147563e..c09cd6d 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -1469,6 +1469,342 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object, } +HValue* HGraphBuilder::BuildSeqStringSizeFor(HValue* length, + String::Encoding encoding) { + STATIC_ASSERT((SeqString::kHeaderSize & kObjectAlignmentMask) == 0); + HValue* size = length; + if (encoding == String::TWO_BYTE_ENCODING) { + size = Add(length, graph()->GetConstant1()); + size->ClearFlag(HValue::kCanOverflow); + size->SetFlag(HValue::kUint32); + } + size = Add(size, Add(static_cast( + SeqString::kHeaderSize + kObjectAlignmentMask))); + size->ClearFlag(HValue::kCanOverflow); + size = Add( + Token::BIT_AND, size, Add(static_cast( + ~kObjectAlignmentMask))); + return size; +} + + +void HGraphBuilder::BuildCopySeqStringChars(HValue* src, + HValue* src_offset, + String::Encoding src_encoding, + HValue* dst, + HValue* dst_offset, + String::Encoding dst_encoding, + HValue* length) { + ASSERT(dst_encoding != String::ONE_BYTE_ENCODING || + src_encoding == String::ONE_BYTE_ENCODING); + LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement); + HValue* index = loop.BeginBody(graph()->GetConstant0(), length, Token::LT); + { + HValue* src_index = Add(src_offset, index); + HValue* value = Add(src_encoding, src, src_index); + HValue* dst_index = Add(dst_offset, index); + Add(dst_encoding, dst, dst_index, value); + } + loop.EndBody(); +} + + +HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left, + HValue* right, + PretenureFlag pretenure_flag) { + // Determine the string lengths. + HValue* left_length = Add( + left, HObjectAccess::ForStringLength()); + HValue* right_length = Add( + right, HObjectAccess::ForStringLength()); + + // Check if we concatenated the strings here, or if we have to resort to the + // runtime function. + HIfContinuation handled(graph()->CreateBasicBlock(), + graph()->CreateBasicBlock()); + + // Check if both parameters do not exceed half the max string length, because + // exceptionally long strings should be handled in the runtime. Unfortunately + // we cannot actually check whether the combined length of both strings + // exceeds String::kMaxLength (because of unclear results from the + // representation inference phase), so we use a pessimistic approach here + // instead, checking that the length of either substring does not exceed half + // of String::kMaxLength. + HConstant* max_length = Add(String::kMaxLength / 2); + IfBuilder if_nooverflow(this); + if_nooverflow.If( + left_length, max_length, Token::LTE); + if_nooverflow.AndIf( + right_length, max_length, Token::LTE); + if_nooverflow.Then(); + { + // Determine the string instance types. + HLoadNamedField* left_instance_type = Add( + Add(left, HObjectAccess::ForMap()), + HObjectAccess::ForMapInstanceType()); + HLoadNamedField* right_instance_type = Add( + Add(right, HObjectAccess::ForMap()), + HObjectAccess::ForMapInstanceType()); + + // Compute difference of instance types. + HValue* xored_instance_types = Add( + Token::BIT_XOR, left_instance_type, right_instance_type); + + // Compute the length of the resulting string. + HValue* length = Add(left_length, right_length); + + // Check if we should create a cons string. + IfBuilder if_createcons(this); + if_createcons.If( + length, Add(ConsString::kMinLength), Token::GTE); + if_createcons.Then(); + { + // Allocate the cons string object. HAllocate does not care whether we + // pass CONS_STRING_TYPE or CONS_ASCII_STRING_TYPE here, so we just use + // CONS_STRING_TYPE here. Below we decide whether the cons string is + // one-byte or two-byte and set the appropriate map. + HAllocate* string = Add(Add(ConsString::kSize), + HType::String(), pretenure_flag, + CONS_STRING_TYPE); + + // Compute the intersection of instance types. + HValue* anded_instance_types = Add( + Token::BIT_AND, left_instance_type, right_instance_type); + + // We create a one-byte cons string if + // 1. both strings are one-byte, or + // 2. at least one of the strings is two-byte, but happens to contain only + // one-byte characters. + // To do this, we check + // 1. if both strings are one-byte, or if the one-byte data hint is set in + // both strings, or + // 2. if one of the strings has the one-byte data hint set and the other + // string is one-byte. + IfBuilder if_onebyte(this); + STATIC_ASSERT(kOneByteStringTag != 0); + STATIC_ASSERT(kOneByteDataHintMask != 0); + if_onebyte.If( + Add( + Token::BIT_AND, anded_instance_types, + Add(static_cast( + kStringEncodingMask | kOneByteDataHintMask))), + graph()->GetConstant0(), Token::NE); + if_onebyte.Or(); + STATIC_ASSERT(kOneByteStringTag != 0 && + kOneByteDataHintTag != 0 && + kOneByteDataHintTag != kOneByteStringTag); + if_onebyte.If( + Add( + Token::BIT_AND, xored_instance_types, + Add(static_cast( + kOneByteStringTag | kOneByteDataHintTag))), + Add(static_cast( + kOneByteStringTag | kOneByteDataHintTag)), Token::EQ); + if_onebyte.Then(); + { + // We can safely skip the write barrier for storing the map here. + Handle map = isolate()->factory()->cons_ascii_string_map(); + AddStoreMapConstantNoWriteBarrier(string, map); + } + if_onebyte.Else(); + { + // We can safely skip the write barrier for storing the map here. + Handle map = isolate()->factory()->cons_string_map(); + AddStoreMapConstantNoWriteBarrier(string, map); + } + if_onebyte.End(); + + // Initialize the cons string fields. + Add(string, HObjectAccess::ForStringHashField(), + Add(String::kEmptyHashField)); + Add(string, HObjectAccess::ForStringLength(), length); + Add(string, HObjectAccess::ForConsStringFirst(), left); + Add(string, HObjectAccess::ForConsStringSecond(), + right); + + // Cons string is result. + Push(string); + } + if_createcons.Else(); + { + // Compute union of instance types. + HValue* ored_instance_types = Add( + Token::BIT_OR, left_instance_type, right_instance_type); + + // Check if both strings have the same encoding and both are + // sequential. + IfBuilder if_sameencodingandsequential(this); + if_sameencodingandsequential.If( + Add( + Token::BIT_AND, xored_instance_types, + Add(static_cast(kStringEncodingMask))), + graph()->GetConstant0(), Token::EQ); + if_sameencodingandsequential.And(); + STATIC_ASSERT(kSeqStringTag == 0); + if_sameencodingandsequential.If( + Add( + Token::BIT_AND, ored_instance_types, + Add(static_cast(kStringRepresentationMask))), + graph()->GetConstant0(), Token::EQ); + if_sameencodingandsequential.Then(); + { + // Check if the result is a one-byte string. + IfBuilder if_onebyte(this); + STATIC_ASSERT(kOneByteStringTag != 0); + if_onebyte.If( + Add( + Token::BIT_AND, ored_instance_types, + Add(static_cast(kStringEncodingMask))), + graph()->GetConstant0(), Token::NE); + if_onebyte.Then(); + { + // Calculate the number of bytes needed for the characters in the + // string while observing object alignment. + HValue* size = BuildSeqStringSizeFor( + length, String::ONE_BYTE_ENCODING); + + // Allocate the ASCII string object. + Handle map = isolate()->factory()->ascii_string_map(); + HAllocate* string = Add(size, HType::String(), + pretenure_flag, ASCII_STRING_TYPE); + string->set_known_initial_map(map); + + // We can safely skip the write barrier for storing map here. + AddStoreMapConstantNoWriteBarrier(string, map); + + // Copy bytes from the left string. + BuildCopySeqStringChars( + left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, + string, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, + left_length); + + // Copy bytes from the right string. + BuildCopySeqStringChars( + right, graph()->GetConstant0(), String::ONE_BYTE_ENCODING, + string, left_length, String::ONE_BYTE_ENCODING, + right_length); + + // Return the string. + Push(string); + } + if_onebyte.Else(); + { + // Calculate the number of bytes needed for the characters in the + // string while observing object alignment. + HValue* size = BuildSeqStringSizeFor( + length, String::TWO_BYTE_ENCODING); + + // Allocate the two-byte string object. + Handle map = isolate()->factory()->string_map(); + HAllocate* string = Add(size, HType::String(), + pretenure_flag, STRING_TYPE); + string->set_known_initial_map(map); + + // We can safely skip the write barrier for storing map here. + AddStoreMapConstantNoWriteBarrier(string, map); + + // Copy bytes from the left string. + BuildCopySeqStringChars( + left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, + string, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, + left_length); + + // Copy bytes from the right string. + BuildCopySeqStringChars( + right, graph()->GetConstant0(), String::TWO_BYTE_ENCODING, + string, left_length, String::TWO_BYTE_ENCODING, + right_length); + + // Return the string. + Push(string); + } + if_onebyte.End(); + + // Initialize the (common) string fields. + HValue* string = Pop(); + Add(string, HObjectAccess::ForStringHashField(), + Add(String::kEmptyHashField)); + Add(string, HObjectAccess::ForStringLength(), + length); + Push(string); + } + if_sameencodingandsequential.JoinContinuation(&handled); + } + if_createcons.JoinContinuation(&handled); + } + if_nooverflow.JoinContinuation(&handled); + + // Check if the strings were concatenated successfully, otherwise fallback to + // add the strings in the runtime. + IfBuilder if_handled(this, &handled); + if_handled.Then(); + { + // Count the native string addition. + AddIncrementCounter(isolate()->counters()->string_add_native()); + } + if_handled.Else(); + { + // Fallback to the runtime to add the two strings. + Add(left); + Add(right); + Push(Add(isolate()->factory()->empty_string(), + Runtime::FunctionForId(Runtime::kStringAdd), + 2)); + } + if_handled.End(); + + return Pop(); +} + + +HValue* HGraphBuilder::BuildStringAdd(HValue* left, + HValue* right, + PretenureFlag pretenure_flag) { + // Determine the string lengths. + HValue* left_length = Add( + left, HObjectAccess::ForStringLength()); + HValue* right_length = Add( + right, HObjectAccess::ForStringLength()); + + // Check if left string is empty. + IfBuilder if_leftisempty(this); + if_leftisempty.If( + left_length, graph()->GetConstant0(), Token::EQ); + if_leftisempty.Then(); + { + // Count the native string addition. + AddIncrementCounter(isolate()->counters()->string_add_native()); + + // Just return the right string. + Push(right); + } + if_leftisempty.Else(); + { + // Check if right string is empty. + IfBuilder if_rightisempty(this); + if_rightisempty.If( + right_length, graph()->GetConstant0(), Token::EQ); + if_rightisempty.Then(); + { + // Count the native string addition. + AddIncrementCounter(isolate()->counters()->string_add_native()); + + // Just return the left string. + Push(left); + } + if_rightisempty.Else(); + { + // Concatenate the two non-empty strings. + Push(BuildUncheckedStringAdd(left, right, pretenure_flag)); + } + if_rightisempty.End(); + } + if_leftisempty.End(); + + return Pop(); +} + + HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess( HValue* checked_object, HValue* key, diff --git a/src/hydrogen.h b/src/hydrogen.h index 8f4878d..b3cb8ff 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -1276,6 +1276,26 @@ class HGraphBuilder { HValue* BuildNumberToString(HValue* object, Handle type); + // Computes the size for a sequential string of the given length and encoding. + HValue* BuildSeqStringSizeFor(HValue* length, + String::Encoding encoding); + // Copies characters from one sequential string to another. + void BuildCopySeqStringChars(HValue* src, + HValue* src_offset, + String::Encoding src_encoding, + HValue* dst, + HValue* dst_offset, + String::Encoding dst_encoding, + HValue* length); + // Both operands are non-empty strings. + HValue* BuildUncheckedStringAdd(HValue* left, + HValue* right, + PretenureFlag pretenure_flag); + // Both operands are strings. + HValue* BuildStringAdd(HValue* left, + HValue* right, + PretenureFlag pretenure_flag); + HInstruction* BuildUncheckedMonomorphicElementAccess( HValue* checked_object, HValue* key, @@ -1298,7 +1318,13 @@ class HGraphBuilder { HLoadNamedField* BuildLoadNamedField(HValue* object, HObjectAccess access); HInstruction* AddLoadNamedField(HValue* object, HObjectAccess access); HInstruction* BuildLoadStringLength(HValue* object, HValue* checked_value); - HStoreNamedField* AddStoreMapConstant(HValue* object, Handle); + HStoreNamedField* AddStoreMapConstant(HValue* object, Handle map); + HStoreNamedField* AddStoreMapConstantNoWriteBarrier(HValue* object, + Handle map) { + HStoreNamedField* store_map = AddStoreMapConstant(object, map); + store_map->SkipWriteBarrier(); + return store_map; + } HLoadNamedField* AddLoadElements(HValue* object); bool MatchRotateRight(HValue* left, diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc index 941bb32..264e300 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -306,6 +306,17 @@ void BinaryOpStub::InitializeInterfaceDescriptor( } +void NewStringAddStub::InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) { + static Register registers[] = { edx, eax }; + descriptor->register_param_count_ = 2; + descriptor->register_params_ = registers; + descriptor->deoptimization_handler_ = + Runtime::FunctionForId(Runtime::kStringAdd)->entry; +} + + #define __ ACCESS_MASM(masm) diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index b7ddeac..f3ac9c5 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -3671,11 +3671,20 @@ void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { ZoneList* args = expr->arguments(); ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); + if (FLAG_new_string_add) { + VisitForStackValue(args->at(0)); + VisitForAccumulatorValue(args->at(1)); - StringAddStub stub(STRING_ADD_CHECK_BOTH); - __ CallStub(&stub); + __ pop(edx); + NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED); + __ CallStub(&stub); + } else { + VisitForStackValue(args->at(0)); + VisitForStackValue(args->at(1)); + + StringAddStub stub(STRING_ADD_CHECK_BOTH); + __ CallStub(&stub); + } context()->Plug(eax); } diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc index 89df802..afdf480 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -4551,6 +4551,10 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { if (operand_value->IsRegister()) { Register value = ToRegister(operand_value); __ Store(value, operand, representation); + } else if (representation.IsInteger32()) { + Immediate immediate = ToImmediate(operand_value, representation); + ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); + __ mov(operand, immediate); } else { Handle handle_value = ToHandle(operand_value); ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); @@ -4997,10 +5001,18 @@ void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) { void LCodeGen::DoStringAdd(LStringAdd* instr) { ASSERT(ToRegister(instr->context()).is(esi)); - EmitPushTaggedOperand(instr->left()); - EmitPushTaggedOperand(instr->right()); - StringAddStub stub(instr->hydrogen()->flags()); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + if (FLAG_new_string_add) { + ASSERT(ToRegister(instr->left()).is(edx)); + ASSERT(ToRegister(instr->right()).is(eax)); + NewStringAddStub stub(instr->hydrogen()->flags(), + isolate()->heap()->GetPretenureMode()); + CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + } else { + EmitPushTaggedOperand(instr->left()); + EmitPushTaggedOperand(instr->right()); + StringAddStub stub(instr->hydrogen()->flags()); + CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + } } diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index 632b065..2b1a025 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -2485,8 +2485,12 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) { LOperand* context = UseFixed(instr->context(), esi); - LOperand* left = UseOrConstantAtStart(instr->left()); - LOperand* right = UseOrConstantAtStart(instr->right()); + LOperand* left = FLAG_new_string_add + ? UseFixed(instr->left(), edx) + : UseOrConstantAtStart(instr->left()); + LOperand* right = FLAG_new_string_add + ? UseFixed(instr->right(), eax) + : UseOrConstantAtStart(instr->right()); LStringAdd* string_add = new(zone()) LStringAdd(context, left, right); return MarkAsCall(DefineFixed(string_add, eax), instr); } diff --git a/src/isolate.cc b/src/isolate.cc index 0e764bf..740cce0 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -2328,6 +2328,7 @@ bool Isolate::Init(Deserializer* des) { InternalArrayConstructorStubBase::InstallDescriptors(this); FastNewClosureStub::InstallDescriptors(this); NumberToStringStub::InstallDescriptors(this); + NewStringAddStub::InstallDescriptors(this); } if (FLAG_sweeper_threads > 0) { diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc index 7a7c0a7..0912661 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -302,6 +302,17 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor( } +void NewStringAddStub::InitializeInterfaceDescriptor( + Isolate* isolate, + CodeStubInterfaceDescriptor* descriptor) { + static Register registers[] = { rdx, rax }; + descriptor->register_param_count_ = 2; + descriptor->register_params_ = registers; + descriptor->deoptimization_handler_ = + Runtime::FunctionForId(Runtime::kStringAdd)->entry; +} + + #define __ ACCESS_MASM(masm) diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index cc0d3d9..5a343be 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -3628,11 +3628,20 @@ void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) { ZoneList* args = expr->arguments(); ASSERT_EQ(2, args->length()); - VisitForStackValue(args->at(0)); - VisitForStackValue(args->at(1)); + if (FLAG_new_string_add) { + VisitForStackValue(args->at(0)); + VisitForAccumulatorValue(args->at(1)); - StringAddStub stub(STRING_ADD_CHECK_BOTH); - __ CallStub(&stub); + __ pop(rdx); + NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED); + __ CallStub(&stub); + } else { + VisitForStackValue(args->at(0)); + VisitForStackValue(args->at(1)); + + StringAddStub stub(STRING_ADD_CHECK_BOTH); + __ CallStub(&stub); + } context()->Plug(rax); } diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc index 20b1897..cd29af4 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -4077,6 +4077,10 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) { if (operand_value->IsRegister()) { Register value = ToRegister(operand_value); __ Store(FieldOperand(write_register, offset), value, representation); + } else if (representation.IsInteger32()) { + int32_t value = ToInteger32(operand_value); + ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); + __ movl(FieldOperand(write_register, offset), Immediate(value)); } else { Handle handle_value = ToHandle(operand_value); ASSERT(!instr->hydrogen()->NeedsWriteBarrier()); @@ -4400,10 +4404,18 @@ void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) { void LCodeGen::DoStringAdd(LStringAdd* instr) { ASSERT(ToRegister(instr->context()).is(rsi)); - EmitPushTaggedOperand(instr->left()); - EmitPushTaggedOperand(instr->right()); - StringAddStub stub(instr->hydrogen()->flags()); - CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + if (FLAG_new_string_add) { + ASSERT(ToRegister(instr->left()).is(rdx)); + ASSERT(ToRegister(instr->right()).is(rax)); + NewStringAddStub stub(instr->hydrogen()->flags(), + isolate()->heap()->GetPretenureMode()); + CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + } else { + EmitPushTaggedOperand(instr->left()); + EmitPushTaggedOperand(instr->right()); + StringAddStub stub(instr->hydrogen()->flags()); + CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr); + } } diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 281589e..eec5597 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -2339,8 +2339,12 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) { LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) { LOperand* context = UseFixed(instr->context(), rsi); - LOperand* left = UseOrConstantAtStart(instr->left()); - LOperand* right = UseOrConstantAtStart(instr->right()); + LOperand* left = FLAG_new_string_add + ? UseFixed(instr->left(), rdx) + : UseOrConstantAtStart(instr->left()); + LOperand* right = FLAG_new_string_add + ? UseFixed(instr->right(), rax) + : UseOrConstantAtStart(instr->right()); return MarkAsCall( DefineFixed(new(zone()) LStringAdd(context, left, right), rax), instr); } diff --git a/test/mjsunit/new-string-add.js b/test/mjsunit/new-string-add.js new file mode 100644 index 0000000..f5b7cbf --- /dev/null +++ b/test/mjsunit/new-string-add.js @@ -0,0 +1,197 @@ +// Copyright 2013 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: --new-string-add + +assertEquals("ab", "a" + "b", "ll"); + +assertEquals("12", "1" + "2", "dd"); +assertEquals("123", "1" + "2" + "3", "ddd"); +assertEquals("123", 1 + "2" + "3", "ndd"); +assertEquals("123", "1" + 2 + "3", "dnd"); +assertEquals("123", "1" + "2" + 3, "ddn"); + +assertEquals("123", "1" + 2 + 3, "dnn"); +assertEquals("123", 1 + "2" + 3, "ndn"); +assertEquals("33", 1 + 2 + "3", "nnd"); + +var x = "1"; +assertEquals("12", x + 2, "vn"); +assertEquals("12", x + "2", "vd"); +assertEquals("21", 2 + x, "nv"); +assertEquals("21", "2" + x, "dv"); + +var y = "2"; +assertEquals("12", x + y, "vdvd"); + +x = 1; +assertEquals("12", x + y, "vnvd"); + +y = 2; +assertEquals(3, x + y, "vnvn"); + +x = "1"; +assertEquals("12", x + y, "vdvn"); + +y = "2"; +assertEquals("12", x + y, "vdvd2"); + +(function(x, y) { + var z = "3"; + var w = "4"; + + assertEquals("11", x + x, "xx"); + assertEquals("12", x + y, "xy"); + assertEquals("13", x + z, "xz"); + assertEquals("14", x + w, "xw"); + + assertEquals("21", y + x, "yx"); + assertEquals("22", y + y, "yy"); + assertEquals("23", y + z, "yz"); + assertEquals("24", y + w, "yw"); + + assertEquals("31", z + x, "zx"); + assertEquals("32", z + y, "zy"); + assertEquals("33", z + z, "zz"); + assertEquals("34", z + w, "zw"); + + assertEquals("41", w + x, "wx"); + assertEquals("42", w + y, "wy"); + assertEquals("43", w + z, "wz"); + assertEquals("44", w + w, "ww"); + + (function(){x = 1; z = 3;})(); + + assertEquals(2, x + x, "x'x"); + assertEquals("12", x + y, "x'y"); + assertEquals(4, x + z, "x'z'"); + assertEquals("14", x + w, "x'w"); + + assertEquals("21", y + x, "yx'"); + assertEquals("22", y + y, "yy"); + assertEquals("23", y + z, "yz'"); + assertEquals("24", y + w, "yw"); + + assertEquals(4, z + x, "z'x'"); + assertEquals("32", z + y, "z'y"); + assertEquals(6, z + z, "z'z'"); + assertEquals("34", z + w, "z'w"); + + assertEquals("41", w + x, "wx'"); + assertEquals("42", w + y, "wy"); + assertEquals("43", w + z, "wz'"); + assertEquals("44", w + w, "ww"); +})("1", "2"); + +assertEquals("142", "1" + new Number(42), "sN"); +assertEquals("421", new Number(42) + "1", "Ns"); +assertEquals(84, new Number(42) + new Number(42), "NN"); + +assertEquals("142", "1" + new String("42"), "sS"); +assertEquals("421", new String("42") + "1", "Ss"); +assertEquals("142", "1" + new String("42"), "sS"); +assertEquals("4242", new String("42") + new String("42"), "SS"); + +assertEquals("1true", "1" + true, "sb"); +assertEquals("true1", true + "1", "bs"); +assertEquals(2, true + true, "bs"); + +assertEquals("1true", "1" + new Boolean(true), "sB"); +assertEquals("true1", new Boolean(true) + "1", "Bs"); +assertEquals(2, new Boolean(true) + new Boolean(true), "Bs"); + +assertEquals("1undefined", "1" + void 0, "sv"); +assertEquals("undefined1", (void 0) + "1", "vs"); +assertTrue(isNaN(void 0 + void 0), "vv"); + +assertEquals("1null", "1" + null, "su"); +assertEquals("null1", null + "1", "us"); +assertEquals(0, null + null, "uu"); + +(function (i) { + // Check that incoming frames are merged correctly. + var x; + var y; + var z; + var w; + switch (i) { + case 1: x = 42; y = "stry"; z = "strz"; w = 42; break; + default: x = "strx", y = 42; z = "strz"; w = 42; break; + } + var resxx = x + x; + var resxy = x + y; + var resxz = x + z; + var resxw = x + w; + var resyx = y + x; + var resyy = y + y; + var resyz = y + z; + var resyw = y + w; + var reszx = z + x; + var reszy = z + y; + var reszz = z + z; + var reszw = z + w; + var reswx = w + x; + var reswy = w + y; + var reswz = w + z; + var resww = w + w; + assertEquals(84, resxx, "swxx"); + assertEquals("42stry", resxy, "swxy"); + assertEquals("42strz", resxz, "swxz"); + assertEquals(84, resxw, "swxw"); + assertEquals("stry42", resyx, "swyx"); + assertEquals("strystry", resyy, "swyy"); + assertEquals("strystrz", resyz, "swyz"); + assertEquals("stry42", resyw, "swyw"); + assertEquals("strz42", reszx, "swzx"); + assertEquals("strzstry", reszy, "swzy"); + assertEquals("strzstrz", reszz, "swzz"); + assertEquals("strz42", reszw, "swzw"); + assertEquals(84, reswx, "swwx"); + assertEquals("42stry", reswy, "swwy"); + assertEquals("42strz", reswz, "swwz"); + assertEquals(84, resww, "swww"); +})(1); + +// Generate ascii and non ascii strings from length 0 to 20. +var ascii = 'aaaaaaaaaaaaaaaaaaaa'; +var non_ascii = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234'; +assertEquals(20, ascii.length); +assertEquals(20, non_ascii.length); +var a = Array(21); +var b = Array(21); +for (var i = 0; i <= 20; i++) { + a[i] = ascii.substring(0, i); + b[i] = non_ascii.substring(0, i); +} + +// Add ascii and non-ascii strings generating strings with length from 0 to 20. +for (var i = 0; i <= 20; i++) { + for (var j = 0; j < i; j++) { + assertEquals(a[i], a[j] + a[i - j]) + assertEquals(b[i], b[j] + b[i - j]) + } +}