| Index: src/code-stub-assembler.cc
 | 
| diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc
 | 
| index 53b9835ab46eb2334bbb526d346c290eeb5a05d5..52a64bc4e78511d1b31010b9a739d6b5ab37f76c 100644
 | 
| --- a/src/code-stub-assembler.cc
 | 
| +++ b/src/code-stub-assembler.cc
 | 
| @@ -2904,6 +2904,22 @@ Node* CodeStubAssembler::IsSequentialStringInstanceType(Node* instance_type) {
 | 
|        Int32Constant(kSeqStringTag));
 | 
|  }
 | 
|  
 | 
| +Node* CodeStubAssembler::IsExternalStringInstanceType(Node* instance_type) {
 | 
| +  CSA_ASSERT(this, IsStringInstanceType(instance_type));
 | 
| +  return Word32Equal(
 | 
| +      Word32And(instance_type, Int32Constant(kStringRepresentationMask)),
 | 
| +      Int32Constant(kExternalStringTag));
 | 
| +}
 | 
| +
 | 
| +Node* CodeStubAssembler::IsShortExternalStringInstanceType(
 | 
| +    Node* instance_type) {
 | 
| +  CSA_ASSERT(this, IsStringInstanceType(instance_type));
 | 
| +  STATIC_ASSERT(kShortExternalStringTag != 0);
 | 
| +  return Word32NotEqual(
 | 
| +      Word32And(instance_type, Int32Constant(kShortExternalStringMask)),
 | 
| +      Int32Constant(0));
 | 
| +}
 | 
| +
 | 
|  Node* CodeStubAssembler::IsJSReceiverInstanceType(Node* instance_type) {
 | 
|    STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
 | 
|    return Int32GreaterThanOrEqual(instance_type,
 | 
| @@ -3013,188 +3029,58 @@ Node* CodeStubAssembler::IsJSFunction(Node* object) {
 | 
|  Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index,
 | 
|                                            ParameterMode parameter_mode) {
 | 
|    CSA_ASSERT(this, IsString(string));
 | 
| +
 | 
|    // Translate the {index} into a Word.
 | 
| -  index = ParameterToWord(index, parameter_mode);
 | 
| +  Node* const int_index = ParameterToWord(index, parameter_mode);
 | 
|  
 | 
| -  // We may need to loop in case of cons, thin, or sliced strings.
 | 
| -  Variable var_index(this, MachineType::PointerRepresentation(), index);
 | 
| -  Variable var_string(this, MachineRepresentation::kTagged, string);
 | 
|    Variable var_result(this, MachineRepresentation::kWord32);
 | 
| -  Variable* loop_vars[] = {&var_index, &var_string};
 | 
| -  Label done_loop(this, &var_result), loop(this, 2, loop_vars);
 | 
| -  Goto(&loop);
 | 
| -  Bind(&loop);
 | 
| -  {
 | 
| -    // Load the current {index}.
 | 
| -    index = var_index.value();
 | 
|  
 | 
| -    // Load the current {string}.
 | 
| -    string = var_string.value();
 | 
| +  Label out(this, &var_result), runtime_generic(this), runtime_external(this);
 | 
|  
 | 
| -    // Load the instance type of the {string}.
 | 
| -    Node* string_instance_type = LoadInstanceType(string);
 | 
| +  ToDirectStringAssembler to_direct(state(), string);
 | 
| +  Node* const direct_string = to_direct.TryToDirect(&runtime_generic);
 | 
| +  Node* const offset = IntPtrAdd(int_index, to_direct.offset());
 | 
| +  Node* const instance_type = to_direct.instance_type();
 | 
|  
 | 
| -    // Check if the {string} is a SeqString.
 | 
| -    Label if_stringissequential(this), if_stringisnotsequential(this);
 | 
| -    Branch(Word32Equal(Word32And(string_instance_type,
 | 
| -                                 Int32Constant(kStringRepresentationMask)),
 | 
| -                       Int32Constant(kSeqStringTag)),
 | 
| -           &if_stringissequential, &if_stringisnotsequential);
 | 
| +  Node* const string_data = to_direct.PointerToData(&runtime_external);
 | 
|  
 | 
| -    Bind(&if_stringissequential);
 | 
| -    {
 | 
| -      // Check if the {string} is a TwoByteSeqString or a OneByteSeqString.
 | 
| -      Label if_stringistwobyte(this), if_stringisonebyte(this);
 | 
| -      Branch(Word32Equal(Word32And(string_instance_type,
 | 
| -                                   Int32Constant(kStringEncodingMask)),
 | 
| -                         Int32Constant(kTwoByteStringTag)),
 | 
| -             &if_stringistwobyte, &if_stringisonebyte);
 | 
| -
 | 
| -      Bind(&if_stringisonebyte);
 | 
| -      {
 | 
| -        var_result.Bind(
 | 
| -            Load(MachineType::Uint8(), string,
 | 
| -                 IntPtrAdd(index, IntPtrConstant(SeqOneByteString::kHeaderSize -
 | 
| -                                                 kHeapObjectTag))));
 | 
| -        Goto(&done_loop);
 | 
| -      }
 | 
| -
 | 
| -      Bind(&if_stringistwobyte);
 | 
| -      {
 | 
| -        var_result.Bind(
 | 
| -            Load(MachineType::Uint16(), string,
 | 
| -                 IntPtrAdd(WordShl(index, IntPtrConstant(1)),
 | 
| -                           IntPtrConstant(SeqTwoByteString::kHeaderSize -
 | 
| -                                          kHeapObjectTag))));
 | 
| -        Goto(&done_loop);
 | 
| -      }
 | 
| -    }
 | 
| -
 | 
| -    Bind(&if_stringisnotsequential);
 | 
| -    {
 | 
| -      // Check if the {string} is a ConsString.
 | 
| -      Label if_stringiscons(this), if_stringisnotcons(this);
 | 
| -      Branch(Word32Equal(Word32And(string_instance_type,
 | 
| -                                   Int32Constant(kStringRepresentationMask)),
 | 
| -                         Int32Constant(kConsStringTag)),
 | 
| -             &if_stringiscons, &if_stringisnotcons);
 | 
| -
 | 
| -      Bind(&if_stringiscons);
 | 
| -      {
 | 
| -        // Check whether the right hand side is the empty string (i.e. if
 | 
| -        // this is really a flat string in a cons string). If that is not
 | 
| -        // the case we flatten the string first.
 | 
| -        Label if_rhsisempty(this), if_rhsisnotempty(this, Label::kDeferred);
 | 
| -        Node* rhs = LoadObjectField(string, ConsString::kSecondOffset);
 | 
| -        Branch(WordEqual(rhs, EmptyStringConstant()), &if_rhsisempty,
 | 
| -               &if_rhsisnotempty);
 | 
| -
 | 
| -        Bind(&if_rhsisempty);
 | 
| -        {
 | 
| -          // Just operate on the left hand side of the {string}.
 | 
| -          var_string.Bind(LoadObjectField(string, ConsString::kFirstOffset));
 | 
| -          Goto(&loop);
 | 
| -        }
 | 
| -
 | 
| -        Bind(&if_rhsisnotempty);
 | 
| -        {
 | 
| -          // Flatten the {string} and lookup in the resulting string.
 | 
| -          var_string.Bind(CallRuntime(Runtime::kFlattenString,
 | 
| -                                      NoContextConstant(), string));
 | 
| -          Goto(&loop);
 | 
| -        }
 | 
| -      }
 | 
| +  // Check if the {string} is a TwoByteSeqString or a OneByteSeqString.
 | 
| +  Label if_stringistwobyte(this), if_stringisonebyte(this);
 | 
| +  Branch(IsOneByteStringInstanceType(instance_type), &if_stringisonebyte,
 | 
| +         &if_stringistwobyte);
 | 
|  
 | 
| -      Bind(&if_stringisnotcons);
 | 
| -      {
 | 
| -        // Check if the {string} is an ExternalString.
 | 
| -        Label if_stringisexternal(this), if_stringisnotexternal(this);
 | 
| -        Branch(Word32Equal(Word32And(string_instance_type,
 | 
| -                                     Int32Constant(kStringRepresentationMask)),
 | 
| -                           Int32Constant(kExternalStringTag)),
 | 
| -               &if_stringisexternal, &if_stringisnotexternal);
 | 
| -
 | 
| -        Bind(&if_stringisexternal);
 | 
| -        {
 | 
| -          // Check if the {string} is a short external string.
 | 
| -          Label if_stringisnotshort(this),
 | 
| -              if_stringisshort(this, Label::kDeferred);
 | 
| -          Branch(Word32Equal(Word32And(string_instance_type,
 | 
| -                                       Int32Constant(kShortExternalStringMask)),
 | 
| -                             Int32Constant(0)),
 | 
| -                 &if_stringisnotshort, &if_stringisshort);
 | 
| -
 | 
| -          Bind(&if_stringisnotshort);
 | 
| -          {
 | 
| -            // Load the actual resource data from the {string}.
 | 
| -            Node* string_resource_data =
 | 
| -                LoadObjectField(string, ExternalString::kResourceDataOffset,
 | 
| -                                MachineType::Pointer());
 | 
| -
 | 
| -            // Check if the {string} is a TwoByteExternalString or a
 | 
| -            // OneByteExternalString.
 | 
| -            Label if_stringistwobyte(this), if_stringisonebyte(this);
 | 
| -            Branch(Word32Equal(Word32And(string_instance_type,
 | 
| -                                         Int32Constant(kStringEncodingMask)),
 | 
| -                               Int32Constant(kTwoByteStringTag)),
 | 
| -                   &if_stringistwobyte, &if_stringisonebyte);
 | 
| -
 | 
| -            Bind(&if_stringisonebyte);
 | 
| -            {
 | 
| -              var_result.Bind(
 | 
| -                  Load(MachineType::Uint8(), string_resource_data, index));
 | 
| -              Goto(&done_loop);
 | 
| -            }
 | 
| +  Bind(&if_stringisonebyte);
 | 
| +  {
 | 
| +    var_result.Bind(Load(MachineType::Uint8(), string_data, offset));
 | 
| +    Goto(&out);
 | 
| +  }
 | 
|  
 | 
| -            Bind(&if_stringistwobyte);
 | 
| -            {
 | 
| -              var_result.Bind(Load(MachineType::Uint16(), string_resource_data,
 | 
| -                                   WordShl(index, IntPtrConstant(1))));
 | 
| -              Goto(&done_loop);
 | 
| -            }
 | 
| -          }
 | 
| +  Bind(&if_stringistwobyte);
 | 
| +  {
 | 
| +    var_result.Bind(Load(MachineType::Uint16(), string_data,
 | 
| +                         WordShl(offset, IntPtrConstant(1))));
 | 
| +    Goto(&out);
 | 
| +  }
 | 
|  
 | 
| -          Bind(&if_stringisshort);
 | 
| -          {
 | 
| -            // The {string} might be compressed, call the runtime.
 | 
| -            var_result.Bind(SmiToWord32(
 | 
| -                CallRuntime(Runtime::kExternalStringGetChar,
 | 
| -                            NoContextConstant(), string, SmiTag(index))));
 | 
| -            Goto(&done_loop);
 | 
| -          }
 | 
| -        }
 | 
| +  Bind(&runtime_generic);
 | 
| +  {
 | 
| +    Node* const smi_index = ParameterToTagged(index, parameter_mode);
 | 
| +    Node* const result = CallRuntime(Runtime::kStringCharCodeAtRT,
 | 
| +                                     NoContextConstant(), string, smi_index);
 | 
| +    var_result.Bind(SmiToWord32(result));
 | 
| +    Goto(&out);
 | 
| +  }
 | 
|  
 | 
| -        Bind(&if_stringisnotexternal);
 | 
| -        {
 | 
| -          Label if_stringissliced(this), if_stringisthin(this);
 | 
| -          Branch(
 | 
| -              Word32Equal(Word32And(string_instance_type,
 | 
| -                                    Int32Constant(kStringRepresentationMask)),
 | 
| -                          Int32Constant(kSlicedStringTag)),
 | 
| -              &if_stringissliced, &if_stringisthin);
 | 
| -          Bind(&if_stringissliced);
 | 
| -          {
 | 
| -            // The {string} is a SlicedString, continue with its parent.
 | 
| -            Node* string_offset =
 | 
| -                LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
 | 
| -            Node* string_parent =
 | 
| -                LoadObjectField(string, SlicedString::kParentOffset);
 | 
| -            var_index.Bind(IntPtrAdd(index, string_offset));
 | 
| -            var_string.Bind(string_parent);
 | 
| -            Goto(&loop);
 | 
| -          }
 | 
| -          Bind(&if_stringisthin);
 | 
| -          {
 | 
| -            // The {string} is a ThinString, continue with its actual value.
 | 
| -            var_string.Bind(LoadObjectField(string, ThinString::kActualOffset));
 | 
| -            Goto(&loop);
 | 
| -          }
 | 
| -        }
 | 
| -      }
 | 
| -    }
 | 
| +  Bind(&runtime_external);
 | 
| +  {
 | 
| +    Node* const result =
 | 
| +        CallRuntime(Runtime::kExternalStringGetChar, NoContextConstant(),
 | 
| +                    direct_string, SmiTag(offset));
 | 
| +    var_result.Bind(SmiToWord32(result));
 | 
| +    Goto(&out);
 | 
|    }
 | 
|  
 | 
| -  Bind(&done_loop);
 | 
| +  Bind(&out);
 | 
|    return var_result.value();
 | 
|  }
 | 
|  
 | 
| @@ -3313,27 +3199,14 @@ Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context,
 | 
|  
 | 
|  Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
 | 
|                                     Node* to) {
 | 
| -  Label end(this);
 | 
| -  Label runtime(this);
 | 
| -
 | 
| -  Node* const int_zero = Int32Constant(0);
 | 
| -
 | 
| -  // Int32 variables.
 | 
| -  Variable var_instance_type(this, MachineRepresentation::kWord32, int_zero);
 | 
| -  Variable var_representation(this, MachineRepresentation::kWord32, int_zero);
 | 
| -
 | 
| -  Variable var_from(this, MachineRepresentation::kTagged, from);      // Smi.
 | 
| -  Variable var_string(this, MachineRepresentation::kTagged, string);  // String.
 | 
| -  Variable var_result(this, MachineRepresentation::kTagged);          // String.
 | 
| +  Variable var_result(this, MachineRepresentation::kTagged);
 | 
| +  ToDirectStringAssembler to_direct(state(), string);
 | 
| +  Label end(this), runtime(this);
 | 
|  
 | 
|    // Make sure first argument is a string.
 | 
|    CSA_ASSERT(this, TaggedIsNotSmi(string));
 | 
|    CSA_ASSERT(this, IsString(string));
 | 
|  
 | 
| -  // Load the instance type of the {string}.
 | 
| -  Node* const instance_type = LoadInstanceType(string);
 | 
| -  var_instance_type.Bind(instance_type);
 | 
| -
 | 
|    // Make sure that both from and to are non-negative smis.
 | 
|  
 | 
|    GotoIfNot(TaggedIsPositiveSmi(from), &runtime);
 | 
| @@ -3356,132 +3229,56 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
 | 
|    // TODO(jgruber): Add an additional case for substring of length == 0?
 | 
|  
 | 
|    // Deal with different string types: update the index if necessary
 | 
| -  // and put the underlying string into var_string.
 | 
| -
 | 
| -  // If the string is not indirect, it can only be sequential or external.
 | 
| -  STATIC_ASSERT(kIsIndirectStringMask ==
 | 
| -                (kSlicedStringTag & kConsStringTag & kThinStringTag));
 | 
| -  STATIC_ASSERT(kIsIndirectStringMask != 0);
 | 
| -  Label underlying_unpacked(this);
 | 
| -  GotoIf(Word32Equal(
 | 
| -             Word32And(instance_type, Int32Constant(kIsIndirectStringMask)),
 | 
| -             Int32Constant(0)),
 | 
| -         &underlying_unpacked);
 | 
| -
 | 
| -  // The subject string is a sliced, cons, or thin string.
 | 
| -
 | 
| -  Label thin_string(this), thin_or_sliced(this);
 | 
| -  var_representation.Bind(
 | 
| -      Word32And(instance_type, Int32Constant(kStringRepresentationMask)));
 | 
| -  GotoIf(
 | 
| -      Word32NotEqual(var_representation.value(), Int32Constant(kConsStringTag)),
 | 
| -      &thin_or_sliced);
 | 
| +  // and extract the underlying string.
 | 
|  
 | 
| -  // Cons string.  Check whether it is flat, then fetch first part.
 | 
| -  // Flat cons strings have an empty second part.
 | 
| -  {
 | 
| -    GotoIf(WordNotEqual(LoadObjectField(string, ConsString::kSecondOffset),
 | 
| -                        EmptyStringConstant()),
 | 
| -           &runtime);
 | 
| -
 | 
| -    Node* first_string_part = LoadObjectField(string, ConsString::kFirstOffset);
 | 
| -    var_string.Bind(first_string_part);
 | 
| -    var_instance_type.Bind(LoadInstanceType(first_string_part));
 | 
| -    var_representation.Bind(Word32And(
 | 
| -        var_instance_type.value(), Int32Constant(kStringRepresentationMask)));
 | 
| -
 | 
| -    // The loaded first part might be a thin string.
 | 
| -    Branch(Word32Equal(Word32And(var_instance_type.value(),
 | 
| -                                 Int32Constant(kIsIndirectStringMask)),
 | 
| -                       Int32Constant(0)),
 | 
| -           &underlying_unpacked, &thin_string);
 | 
| -  }
 | 
| -
 | 
| -  Bind(&thin_or_sliced);
 | 
| -  {
 | 
| -    GotoIf(
 | 
| -        Word32Equal(var_representation.value(), Int32Constant(kThinStringTag)),
 | 
| -        &thin_string);
 | 
| -    // Otherwise it's a sliced string.
 | 
| -    // Fetch parent and correct start index by offset.
 | 
| -    Node* sliced_offset =
 | 
| -        LoadObjectField(var_string.value(), SlicedString::kOffsetOffset);
 | 
| -    var_from.Bind(SmiAdd(from, sliced_offset));
 | 
| -
 | 
| -    Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset);
 | 
| -    var_string.Bind(slice_parent);
 | 
| -
 | 
| -    Node* slice_parent_instance_type = LoadInstanceType(slice_parent);
 | 
| -    var_instance_type.Bind(slice_parent_instance_type);
 | 
| -
 | 
| -    // The loaded parent might be a thin string.
 | 
| -    Branch(Word32Equal(Word32And(var_instance_type.value(),
 | 
| -                                 Int32Constant(kIsIndirectStringMask)),
 | 
| -                       Int32Constant(0)),
 | 
| -           &underlying_unpacked, &thin_string);
 | 
| -  }
 | 
| -
 | 
| -  Bind(&thin_string);
 | 
| -  {
 | 
| -    Node* actual_string =
 | 
| -        LoadObjectField(var_string.value(), ThinString::kActualOffset);
 | 
| -    var_string.Bind(actual_string);
 | 
| -    var_instance_type.Bind(LoadInstanceType(actual_string));
 | 
| -    Goto(&underlying_unpacked);
 | 
| -  }
 | 
| +  Node* const direct_string = to_direct.TryToDirect(&runtime);
 | 
| +  Node* const offset = SmiAdd(from, SmiTag(to_direct.offset()));
 | 
| +  Node* const instance_type = to_direct.instance_type();
 | 
|  
 | 
|    // The subject string can only be external or sequential string of either
 | 
|    // encoding at this point.
 | 
|    Label external_string(this);
 | 
| -  Bind(&underlying_unpacked);
 | 
|    {
 | 
|      if (FLAG_string_slices) {
 | 
| -      Label copy_routine(this);
 | 
| +      Label next(this);
 | 
|  
 | 
|        // Short slice.  Copy instead of slicing.
 | 
|        GotoIf(SmiLessThan(substr_length,
 | 
|                           SmiConstant(Smi::FromInt(SlicedString::kMinLength))),
 | 
| -             ©_routine);
 | 
| +             &next);
 | 
|  
 | 
|        // Allocate new sliced string.
 | 
|  
 | 
| -      Label two_byte_slice(this);
 | 
| -      STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
 | 
| -      STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
 | 
| -
 | 
|        Counters* counters = isolate()->counters();
 | 
|        IncrementCounter(counters->sub_string_native(), 1);
 | 
|  
 | 
| -      GotoIf(Word32Equal(Word32And(var_instance_type.value(),
 | 
| -                                   Int32Constant(kStringEncodingMask)),
 | 
| -                         Int32Constant(0)),
 | 
| -             &two_byte_slice);
 | 
| +      Label one_byte_slice(this), two_byte_slice(this);
 | 
| +      Branch(IsOneByteStringInstanceType(to_direct.instance_type()),
 | 
| +             &one_byte_slice, &two_byte_slice);
 | 
|  
 | 
| -      var_result.Bind(AllocateSlicedOneByteString(
 | 
| -          substr_length, var_string.value(), var_from.value()));
 | 
| -      Goto(&end);
 | 
| +      Bind(&one_byte_slice);
 | 
| +      {
 | 
| +        var_result.Bind(
 | 
| +            AllocateSlicedOneByteString(substr_length, direct_string, offset));
 | 
| +        Goto(&end);
 | 
| +      }
 | 
|  
 | 
|        Bind(&two_byte_slice);
 | 
| +      {
 | 
| +        var_result.Bind(
 | 
| +            AllocateSlicedTwoByteString(substr_length, direct_string, offset));
 | 
| +        Goto(&end);
 | 
| +      }
 | 
|  
 | 
| -      var_result.Bind(AllocateSlicedTwoByteString(
 | 
| -          substr_length, var_string.value(), var_from.value()));
 | 
| -      Goto(&end);
 | 
| -
 | 
| -      Bind(©_routine);
 | 
| +      Bind(&next);
 | 
|      }
 | 
|  
 | 
|      // The subject string can only be external or sequential string of either
 | 
|      // encoding at this point.
 | 
| -    STATIC_ASSERT(kExternalStringTag != 0);
 | 
| -    STATIC_ASSERT(kSeqStringTag == 0);
 | 
| -    GotoIfNot(Word32Equal(Word32And(var_instance_type.value(),
 | 
| -                                    Int32Constant(kExternalStringTag)),
 | 
| -                          Int32Constant(0)),
 | 
| -              &external_string);
 | 
| +    GotoIf(to_direct.is_external(), &external_string);
 | 
|  
 | 
|      var_result.Bind(AllocAndCopyStringCharacters(
 | 
| -        this, context, var_string.value(), var_instance_type.value(),
 | 
| -        var_from.value(), substr_length));
 | 
| +        this, context, direct_string, instance_type, offset, substr_length));
 | 
|  
 | 
|      Counters* counters = isolate()->counters();
 | 
|      IncrementCounter(counters->sub_string_native(), 1);
 | 
| @@ -3492,12 +3289,11 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
 | 
|    // Handle external string.
 | 
|    Bind(&external_string);
 | 
|    {
 | 
| -    Node* const fake_sequential_string = TryDerefExternalString(
 | 
| -        var_string.value(), var_instance_type.value(), &runtime);
 | 
| +    Node* const fake_sequential_string = to_direct.PointerToString(&runtime);
 | 
|  
 | 
| -    var_result.Bind(AllocAndCopyStringCharacters(
 | 
| -        this, context, fake_sequential_string, var_instance_type.value(),
 | 
| -        var_from.value(), substr_length));
 | 
| +    var_result.Bind(
 | 
| +        AllocAndCopyStringCharacters(this, context, fake_sequential_string,
 | 
| +                                     instance_type, offset, substr_length));
 | 
|  
 | 
|      Counters* counters = isolate()->counters();
 | 
|      IncrementCounter(counters->sub_string_native(), 1);
 | 
| @@ -3508,7 +3304,7 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
 | 
|    // Substrings of length 1 are generated through CharCodeAt and FromCharCode.
 | 
|    Bind(&single_char);
 | 
|    {
 | 
| -    Node* char_code = StringCharCodeAt(var_string.value(), var_from.value());
 | 
| +    Node* char_code = StringCharCodeAt(string, from);
 | 
|      var_result.Bind(StringFromCharCode(char_code));
 | 
|      Goto(&end);
 | 
|    }
 | 
| @@ -3542,45 +3338,30 @@ Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
 | 
|    return var_result.value();
 | 
|  }
 | 
|  
 | 
| -namespace {
 | 
| -
 | 
| -Node* IsExternalStringInstanceType(CodeStubAssembler* a,
 | 
| -                                   Node* const instance_type) {
 | 
| -  CSA_ASSERT(a, a->IsStringInstanceType(instance_type));
 | 
| -  return a->Word32Equal(
 | 
| -      a->Word32And(instance_type, a->Int32Constant(kStringRepresentationMask)),
 | 
| -      a->Int32Constant(kExternalStringTag));
 | 
| -}
 | 
| +ToDirectStringAssembler::ToDirectStringAssembler(
 | 
| +    compiler::CodeAssemblerState* state, Node* string)
 | 
| +    : CodeStubAssembler(state),
 | 
| +      var_string_(this, MachineRepresentation::kTagged, string),
 | 
| +      var_instance_type_(this, MachineRepresentation::kWord32),
 | 
| +      var_offset_(this, MachineType::PointerRepresentation()),
 | 
| +      var_is_external_(this, MachineRepresentation::kWord32) {
 | 
| +  CSA_ASSERT(this, TaggedIsNotSmi(string));
 | 
| +  CSA_ASSERT(this, IsString(string));
 | 
|  
 | 
| -Node* IsShortExternalStringInstanceType(CodeStubAssembler* a,
 | 
| -                                        Node* const instance_type) {
 | 
| -  CSA_ASSERT(a, a->IsStringInstanceType(instance_type));
 | 
| -  STATIC_ASSERT(kShortExternalStringTag != 0);
 | 
| -  return a->Word32NotEqual(
 | 
| -      a->Word32And(instance_type, a->Int32Constant(kShortExternalStringMask)),
 | 
| -      a->Int32Constant(0));
 | 
| +  var_string_.Bind(string);
 | 
| +  var_offset_.Bind(IntPtrConstant(0));
 | 
| +  var_instance_type_.Bind(LoadInstanceType(string));
 | 
| +  var_is_external_.Bind(Int32Constant(0));
 | 
|  }
 | 
|  
 | 
| -}  // namespace
 | 
| -
 | 
| -void CodeStubAssembler::TryUnpackString(Variable* var_string,
 | 
| -                                        Variable* var_offset,
 | 
| -                                        Variable* var_instance_type,
 | 
| -                                        Label* if_bailout) {
 | 
| -  DCHECK_EQ(var_string->rep(), MachineType::PointerRepresentation());
 | 
| -  DCHECK_EQ(var_offset->rep(), MachineType::PointerRepresentation());
 | 
| -  DCHECK_EQ(var_instance_type->rep(), MachineRepresentation::kWord32);
 | 
| -  CSA_ASSERT(this, IsString(var_string->value()));
 | 
| -
 | 
| -  Label out(this);
 | 
| -
 | 
| -  VariableList vars({var_string, var_offset, var_instance_type}, zone());
 | 
| +Node* ToDirectStringAssembler::TryToDirect(Label* if_bailout) {
 | 
| +  VariableList vars({&var_string_, &var_offset_, &var_instance_type_}, zone());
 | 
|    Label dispatch(this, vars);
 | 
| -  Label if_isdirect(this);
 | 
| -  Label if_iscons(this, Label::kDeferred);
 | 
| -  Label if_isexternal(this, Label::kDeferred);
 | 
| -  Label if_issliced(this, Label::kDeferred);
 | 
| -  Label if_isthin(this, Label::kDeferred);
 | 
| +  Label if_iscons(this);
 | 
| +  Label if_isexternal(this);
 | 
| +  Label if_issliced(this);
 | 
| +  Label if_isthin(this);
 | 
| +  Label out(this);
 | 
|  
 | 
|    Goto(&dispatch);
 | 
|  
 | 
| @@ -3592,12 +3373,12 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
 | 
|          kSlicedStringTag, kThinStringTag,
 | 
|      };
 | 
|      Label* labels[] = {
 | 
| -        &if_isdirect, &if_iscons, &if_isexternal, &if_issliced, &if_isthin,
 | 
| +        &out, &if_iscons, &if_isexternal, &if_issliced, &if_isthin,
 | 
|      };
 | 
|      STATIC_ASSERT(arraysize(values) == arraysize(labels));
 | 
|  
 | 
|      Node* const representation = Word32And(
 | 
| -        var_instance_type->value(), Int32Constant(kStringRepresentationMask));
 | 
| +        var_instance_type_.value(), Int32Constant(kStringRepresentationMask));
 | 
|      Switch(representation, if_bailout, values, labels, arraysize(values));
 | 
|    }
 | 
|  
 | 
| @@ -3605,13 +3386,13 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
 | 
|    // Flat cons strings have an empty second part.
 | 
|    Bind(&if_iscons);
 | 
|    {
 | 
| -    Node* const string = var_string->value();
 | 
| +    Node* const string = var_string_.value();
 | 
|      GotoIfNot(IsEmptyString(LoadObjectField(string, ConsString::kSecondOffset)),
 | 
|                if_bailout);
 | 
|  
 | 
|      Node* const lhs = LoadObjectField(string, ConsString::kFirstOffset);
 | 
| -    var_string->Bind(BitcastTaggedToWord(lhs));
 | 
| -    var_instance_type->Bind(LoadInstanceType(lhs));
 | 
| +    var_string_.Bind(lhs);
 | 
| +    var_instance_type_.Bind(LoadInstanceType(lhs));
 | 
|  
 | 
|      Goto(&dispatch);
 | 
|    }
 | 
| @@ -3619,14 +3400,14 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
 | 
|    // Sliced string. Fetch parent and correct start index by offset.
 | 
|    Bind(&if_issliced);
 | 
|    {
 | 
| -    Node* const string = var_string->value();
 | 
| +    Node* const string = var_string_.value();
 | 
|      Node* const sliced_offset =
 | 
| -        LoadObjectField(string, SlicedString::kOffsetOffset);
 | 
| -    var_offset->Bind(IntPtrAdd(var_offset->value(), SmiUntag(sliced_offset)));
 | 
| +        LoadAndUntagObjectField(string, SlicedString::kOffsetOffset);
 | 
| +    var_offset_.Bind(IntPtrAdd(var_offset_.value(), sliced_offset));
 | 
|  
 | 
|      Node* const parent = LoadObjectField(string, SlicedString::kParentOffset);
 | 
| -    var_string->Bind(BitcastTaggedToWord(parent));
 | 
| -    var_instance_type->Bind(LoadInstanceType(parent));
 | 
| +    var_string_.Bind(parent);
 | 
| +    var_instance_type_.Bind(LoadInstanceType(parent));
 | 
|  
 | 
|      Goto(&dispatch);
 | 
|    }
 | 
| @@ -3634,36 +3415,65 @@ void CodeStubAssembler::TryUnpackString(Variable* var_string,
 | 
|    // Thin string. Fetch the actual string.
 | 
|    Bind(&if_isthin);
 | 
|    {
 | 
| -    Node* const string = var_string->value();
 | 
| +    Node* const string = var_string_.value();
 | 
|      Node* const actual_string =
 | 
|          LoadObjectField(string, ThinString::kActualOffset);
 | 
|      Node* const actual_instance_type = LoadInstanceType(actual_string);
 | 
|  
 | 
| -    var_string->Bind(BitcastTaggedToWord(actual_string));
 | 
| -    var_instance_type->Bind(actual_instance_type);
 | 
| +    var_string_.Bind(actual_string);
 | 
| +    var_instance_type_.Bind(actual_instance_type);
 | 
|  
 | 
|      Goto(&dispatch);
 | 
|    }
 | 
|  
 | 
|    // External string.
 | 
|    Bind(&if_isexternal);
 | 
| -  {
 | 
| -    Node* const string = var_string->value();
 | 
| -    Node* const faked_seq_string =
 | 
| -        TryDerefExternalString(string, var_instance_type->value(), if_bailout);
 | 
| +  var_is_external_.Bind(Int32Constant(1));
 | 
| +  Goto(&out);
 | 
|  
 | 
| -    STATIC_ASSERT(kSeqStringTag == 0x0);
 | 
| -    Node* const faked_seq_instance_type = Word32Xor(
 | 
| -        var_instance_type->value(), Int32Constant(kExternalStringTag));
 | 
| -    CSA_ASSERT(this, IsSequentialStringInstanceType(faked_seq_instance_type));
 | 
| +  Bind(&out);
 | 
| +  return var_string_.value();
 | 
| +}
 | 
| +
 | 
| +Node* ToDirectStringAssembler::TryToSequential(StringPointerKind ptr_kind,
 | 
| +                                               Label* if_bailout) {
 | 
| +  CHECK(ptr_kind == PTR_TO_DATA || ptr_kind == PTR_TO_STRING);
 | 
| +
 | 
| +  Variable var_result(this, MachineType::PointerRepresentation());
 | 
| +  Label out(this), if_issequential(this), if_isexternal(this);
 | 
| +  Branch(is_external(), &if_isexternal, &if_issequential);
 | 
| +
 | 
| +  Bind(&if_issequential);
 | 
| +  {
 | 
| +    STATIC_ASSERT(SeqOneByteString::kHeaderSize ==
 | 
| +                  SeqTwoByteString::kHeaderSize);
 | 
| +    Node* result = BitcastTaggedToWord(var_string_.value());
 | 
| +    if (ptr_kind == PTR_TO_DATA) {
 | 
| +      result = IntPtrAdd(result, IntPtrConstant(SeqOneByteString::kHeaderSize -
 | 
| +                                                kHeapObjectTag));
 | 
| +    }
 | 
| +    var_result.Bind(result);
 | 
| +    Goto(&out);
 | 
| +  }
 | 
|  
 | 
| -    var_string->Bind(faked_seq_string);
 | 
| -    var_instance_type->Bind(faked_seq_instance_type);
 | 
| +  Bind(&if_isexternal);
 | 
| +  {
 | 
| +    GotoIf(IsShortExternalStringInstanceType(var_instance_type_.value()),
 | 
| +           if_bailout);
 | 
|  
 | 
| -    Goto(&if_isdirect);
 | 
| +    Node* const string = var_string_.value();
 | 
| +    Node* result = LoadObjectField(string, ExternalString::kResourceDataOffset,
 | 
| +                                   MachineType::Pointer());
 | 
| +    if (ptr_kind == PTR_TO_STRING) {
 | 
| +      result = IntPtrSub(result, IntPtrConstant(SeqOneByteString::kHeaderSize -
 | 
| +                                                kHeapObjectTag));
 | 
| +    }
 | 
| +    var_result.Bind(result);
 | 
| +    Goto(&out);
 | 
|    }
 | 
|  
 | 
| -  Bind(&if_isdirect);
 | 
| +  Bind(&out);
 | 
| +  return var_result.value();
 | 
|  }
 | 
|  
 | 
|  Node* CodeStubAssembler::TryDerefExternalString(Node* const string,
 | 
| @@ -3671,9 +3481,8 @@ Node* CodeStubAssembler::TryDerefExternalString(Node* const string,
 | 
|                                                  Label* if_bailout) {
 | 
|    Label out(this);
 | 
|  
 | 
| -  USE(IsExternalStringInstanceType);
 | 
| -  CSA_ASSERT(this, IsExternalStringInstanceType(this, instance_type));
 | 
| -  GotoIf(IsShortExternalStringInstanceType(this, instance_type), if_bailout);
 | 
| +  CSA_ASSERT(this, IsExternalStringInstanceType(instance_type));
 | 
| +  GotoIf(IsShortExternalStringInstanceType(instance_type), if_bailout);
 | 
|  
 | 
|    // Move the pointer so that offset-wise, it looks like a sequential string.
 | 
|    STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
 | 
| 
 |