Add initial hydrogenized NewStringAddStub.
authorbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 12 Nov 2013 10:21:08 +0000 (10:21 +0000)
committerbmeurer@chromium.org <bmeurer@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 12 Nov 2013 10:21:08 +0000 (10:21 +0000)
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

21 files changed:
src/arm/code-stubs-arm.cc
src/arm/full-codegen-arm.cc
src/arm/lithium-arm.cc
src/arm/lithium-codegen-arm.cc
src/code-stubs-hydrogen.cc
src/code-stubs.cc
src/code-stubs.h
src/flag-definitions.h
src/hydrogen-instructions.h
src/hydrogen.cc
src/hydrogen.h
src/ia32/code-stubs-ia32.cc
src/ia32/full-codegen-ia32.cc
src/ia32/lithium-codegen-ia32.cc
src/ia32/lithium-ia32.cc
src/isolate.cc
src/x64/code-stubs-x64.cc
src/x64/full-codegen-x64.cc
src/x64/lithium-codegen-x64.cc
src/x64/lithium-x64.cc
test/mjsunit/new-string-add.js [new file with mode: 0644]

index 95a04f7..2607f9e 100644 (file)
@@ -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)
 
 
index 8fb1e15..28d8184 100644 (file)
@@ -3705,11 +3705,21 @@ void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) {
 void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
   ZoneList<Expression*>* 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);
 }
 
index 5fb0e53..6d02ba1 100644 (file)
@@ -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);
index 8049b68..348b8ce 100644 (file)
@@ -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);
+  }
 }
 
 
index 19b6088..76786d1 100644 (file)
@@ -972,6 +972,38 @@ Handle<Code> BinaryOpStub::GenerateCode(Isolate* isolate) {
 
 
 template <>
+HValue* CodeStubGraphBuilder<NewStringAddStub>::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<HIsStringAndBranch>(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<HIsStringAndBranch>(right);
+    if_rightnotstring.Then();
+    if_rightnotstring.Deopt("Expected string for RHS of string addition");
+  }
+
+  return BuildStringAdd(left, right, pretenure_flag);
+}
+
+
+Handle<Code> NewStringAddStub::GenerateCode(Isolate* isolate) {
+  return DoGenerateCode(isolate, this);
+}
+
+
+template <>
 HValue* CodeStubGraphBuilder<ToBooleanStub>::BuildCodeInitializedStub() {
   ToBooleanStub* stub = casted_stub();
 
index afa3cd0..e76bed3 100644 (file)
@@ -682,6 +682,21 @@ Handle<Type> 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);
index 3dc32e8..b56a3ec 100644 (file)
@@ -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<Code> 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<StringAddFlags, 0, 2> {};
+  class PretenureFlagBits: public BitField<PretenureFlag, 2, 1> {};
+  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,
index f0444d1..1781f7f 100644 (file)
@@ -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")
index b9c0ca9..d2ae975 100644 (file)
@@ -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);
   }
index 147563e..c09cd6d 100644 (file)
@@ -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<HShl>(length, graph()->GetConstant1());
+    size->ClearFlag(HValue::kCanOverflow);
+    size->SetFlag(HValue::kUint32);
+  }
+  size = Add<HAdd>(size, Add<HConstant>(static_cast<int32_t>(
+              SeqString::kHeaderSize + kObjectAlignmentMask)));
+  size->ClearFlag(HValue::kCanOverflow);
+  size = Add<HBitwise>(
+      Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>(
+              ~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<HAdd>(src_offset, index);
+    HValue* value = Add<HSeqStringGetChar>(src_encoding, src, src_index);
+    HValue* dst_index = Add<HAdd>(dst_offset, index);
+    Add<HSeqStringSetChar>(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<HLoadNamedField>(
+      left, HObjectAccess::ForStringLength());
+  HValue* right_length = Add<HLoadNamedField>(
+      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<HConstant>(String::kMaxLength / 2);
+  IfBuilder if_nooverflow(this);
+  if_nooverflow.If<HCompareNumericAndBranch>(
+      left_length, max_length, Token::LTE);
+  if_nooverflow.AndIf<HCompareNumericAndBranch>(
+      right_length, max_length, Token::LTE);
+  if_nooverflow.Then();
+  {
+    // Determine the string instance types.
+    HLoadNamedField* left_instance_type = Add<HLoadNamedField>(
+        Add<HLoadNamedField>(left, HObjectAccess::ForMap()),
+        HObjectAccess::ForMapInstanceType());
+    HLoadNamedField* right_instance_type = Add<HLoadNamedField>(
+        Add<HLoadNamedField>(right, HObjectAccess::ForMap()),
+        HObjectAccess::ForMapInstanceType());
+
+    // Compute difference of instance types.
+    HValue* xored_instance_types = Add<HBitwise>(
+        Token::BIT_XOR, left_instance_type, right_instance_type);
+
+    // Compute the length of the resulting string.
+    HValue* length = Add<HAdd>(left_length, right_length);
+
+    // Check if we should create a cons string.
+    IfBuilder if_createcons(this);
+    if_createcons.If<HCompareNumericAndBranch>(
+        length, Add<HConstant>(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<HAllocate>(Add<HConstant>(ConsString::kSize),
+                                         HType::String(), pretenure_flag,
+                                         CONS_STRING_TYPE);
+
+      // Compute the intersection of instance types.
+      HValue* anded_instance_types = Add<HBitwise>(
+          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<HCompareNumericAndBranch>(
+          Add<HBitwise>(
+              Token::BIT_AND, anded_instance_types,
+              Add<HConstant>(static_cast<int32_t>(
+                      kStringEncodingMask | kOneByteDataHintMask))),
+          graph()->GetConstant0(), Token::NE);
+      if_onebyte.Or();
+      STATIC_ASSERT(kOneByteStringTag != 0 &&
+                    kOneByteDataHintTag != 0 &&
+                    kOneByteDataHintTag != kOneByteStringTag);
+      if_onebyte.If<HCompareNumericAndBranch>(
+          Add<HBitwise>(
+              Token::BIT_AND, xored_instance_types,
+              Add<HConstant>(static_cast<int32_t>(
+                      kOneByteStringTag | kOneByteDataHintTag))),
+          Add<HConstant>(static_cast<int32_t>(
+                  kOneByteStringTag | kOneByteDataHintTag)), Token::EQ);
+      if_onebyte.Then();
+      {
+        // We can safely skip the write barrier for storing the map here.
+        Handle<Map> 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> map = isolate()->factory()->cons_string_map();
+        AddStoreMapConstantNoWriteBarrier(string, map);
+      }
+      if_onebyte.End();
+
+      // Initialize the cons string fields.
+      Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
+                            Add<HConstant>(String::kEmptyHashField));
+      Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(), length);
+      Add<HStoreNamedField>(string, HObjectAccess::ForConsStringFirst(), left);
+      Add<HStoreNamedField>(string, HObjectAccess::ForConsStringSecond(),
+                            right);
+
+      // Cons string is result.
+      Push(string);
+    }
+    if_createcons.Else();
+    {
+      // Compute union of instance types.
+      HValue* ored_instance_types = Add<HBitwise>(
+          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<HCompareNumericAndBranch>(
+          Add<HBitwise>(
+              Token::BIT_AND, xored_instance_types,
+              Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
+          graph()->GetConstant0(), Token::EQ);
+      if_sameencodingandsequential.And();
+      STATIC_ASSERT(kSeqStringTag == 0);
+      if_sameencodingandsequential.If<HCompareNumericAndBranch>(
+          Add<HBitwise>(
+              Token::BIT_AND, ored_instance_types,
+              Add<HConstant>(static_cast<int32_t>(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<HCompareNumericAndBranch>(
+            Add<HBitwise>(
+                Token::BIT_AND, ored_instance_types,
+                Add<HConstant>(static_cast<int32_t>(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> map = isolate()->factory()->ascii_string_map();
+          HAllocate* string = Add<HAllocate>(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> map = isolate()->factory()->string_map();
+          HAllocate* string = Add<HAllocate>(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<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
+                              Add<HConstant>(String::kEmptyHashField));
+        Add<HStoreNamedField>(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<HPushArgument>(left);
+    Add<HPushArgument>(right);
+    Push(Add<HCallRuntime>(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<HLoadNamedField>(
+      left, HObjectAccess::ForStringLength());
+  HValue* right_length = Add<HLoadNamedField>(
+      right, HObjectAccess::ForStringLength());
+
+  // Check if left string is empty.
+  IfBuilder if_leftisempty(this);
+  if_leftisempty.If<HCompareNumericAndBranch>(
+      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<HCompareNumericAndBranch>(
+        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,
index 8f4878d..b3cb8ff 100644 (file)
@@ -1276,6 +1276,26 @@ class HGraphBuilder {
 
   HValue* BuildNumberToString(HValue* object, Handle<Type> 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<Map>);
+  HStoreNamedField* AddStoreMapConstant(HValue* object, Handle<Map> map);
+  HStoreNamedField* AddStoreMapConstantNoWriteBarrier(HValue* object,
+                                                      Handle<Map> map) {
+    HStoreNamedField* store_map = AddStoreMapConstant(object, map);
+    store_map->SkipWriteBarrier();
+    return store_map;
+  }
   HLoadNamedField* AddLoadElements(HValue* object);
 
   bool MatchRotateRight(HValue* left,
index 941bb32..264e300 100644 (file)
@@ -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)
 
 
index b7ddeac..f3ac9c5 100644 (file)
@@ -3671,11 +3671,20 @@ void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
   ZoneList<Expression*>* 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);
 }
 
index 89df802..afdf480 100644 (file)
@@ -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<Object> 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);
+  }
 }
 
 
index 632b065..2b1a025 100644 (file)
@@ -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);
 }
index 0e764bf..740cce0 100644 (file)
@@ -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) {
index 7a7c0a7..0912661 100644 (file)
@@ -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)
 
 
index cc0d3d9..5a343be 100644 (file)
@@ -3628,11 +3628,20 @@ void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
   ZoneList<Expression*>* 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);
 }
 
index 20b1897..cd29af4 100644 (file)
@@ -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<Object> 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);
+  }
 }
 
 
index 281589e..eec5597 100644 (file)
@@ -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 (file)
index 0000000..f5b7cbf
--- /dev/null
@@ -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])
+  }
+}