Home | History | Annotate | Download | only in builtins
      1 // Copyright 2017 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/builtins/builtins-string-gen.h"
      6 
      7 #include "src/builtins/builtins-regexp-gen.h"
      8 #include "src/builtins/builtins-utils-gen.h"
      9 #include "src/builtins/builtins.h"
     10 #include "src/code-factory.h"
     11 #include "src/heap/factory-inl.h"
     12 #include "src/objects.h"
     13 
     14 namespace v8 {
     15 namespace internal {
     16 
     17 typedef compiler::Node Node;
     18 template <class T>
     19 using TNode = compiler::TNode<T>;
     20 
     21 Node* StringBuiltinsAssembler::DirectStringData(Node* string,
     22                                                 Node* string_instance_type) {
     23   // Compute the effective offset of the first character.
     24   VARIABLE(var_data, MachineType::PointerRepresentation());
     25   Label if_sequential(this), if_external(this), if_join(this);
     26   Branch(Word32Equal(Word32And(string_instance_type,
     27                                Int32Constant(kStringRepresentationMask)),
     28                      Int32Constant(kSeqStringTag)),
     29          &if_sequential, &if_external);
     30 
     31   BIND(&if_sequential);
     32   {
     33     var_data.Bind(IntPtrAdd(
     34         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
     35         BitcastTaggedToWord(string)));
     36     Goto(&if_join);
     37   }
     38 
     39   BIND(&if_external);
     40   {
     41     // This is only valid for ExternalStrings where the resource data
     42     // pointer is cached (i.e. no short external strings).
     43     CSA_ASSERT(
     44         this, Word32NotEqual(Word32And(string_instance_type,
     45                                        Int32Constant(kShortExternalStringMask)),
     46                              Int32Constant(kShortExternalStringTag)));
     47     var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
     48                                   MachineType::Pointer()));
     49     Goto(&if_join);
     50   }
     51 
     52   BIND(&if_join);
     53   return var_data.value();
     54 }
     55 
     56 void StringBuiltinsAssembler::DispatchOnStringEncodings(
     57     Node* const lhs_instance_type, Node* const rhs_instance_type,
     58     Label* if_one_one, Label* if_one_two, Label* if_two_one,
     59     Label* if_two_two) {
     60   STATIC_ASSERT(kStringEncodingMask == 0x8);
     61   STATIC_ASSERT(kTwoByteStringTag == 0x0);
     62   STATIC_ASSERT(kOneByteStringTag == 0x8);
     63 
     64   // First combine the encodings.
     65 
     66   Node* const encoding_mask = Int32Constant(kStringEncodingMask);
     67   Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
     68   Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
     69 
     70   Node* const combined_encodings =
     71       Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
     72 
     73   // Then dispatch on the combined encoding.
     74 
     75   Label unreachable(this, Label::kDeferred);
     76 
     77   int32_t values[] = {
     78       kOneByteStringTag | (kOneByteStringTag >> 1),
     79       kOneByteStringTag | (kTwoByteStringTag >> 1),
     80       kTwoByteStringTag | (kOneByteStringTag >> 1),
     81       kTwoByteStringTag | (kTwoByteStringTag >> 1),
     82   };
     83   Label* labels[] = {
     84       if_one_one, if_one_two, if_two_one, if_two_two,
     85   };
     86 
     87   STATIC_ASSERT(arraysize(values) == arraysize(labels));
     88   Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
     89 
     90   BIND(&unreachable);
     91   Unreachable();
     92 }
     93 
     94 template <typename SubjectChar, typename PatternChar>
     95 Node* StringBuiltinsAssembler::CallSearchStringRaw(Node* const subject_ptr,
     96                                                    Node* const subject_length,
     97                                                    Node* const search_ptr,
     98                                                    Node* const search_length,
     99                                                    Node* const start_position) {
    100   Node* const function_addr = ExternalConstant(
    101       ExternalReference::search_string_raw<SubjectChar, PatternChar>());
    102   Node* const isolate_ptr =
    103       ExternalConstant(ExternalReference::isolate_address(isolate()));
    104 
    105   MachineType type_ptr = MachineType::Pointer();
    106   MachineType type_intptr = MachineType::IntPtr();
    107 
    108   Node* const result = CallCFunction6(
    109       type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr,
    110       type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length,
    111       search_ptr, search_length, start_position);
    112 
    113   return result;
    114 }
    115 
    116 Node* StringBuiltinsAssembler::PointerToStringDataAtIndex(
    117     Node* const string_data, Node* const index, String::Encoding encoding) {
    118   const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
    119                                 ? UINT8_ELEMENTS
    120                                 : UINT16_ELEMENTS;
    121   Node* const offset_in_bytes =
    122       ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
    123   return IntPtrAdd(string_data, offset_in_bytes);
    124 }
    125 
    126 void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
    127                                                   Node* right) {
    128   VARIABLE(var_left, MachineRepresentation::kTagged, left);
    129   VARIABLE(var_right, MachineRepresentation::kTagged, right);
    130   Label if_equal(this), if_notequal(this), if_indirect(this, Label::kDeferred),
    131       restart(this, {&var_left, &var_right});
    132 
    133   TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(left);
    134   TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(right);
    135 
    136   // Strings with different lengths cannot be equal.
    137   GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
    138 
    139   Goto(&restart);
    140   BIND(&restart);
    141   Node* lhs = var_left.value();
    142   Node* rhs = var_right.value();
    143 
    144   Node* lhs_instance_type = LoadInstanceType(lhs);
    145   Node* rhs_instance_type = LoadInstanceType(rhs);
    146 
    147   StringEqual_Core(context, lhs, lhs_instance_type, rhs, rhs_instance_type,
    148                    lhs_length, &if_equal, &if_notequal, &if_indirect);
    149 
    150   BIND(&if_indirect);
    151   {
    152     // Try to unwrap indirect strings, restart the above attempt on success.
    153     MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
    154                               rhs_instance_type, &restart);
    155 
    156     TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
    157   }
    158 
    159   BIND(&if_equal);
    160   Return(TrueConstant());
    161 
    162   BIND(&if_notequal);
    163   Return(FalseConstant());
    164 }
    165 
    166 void StringBuiltinsAssembler::StringEqual_Core(
    167     Node* context, Node* lhs, Node* lhs_instance_type, Node* rhs,
    168     Node* rhs_instance_type, TNode<IntPtrT> length, Label* if_equal,
    169     Label* if_not_equal, Label* if_indirect) {
    170   CSA_ASSERT(this, IsString(lhs));
    171   CSA_ASSERT(this, IsString(rhs));
    172   CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(lhs), length));
    173   CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(rhs), length));
    174   // Fast check to see if {lhs} and {rhs} refer to the same String object.
    175   GotoIf(WordEqual(lhs, rhs), if_equal);
    176 
    177   // Combine the instance types into a single 16-bit value, so we can check
    178   // both of them at once.
    179   Node* both_instance_types = Word32Or(
    180       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
    181 
    182   // Check if both {lhs} and {rhs} are internalized. Since we already know
    183   // that they're not the same object, they're not equal in that case.
    184   int const kBothInternalizedMask =
    185       kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
    186   int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
    187   GotoIf(Word32Equal(Word32And(both_instance_types,
    188                                Int32Constant(kBothInternalizedMask)),
    189                      Int32Constant(kBothInternalizedTag)),
    190          if_not_equal);
    191 
    192   // Check if both {lhs} and {rhs} are direct strings, and that in case of
    193   // ExternalStrings the data pointer is cached.
    194   STATIC_ASSERT(kShortExternalStringTag != 0);
    195   STATIC_ASSERT(kIsIndirectStringTag != 0);
    196   int const kBothDirectStringMask =
    197       kIsIndirectStringMask | kShortExternalStringMask |
    198       ((kIsIndirectStringMask | kShortExternalStringMask) << 8);
    199   GotoIfNot(Word32Equal(Word32And(both_instance_types,
    200                                   Int32Constant(kBothDirectStringMask)),
    201                         Int32Constant(0)),
    202             if_indirect);
    203 
    204   // Dispatch based on the {lhs} and {rhs} string encoding.
    205   int const kBothStringEncodingMask =
    206       kStringEncodingMask | (kStringEncodingMask << 8);
    207   int const kOneOneByteStringTag = kOneByteStringTag | (kOneByteStringTag << 8);
    208   int const kTwoTwoByteStringTag = kTwoByteStringTag | (kTwoByteStringTag << 8);
    209   int const kOneTwoByteStringTag = kOneByteStringTag | (kTwoByteStringTag << 8);
    210   Label if_oneonebytestring(this), if_twotwobytestring(this),
    211       if_onetwobytestring(this), if_twoonebytestring(this);
    212   Node* masked_instance_types =
    213       Word32And(both_instance_types, Int32Constant(kBothStringEncodingMask));
    214   GotoIf(
    215       Word32Equal(masked_instance_types, Int32Constant(kOneOneByteStringTag)),
    216       &if_oneonebytestring);
    217   GotoIf(
    218       Word32Equal(masked_instance_types, Int32Constant(kTwoTwoByteStringTag)),
    219       &if_twotwobytestring);
    220   Branch(
    221       Word32Equal(masked_instance_types, Int32Constant(kOneTwoByteStringTag)),
    222       &if_onetwobytestring, &if_twoonebytestring);
    223 
    224   BIND(&if_oneonebytestring);
    225   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
    226                    rhs_instance_type, MachineType::Uint8(), length, if_equal,
    227                    if_not_equal);
    228 
    229   BIND(&if_twotwobytestring);
    230   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
    231                    rhs_instance_type, MachineType::Uint16(), length, if_equal,
    232                    if_not_equal);
    233 
    234   BIND(&if_onetwobytestring);
    235   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint8(), rhs,
    236                    rhs_instance_type, MachineType::Uint16(), length, if_equal,
    237                    if_not_equal);
    238 
    239   BIND(&if_twoonebytestring);
    240   StringEqual_Loop(lhs, lhs_instance_type, MachineType::Uint16(), rhs,
    241                    rhs_instance_type, MachineType::Uint8(), length, if_equal,
    242                    if_not_equal);
    243 }
    244 
    245 void StringBuiltinsAssembler::StringEqual_Loop(
    246     Node* lhs, Node* lhs_instance_type, MachineType lhs_type, Node* rhs,
    247     Node* rhs_instance_type, MachineType rhs_type, TNode<IntPtrT> length,
    248     Label* if_equal, Label* if_not_equal) {
    249   CSA_ASSERT(this, IsString(lhs));
    250   CSA_ASSERT(this, IsString(rhs));
    251   CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(lhs), length));
    252   CSA_ASSERT(this, WordEqual(LoadStringLengthAsWord(rhs), length));
    253 
    254   // Compute the effective offset of the first character.
    255   Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
    256   Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
    257 
    258   // Loop over the {lhs} and {rhs} strings to see if they are equal.
    259   TVARIABLE(IntPtrT, var_offset, IntPtrConstant(0));
    260   Label loop(this, &var_offset);
    261   Goto(&loop);
    262   BIND(&loop);
    263   {
    264     // If {offset} equals {end}, no difference was found, so the
    265     // strings are equal.
    266     GotoIf(WordEqual(var_offset.value(), length), if_equal);
    267 
    268     // Load the next characters from {lhs} and {rhs}.
    269     Node* lhs_value =
    270         Load(lhs_type, lhs_data,
    271              WordShl(var_offset.value(),
    272                      ElementSizeLog2Of(lhs_type.representation())));
    273     Node* rhs_value =
    274         Load(rhs_type, rhs_data,
    275              WordShl(var_offset.value(),
    276                      ElementSizeLog2Of(rhs_type.representation())));
    277 
    278     // Check if the characters match.
    279     GotoIf(Word32NotEqual(lhs_value, rhs_value), if_not_equal);
    280 
    281     // Advance to next character.
    282     var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
    283     Goto(&loop);
    284   }
    285 }
    286 
    287 void StringBuiltinsAssembler::Generate_StringAdd(StringAddFlags flags,
    288                                                  PretenureFlag pretenure_flag,
    289                                                  Node* context, Node* left,
    290                                                  Node* right) {
    291   switch (flags) {
    292     case STRING_ADD_CONVERT_LEFT: {
    293       // TODO(danno): The ToString and JSReceiverToPrimitive below could be
    294       // combined to avoid duplicate smi and instance type checks.
    295       left = ToString(context, JSReceiverToPrimitive(context, left));
    296       Callable callable = CodeFactory::StringAdd(
    297           isolate(), STRING_ADD_CHECK_NONE, pretenure_flag);
    298       TailCallStub(callable, context, left, right);
    299       break;
    300     }
    301     case STRING_ADD_CONVERT_RIGHT: {
    302       // TODO(danno): The ToString and JSReceiverToPrimitive below could be
    303       // combined to avoid duplicate smi and instance type checks.
    304       right = ToString(context, JSReceiverToPrimitive(context, right));
    305       Callable callable = CodeFactory::StringAdd(
    306           isolate(), STRING_ADD_CHECK_NONE, pretenure_flag);
    307       TailCallStub(callable, context, left, right);
    308       break;
    309     }
    310     case STRING_ADD_CHECK_NONE: {
    311       CodeStubAssembler::AllocationFlag allocation_flags =
    312           (pretenure_flag == TENURED) ? CodeStubAssembler::kPretenured
    313                                       : CodeStubAssembler::kNone;
    314       Return(StringAdd(context, CAST(left), CAST(right), allocation_flags));
    315       break;
    316     }
    317   }
    318 }
    319 
    320 TF_BUILTIN(StringAdd_CheckNone_NotTenured, StringBuiltinsAssembler) {
    321   Node* left = Parameter(Descriptor::kLeft);
    322   Node* right = Parameter(Descriptor::kRight);
    323   Node* context = Parameter(Descriptor::kContext);
    324   Generate_StringAdd(STRING_ADD_CHECK_NONE, NOT_TENURED, context, left, right);
    325 }
    326 
    327 TF_BUILTIN(StringAdd_CheckNone_Tenured, StringBuiltinsAssembler) {
    328   Node* left = Parameter(Descriptor::kLeft);
    329   Node* right = Parameter(Descriptor::kRight);
    330   Node* context = Parameter(Descriptor::kContext);
    331   Generate_StringAdd(STRING_ADD_CHECK_NONE, TENURED, context, left, right);
    332 }
    333 
    334 TF_BUILTIN(StringAdd_ConvertLeft_NotTenured, StringBuiltinsAssembler) {
    335   Node* left = Parameter(Descriptor::kLeft);
    336   Node* right = Parameter(Descriptor::kRight);
    337   Node* context = Parameter(Descriptor::kContext);
    338   Generate_StringAdd(STRING_ADD_CONVERT_LEFT, NOT_TENURED, context, left,
    339                      right);
    340 }
    341 
    342 TF_BUILTIN(StringAdd_ConvertRight_NotTenured, StringBuiltinsAssembler) {
    343   Node* left = Parameter(Descriptor::kLeft);
    344   Node* right = Parameter(Descriptor::kRight);
    345   Node* context = Parameter(Descriptor::kContext);
    346   Generate_StringAdd(STRING_ADD_CONVERT_RIGHT, NOT_TENURED, context, left,
    347                      right);
    348 }
    349 
    350 TF_BUILTIN(SubString, StringBuiltinsAssembler) {
    351   TNode<String> string = CAST(Parameter(Descriptor::kString));
    352   TNode<Smi> from = CAST(Parameter(Descriptor::kFrom));
    353   TNode<Smi> to = CAST(Parameter(Descriptor::kTo));
    354   Return(SubString(string, SmiUntag(from), SmiUntag(to)));
    355 }
    356 
    357 void StringBuiltinsAssembler::GenerateStringAt(char const* method_name,
    358                                                TNode<Context> context,
    359                                                Node* receiver,
    360                                                TNode<Object> maybe_position,
    361                                                TNode<Object> default_return,
    362                                                StringAtAccessor accessor) {
    363   // Check that {receiver} is coercible to Object and convert it to a String.
    364   TNode<String> string = ToThisString(context, receiver, method_name);
    365 
    366   // Convert the {position} to a Smi and check that it's in bounds of the
    367   // {string}.
    368   Label if_outofbounds(this, Label::kDeferred);
    369   TNode<Number> position = ToInteger_Inline(
    370       context, maybe_position, CodeStubAssembler::kTruncateMinusZero);
    371   GotoIfNot(TaggedIsSmi(position), &if_outofbounds);
    372   TNode<IntPtrT> index = SmiUntag(CAST(position));
    373   TNode<IntPtrT> length = LoadStringLengthAsWord(string);
    374   GotoIfNot(UintPtrLessThan(index, length), &if_outofbounds);
    375   TNode<Object> result = accessor(string, length, index);
    376   Return(result);
    377 
    378   BIND(&if_outofbounds);
    379   Return(default_return);
    380 }
    381 
    382 void StringBuiltinsAssembler::GenerateStringRelationalComparison(Node* context,
    383                                                                  Node* left,
    384                                                                  Node* right,
    385                                                                  Operation op) {
    386   VARIABLE(var_left, MachineRepresentation::kTagged, left);
    387   VARIABLE(var_right, MachineRepresentation::kTagged, right);
    388 
    389   Variable* input_vars[2] = {&var_left, &var_right};
    390   Label if_less(this), if_equal(this), if_greater(this);
    391   Label restart(this, 2, input_vars);
    392   Goto(&restart);
    393   BIND(&restart);
    394 
    395   Node* lhs = var_left.value();
    396   Node* rhs = var_right.value();
    397   // Fast check to see if {lhs} and {rhs} refer to the same String object.
    398   GotoIf(WordEqual(lhs, rhs), &if_equal);
    399 
    400   // Load instance types of {lhs} and {rhs}.
    401   Node* lhs_instance_type = LoadInstanceType(lhs);
    402   Node* rhs_instance_type = LoadInstanceType(rhs);
    403 
    404   // Combine the instance types into a single 16-bit value, so we can check
    405   // both of them at once.
    406   Node* both_instance_types = Word32Or(
    407       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
    408 
    409   // Check that both {lhs} and {rhs} are flat one-byte strings.
    410   int const kBothSeqOneByteStringMask =
    411       kStringEncodingMask | kStringRepresentationMask |
    412       ((kStringEncodingMask | kStringRepresentationMask) << 8);
    413   int const kBothSeqOneByteStringTag =
    414       kOneByteStringTag | kSeqStringTag |
    415       ((kOneByteStringTag | kSeqStringTag) << 8);
    416   Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this);
    417   Branch(Word32Equal(Word32And(both_instance_types,
    418                                Int32Constant(kBothSeqOneByteStringMask)),
    419                      Int32Constant(kBothSeqOneByteStringTag)),
    420          &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
    421 
    422   BIND(&if_bothonebyteseqstrings);
    423   {
    424     // Load the length of {lhs} and {rhs}.
    425     TNode<IntPtrT> lhs_length = LoadStringLengthAsWord(lhs);
    426     TNode<IntPtrT> rhs_length = LoadStringLengthAsWord(rhs);
    427 
    428     // Determine the minimum length.
    429     TNode<IntPtrT> length = IntPtrMin(lhs_length, rhs_length);
    430 
    431     // Compute the effective offset of the first character.
    432     TNode<IntPtrT> begin =
    433         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag);
    434 
    435     // Compute the first offset after the string from the length.
    436     TNode<IntPtrT> end = IntPtrAdd(begin, length);
    437 
    438     // Loop over the {lhs} and {rhs} strings to see if they are equal.
    439     TVARIABLE(IntPtrT, var_offset, begin);
    440     Label loop(this, &var_offset);
    441     Goto(&loop);
    442     BIND(&loop);
    443     {
    444       // Check if {offset} equals {end}.
    445       Label if_done(this), if_notdone(this);
    446       Branch(WordEqual(var_offset.value(), end), &if_done, &if_notdone);
    447 
    448       BIND(&if_notdone);
    449       {
    450         // Load the next characters from {lhs} and {rhs}.
    451         Node* lhs_value = Load(MachineType::Uint8(), lhs, var_offset.value());
    452         Node* rhs_value = Load(MachineType::Uint8(), rhs, var_offset.value());
    453 
    454         // Check if the characters match.
    455         Label if_valueissame(this), if_valueisnotsame(this);
    456         Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame,
    457                &if_valueisnotsame);
    458 
    459         BIND(&if_valueissame);
    460         {
    461           // Advance to next character.
    462           var_offset = IntPtrAdd(var_offset.value(), IntPtrConstant(1));
    463         }
    464         Goto(&loop);
    465 
    466         BIND(&if_valueisnotsame);
    467         Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater);
    468       }
    469 
    470       BIND(&if_done);
    471       {
    472         // All characters up to the min length are equal, decide based on
    473         // string length.
    474         GotoIf(IntPtrEqual(lhs_length, rhs_length), &if_equal);
    475         Branch(IntPtrLessThan(lhs_length, rhs_length), &if_less, &if_greater);
    476       }
    477     }
    478   }
    479 
    480   BIND(&if_notbothonebyteseqstrings);
    481   {
    482     // Try to unwrap indirect strings, restart the above attempt on success.
    483     MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
    484                               rhs_instance_type, &restart);
    485     // TODO(bmeurer): Add support for two byte string relational comparisons.
    486     switch (op) {
    487       case Operation::kLessThan:
    488         TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs);
    489         break;
    490       case Operation::kLessThanOrEqual:
    491         TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs);
    492         break;
    493       case Operation::kGreaterThan:
    494         TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs);
    495         break;
    496       case Operation::kGreaterThanOrEqual:
    497         TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, rhs);
    498         break;
    499       default:
    500         UNREACHABLE();
    501     }
    502   }
    503 
    504   BIND(&if_less);
    505   switch (op) {
    506     case Operation::kLessThan:
    507     case Operation::kLessThanOrEqual:
    508       Return(TrueConstant());
    509       break;
    510 
    511     case Operation::kGreaterThan:
    512     case Operation::kGreaterThanOrEqual:
    513       Return(FalseConstant());
    514       break;
    515     default:
    516       UNREACHABLE();
    517   }
    518 
    519   BIND(&if_equal);
    520   switch (op) {
    521     case Operation::kLessThan:
    522     case Operation::kGreaterThan:
    523       Return(FalseConstant());
    524       break;
    525 
    526     case Operation::kLessThanOrEqual:
    527     case Operation::kGreaterThanOrEqual:
    528       Return(TrueConstant());
    529       break;
    530     default:
    531       UNREACHABLE();
    532   }
    533 
    534   BIND(&if_greater);
    535   switch (op) {
    536     case Operation::kLessThan:
    537     case Operation::kLessThanOrEqual:
    538       Return(FalseConstant());
    539       break;
    540 
    541     case Operation::kGreaterThan:
    542     case Operation::kGreaterThanOrEqual:
    543       Return(TrueConstant());
    544       break;
    545     default:
    546       UNREACHABLE();
    547   }
    548 }
    549 
    550 TF_BUILTIN(StringEqual, StringBuiltinsAssembler) {
    551   Node* context = Parameter(Descriptor::kContext);
    552   Node* left = Parameter(Descriptor::kLeft);
    553   Node* right = Parameter(Descriptor::kRight);
    554   GenerateStringEqual(context, left, right);
    555 }
    556 
    557 TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
    558   Node* context = Parameter(Descriptor::kContext);
    559   Node* left = Parameter(Descriptor::kLeft);
    560   Node* right = Parameter(Descriptor::kRight);
    561   GenerateStringRelationalComparison(context, left, right,
    562                                      Operation::kLessThan);
    563 }
    564 
    565 TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
    566   Node* context = Parameter(Descriptor::kContext);
    567   Node* left = Parameter(Descriptor::kLeft);
    568   Node* right = Parameter(Descriptor::kRight);
    569   GenerateStringRelationalComparison(context, left, right,
    570                                      Operation::kLessThanOrEqual);
    571 }
    572 
    573 TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
    574   Node* context = Parameter(Descriptor::kContext);
    575   Node* left = Parameter(Descriptor::kLeft);
    576   Node* right = Parameter(Descriptor::kRight);
    577   GenerateStringRelationalComparison(context, left, right,
    578                                      Operation::kGreaterThan);
    579 }
    580 
    581 TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
    582   Node* context = Parameter(Descriptor::kContext);
    583   Node* left = Parameter(Descriptor::kLeft);
    584   Node* right = Parameter(Descriptor::kRight);
    585   GenerateStringRelationalComparison(context, left, right,
    586                                      Operation::kGreaterThanOrEqual);
    587 }
    588 
    589 TF_BUILTIN(StringCharAt, StringBuiltinsAssembler) {
    590   Node* receiver = Parameter(Descriptor::kReceiver);
    591   Node* position = Parameter(Descriptor::kPosition);
    592 
    593   // Load the character code at the {position} from the {receiver}.
    594   TNode<Int32T> code = StringCharCodeAt(receiver, position);
    595 
    596   // And return the single character string with only that {code}
    597   TNode<String> result = StringFromSingleCharCode(code);
    598   Return(result);
    599 }
    600 
    601 TF_BUILTIN(StringCodePointAtUTF16, StringBuiltinsAssembler) {
    602   Node* receiver = Parameter(Descriptor::kReceiver);
    603   Node* position = Parameter(Descriptor::kPosition);
    604   // TODO(sigurds) Figure out if passing length as argument pays off.
    605   TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
    606   // Load the character code at the {position} from the {receiver}.
    607   TNode<Int32T> code =
    608       LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF16);
    609   // And return it as TaggedSigned value.
    610   // TODO(turbofan): Allow builtins to return values untagged.
    611   TNode<Smi> result = SmiFromInt32(code);
    612   Return(result);
    613 }
    614 
    615 TF_BUILTIN(StringCodePointAtUTF32, StringBuiltinsAssembler) {
    616   Node* receiver = Parameter(Descriptor::kReceiver);
    617   Node* position = Parameter(Descriptor::kPosition);
    618 
    619   // TODO(sigurds) Figure out if passing length as argument pays off.
    620   TNode<IntPtrT> length = LoadStringLengthAsWord(receiver);
    621   // Load the character code at the {position} from the {receiver}.
    622   TNode<Int32T> code =
    623       LoadSurrogatePairAt(receiver, length, position, UnicodeEncoding::UTF32);
    624   // And return it as TaggedSigned value.
    625   // TODO(turbofan): Allow builtins to return values untagged.
    626   TNode<Smi> result = SmiFromInt32(code);
    627   Return(result);
    628 }
    629 
    630 // -----------------------------------------------------------------------------
    631 // ES6 section 21.1 String Objects
    632 
    633 // ES6 #sec-string.fromcharcode
    634 TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
    635   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
    636   // arguments are reordered.
    637   TNode<Int32T> argc =
    638       UncheckedCast<Int32T>(Parameter(Descriptor::kJSActualArgumentsCount));
    639   Node* context = Parameter(Descriptor::kContext);
    640 
    641   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
    642   TNode<Smi> smi_argc = SmiTag(arguments.GetLength(INTPTR_PARAMETERS));
    643   // Check if we have exactly one argument (plus the implicit receiver), i.e.
    644   // if the parent frame is not an arguments adaptor frame.
    645   Label if_oneargument(this), if_notoneargument(this);
    646   Branch(Word32Equal(argc, Int32Constant(1)), &if_oneargument,
    647          &if_notoneargument);
    648 
    649   BIND(&if_oneargument);
    650   {
    651     // Single argument case, perform fast single character string cache lookup
    652     // for one-byte code units, or fall back to creating a single character
    653     // string on the fly otherwise.
    654     Node* code = arguments.AtIndex(0);
    655     Node* code32 = TruncateTaggedToWord32(context, code);
    656     TNode<Int32T> code16 =
    657         Signed(Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit)));
    658     Node* result = StringFromSingleCharCode(code16);
    659     arguments.PopAndReturn(result);
    660   }
    661 
    662   Node* code16 = nullptr;
    663   BIND(&if_notoneargument);
    664   {
    665     Label two_byte(this);
    666     // Assume that the resulting string contains only one-byte characters.
    667     Node* one_byte_result = AllocateSeqOneByteString(context, smi_argc);
    668 
    669     TVARIABLE(IntPtrT, var_max_index);
    670     var_max_index = IntPtrConstant(0);
    671 
    672     // Iterate over the incoming arguments, converting them to 8-bit character
    673     // codes. Stop if any of the conversions generates a code that doesn't fit
    674     // in 8 bits.
    675     CodeStubAssembler::VariableList vars({&var_max_index}, zone());
    676     arguments.ForEach(vars, [this, context, &two_byte, &var_max_index, &code16,
    677                              one_byte_result](Node* arg) {
    678       Node* code32 = TruncateTaggedToWord32(context, arg);
    679       code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
    680 
    681       GotoIf(
    682           Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
    683           &two_byte);
    684 
    685       // The {code16} fits into the SeqOneByteString {one_byte_result}.
    686       Node* offset = ElementOffsetFromIndex(
    687           var_max_index.value(), UINT8_ELEMENTS,
    688           CodeStubAssembler::INTPTR_PARAMETERS,
    689           SeqOneByteString::kHeaderSize - kHeapObjectTag);
    690       StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
    691                           offset, code16);
    692       var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
    693     });
    694     arguments.PopAndReturn(one_byte_result);
    695 
    696     BIND(&two_byte);
    697 
    698     // At least one of the characters in the string requires a 16-bit
    699     // representation.  Allocate a SeqTwoByteString to hold the resulting
    700     // string.
    701     Node* two_byte_result = AllocateSeqTwoByteString(context, smi_argc);
    702 
    703     // Copy the characters that have already been put in the 8-bit string into
    704     // their corresponding positions in the new 16-bit string.
    705     TNode<IntPtrT> zero = IntPtrConstant(0);
    706     CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
    707                          var_max_index.value(), String::ONE_BYTE_ENCODING,
    708                          String::TWO_BYTE_ENCODING);
    709 
    710     // Write the character that caused the 8-bit to 16-bit fault.
    711     Node* max_index_offset =
    712         ElementOffsetFromIndex(var_max_index.value(), UINT16_ELEMENTS,
    713                                CodeStubAssembler::INTPTR_PARAMETERS,
    714                                SeqTwoByteString::kHeaderSize - kHeapObjectTag);
    715     StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
    716                         max_index_offset, code16);
    717     var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
    718 
    719     // Resume copying the passed-in arguments from the same place where the
    720     // 8-bit copy stopped, but this time copying over all of the characters
    721     // using a 16-bit representation.
    722     arguments.ForEach(
    723         vars,
    724         [this, context, two_byte_result, &var_max_index](Node* arg) {
    725           Node* code32 = TruncateTaggedToWord32(context, arg);
    726           Node* code16 =
    727               Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
    728 
    729           Node* offset = ElementOffsetFromIndex(
    730               var_max_index.value(), UINT16_ELEMENTS,
    731               CodeStubAssembler::INTPTR_PARAMETERS,
    732               SeqTwoByteString::kHeaderSize - kHeapObjectTag);
    733           StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
    734                               offset, code16);
    735           var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
    736         },
    737         var_max_index.value());
    738 
    739     arguments.PopAndReturn(two_byte_result);
    740   }
    741 }
    742 
    743 // ES6 #sec-string.prototype.charat
    744 TF_BUILTIN(StringPrototypeCharAt, StringBuiltinsAssembler) {
    745   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
    746   Node* receiver = Parameter(Descriptor::kReceiver);
    747   TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
    748 
    749   GenerateStringAt("String.prototype.charAt", context, receiver, maybe_position,
    750                    EmptyStringConstant(),
    751                    [this](TNode<String> string, TNode<IntPtrT> length,
    752                           TNode<IntPtrT> index) {
    753                      TNode<Int32T> code = StringCharCodeAt(string, index);
    754                      return StringFromSingleCharCode(code);
    755                    });
    756 }
    757 
    758 // ES6 #sec-string.prototype.charcodeat
    759 TF_BUILTIN(StringPrototypeCharCodeAt, StringBuiltinsAssembler) {
    760   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
    761   Node* receiver = Parameter(Descriptor::kReceiver);
    762   TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
    763 
    764   GenerateStringAt("String.prototype.charCodeAt", context, receiver,
    765                    maybe_position, NanConstant(),
    766                    [this](TNode<String> receiver, TNode<IntPtrT> length,
    767                           TNode<IntPtrT> index) {
    768                      Node* value = StringCharCodeAt(receiver, index);
    769                      return SmiFromInt32(value);
    770                    });
    771 }
    772 
    773 // ES6 #sec-string.prototype.codepointat
    774 TF_BUILTIN(StringPrototypeCodePointAt, StringBuiltinsAssembler) {
    775   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
    776   Node* receiver = Parameter(Descriptor::kReceiver);
    777   TNode<Object> maybe_position = CAST(Parameter(Descriptor::kPosition));
    778 
    779   GenerateStringAt("String.prototype.codePointAt", context, receiver,
    780                    maybe_position, UndefinedConstant(),
    781                    [this](TNode<String> receiver, TNode<IntPtrT> length,
    782                           TNode<IntPtrT> index) {
    783                      // This is always a call to a builtin from Javascript,
    784                      // so we need to produce UTF32.
    785                      Node* value = LoadSurrogatePairAt(receiver, length, index,
    786                                                        UnicodeEncoding::UTF32);
    787                      return SmiFromInt32(value);
    788                    });
    789 }
    790 
    791 // ES6 String.prototype.concat(...args)
    792 // ES6 #sec-string.prototype.concat
    793 TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) {
    794   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
    795   // arguments are reordered.
    796   CodeStubArguments arguments(
    797       this,
    798       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount)));
    799   Node* receiver = arguments.GetReceiver();
    800   Node* context = Parameter(Descriptor::kContext);
    801 
    802   // Check that {receiver} is coercible to Object and convert it to a String.
    803   receiver = ToThisString(context, receiver, "String.prototype.concat");
    804 
    805   // Concatenate all the arguments passed to this builtin.
    806   VARIABLE(var_result, MachineRepresentation::kTagged);
    807   var_result.Bind(receiver);
    808   arguments.ForEach(
    809       CodeStubAssembler::VariableList({&var_result}, zone()),
    810       [this, context, &var_result](Node* arg) {
    811         arg = ToString_Inline(context, arg);
    812         var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
    813                                  var_result.value(), arg));
    814       });
    815   arguments.PopAndReturn(var_result.value());
    816 }
    817 
    818 void StringBuiltinsAssembler::StringIndexOf(
    819     Node* const subject_string, Node* const search_string, Node* const position,
    820     std::function<void(Node*)> f_return) {
    821   CSA_ASSERT(this, IsString(subject_string));
    822   CSA_ASSERT(this, IsString(search_string));
    823   CSA_ASSERT(this, TaggedIsSmi(position));
    824 
    825   TNode<IntPtrT> const int_zero = IntPtrConstant(0);
    826   TNode<IntPtrT> const search_length = LoadStringLengthAsWord(search_string);
    827   TNode<IntPtrT> const subject_length = LoadStringLengthAsWord(subject_string);
    828   TNode<IntPtrT> const start_position = IntPtrMax(SmiUntag(position), int_zero);
    829 
    830   Label zero_length_needle(this), return_minus_1(this);
    831   {
    832     GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle);
    833 
    834     // Check that the needle fits in the start position.
    835     GotoIfNot(IntPtrLessThanOrEqual(search_length,
    836                                     IntPtrSub(subject_length, start_position)),
    837               &return_minus_1);
    838   }
    839 
    840   // If the string pointers are identical, we can just return 0. Note that this
    841   // implies {start_position} == 0 since we've passed the check above.
    842   Label return_zero(this);
    843   GotoIf(WordEqual(subject_string, search_string), &return_zero);
    844 
    845   // Try to unpack subject and search strings. Bail to runtime if either needs
    846   // to be flattened.
    847   ToDirectStringAssembler subject_to_direct(state(), subject_string);
    848   ToDirectStringAssembler search_to_direct(state(), search_string);
    849 
    850   Label call_runtime_unchecked(this, Label::kDeferred);
    851 
    852   subject_to_direct.TryToDirect(&call_runtime_unchecked);
    853   search_to_direct.TryToDirect(&call_runtime_unchecked);
    854 
    855   // Load pointers to string data.
    856   Node* const subject_ptr =
    857       subject_to_direct.PointerToData(&call_runtime_unchecked);
    858   Node* const search_ptr =
    859       search_to_direct.PointerToData(&call_runtime_unchecked);
    860 
    861   Node* const subject_offset = subject_to_direct.offset();
    862   Node* const search_offset = search_to_direct.offset();
    863 
    864   // Like String::IndexOf, the actual matching is done by the optimized
    865   // SearchString method in string-search.h. Dispatch based on string instance
    866   // types, then call straight into C++ for matching.
    867 
    868   CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero));
    869   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero));
    870   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position));
    871   CSA_ASSERT(this,
    872              IntPtrLessThanOrEqual(search_length,
    873                                    IntPtrSub(subject_length, start_position)));
    874 
    875   Label one_one(this), one_two(this), two_one(this), two_two(this);
    876   DispatchOnStringEncodings(subject_to_direct.instance_type(),
    877                             search_to_direct.instance_type(), &one_one,
    878                             &one_two, &two_one, &two_two);
    879 
    880   typedef const uint8_t onebyte_t;
    881   typedef const uc16 twobyte_t;
    882 
    883   BIND(&one_one);
    884   {
    885     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
    886         subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
    887     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
    888         search_ptr, search_offset, String::ONE_BYTE_ENCODING);
    889 
    890     Label direct_memchr_call(this), generic_fast_path(this);
    891     Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call,
    892            &generic_fast_path);
    893 
    894     // An additional fast path that calls directly into memchr for 1-length
    895     // search strings.
    896     BIND(&direct_memchr_call);
    897     {
    898       Node* const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position);
    899       Node* const search_length = IntPtrSub(subject_length, start_position);
    900       Node* const search_byte =
    901           ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr));
    902 
    903       Node* const memchr =
    904           ExternalConstant(ExternalReference::libc_memchr_function());
    905       Node* const result_address =
    906           CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
    907                          MachineType::IntPtr(), MachineType::UintPtr(), memchr,
    908                          string_addr, search_byte, search_length);
    909       GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
    910       Node* const result_index =
    911           IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
    912       f_return(SmiTag(result_index));
    913     }
    914 
    915     BIND(&generic_fast_path);
    916     {
    917       Node* const result = CallSearchStringRaw<onebyte_t, onebyte_t>(
    918           adjusted_subject_ptr, subject_length, adjusted_search_ptr,
    919           search_length, start_position);
    920       f_return(SmiTag(result));
    921     }
    922   }
    923 
    924   BIND(&one_two);
    925   {
    926     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
    927         subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
    928     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
    929         search_ptr, search_offset, String::TWO_BYTE_ENCODING);
    930 
    931     Node* const result = CallSearchStringRaw<onebyte_t, twobyte_t>(
    932         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
    933         search_length, start_position);
    934     f_return(SmiTag(result));
    935   }
    936 
    937   BIND(&two_one);
    938   {
    939     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
    940         subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
    941     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
    942         search_ptr, search_offset, String::ONE_BYTE_ENCODING);
    943 
    944     Node* const result = CallSearchStringRaw<twobyte_t, onebyte_t>(
    945         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
    946         search_length, start_position);
    947     f_return(SmiTag(result));
    948   }
    949 
    950   BIND(&two_two);
    951   {
    952     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
    953         subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
    954     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
    955         search_ptr, search_offset, String::TWO_BYTE_ENCODING);
    956 
    957     Node* const result = CallSearchStringRaw<twobyte_t, twobyte_t>(
    958         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
    959         search_length, start_position);
    960     f_return(SmiTag(result));
    961   }
    962 
    963   BIND(&return_minus_1);
    964   f_return(SmiConstant(-1));
    965 
    966   BIND(&return_zero);
    967   f_return(SmiConstant(0));
    968 
    969   BIND(&zero_length_needle);
    970   {
    971     Comment("0-length search_string");
    972     f_return(SmiTag(IntPtrMin(subject_length, start_position)));
    973   }
    974 
    975   BIND(&call_runtime_unchecked);
    976   {
    977     // Simplified version of the runtime call where the types of the arguments
    978     // are already known due to type checks in this stub.
    979     Comment("Call Runtime Unchecked");
    980     Node* result =
    981         CallRuntime(Runtime::kStringIndexOfUnchecked, NoContextConstant(),
    982                     subject_string, search_string, position);
    983     f_return(result);
    984   }
    985 }
    986 
    987 // ES6 String.prototype.indexOf(searchString [, position])
    988 // #sec-string.prototype.indexof
    989 // Unchecked helper for builtins lowering.
    990 TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
    991   Node* receiver = Parameter(Descriptor::kReceiver);
    992   Node* search_string = Parameter(Descriptor::kSearchString);
    993   Node* position = Parameter(Descriptor::kPosition);
    994   StringIndexOf(receiver, search_string, position,
    995                 [this](Node* result) { this->Return(result); });
    996 }
    997 
    998 // ES6 String.prototype.includes(searchString [, position])
    999 // #sec-string.prototype.includes
   1000 TF_BUILTIN(StringPrototypeIncludes, StringIncludesIndexOfAssembler) {
   1001   TNode<IntPtrT> argc =
   1002       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   1003   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1004   Generate(kIncludes, argc, context);
   1005 }
   1006 
   1007 // ES6 String.prototype.indexOf(searchString [, position])
   1008 // #sec-string.prototype.indexof
   1009 TF_BUILTIN(StringPrototypeIndexOf, StringIncludesIndexOfAssembler) {
   1010   TNode<IntPtrT> argc =
   1011       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   1012   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1013   Generate(kIndexOf, argc, context);
   1014 }
   1015 
   1016 void StringIncludesIndexOfAssembler::Generate(SearchVariant variant,
   1017                                               TNode<IntPtrT> argc,
   1018                                               TNode<Context> context) {
   1019   CodeStubArguments arguments(this, argc);
   1020   Node* const receiver = arguments.GetReceiver();
   1021 
   1022   VARIABLE(var_search_string, MachineRepresentation::kTagged);
   1023   VARIABLE(var_position, MachineRepresentation::kTagged);
   1024   Label argc_1(this), argc_2(this), call_runtime(this, Label::kDeferred),
   1025       fast_path(this);
   1026 
   1027   GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1);
   1028   GotoIf(IntPtrGreaterThan(argc, IntPtrConstant(1)), &argc_2);
   1029   {
   1030     Comment("0 Argument case");
   1031     CSA_ASSERT(this, IntPtrEqual(argc, IntPtrConstant(0)));
   1032     Node* const undefined = UndefinedConstant();
   1033     var_search_string.Bind(undefined);
   1034     var_position.Bind(undefined);
   1035     Goto(&call_runtime);
   1036   }
   1037   BIND(&argc_1);
   1038   {
   1039     Comment("1 Argument case");
   1040     var_search_string.Bind(arguments.AtIndex(0));
   1041     var_position.Bind(SmiConstant(0));
   1042     Goto(&fast_path);
   1043   }
   1044   BIND(&argc_2);
   1045   {
   1046     Comment("2 Argument case");
   1047     var_search_string.Bind(arguments.AtIndex(0));
   1048     var_position.Bind(arguments.AtIndex(1));
   1049     GotoIfNot(TaggedIsSmi(var_position.value()), &call_runtime);
   1050     Goto(&fast_path);
   1051   }
   1052   BIND(&fast_path);
   1053   {
   1054     Comment("Fast Path");
   1055     Node* const search = var_search_string.value();
   1056     Node* const position = var_position.value();
   1057     GotoIf(TaggedIsSmi(receiver), &call_runtime);
   1058     GotoIf(TaggedIsSmi(search), &call_runtime);
   1059     GotoIfNot(IsString(receiver), &call_runtime);
   1060     GotoIfNot(IsString(search), &call_runtime);
   1061 
   1062     StringIndexOf(receiver, search, position, [&](Node* result) {
   1063       CSA_ASSERT(this, TaggedIsSmi(result));
   1064       arguments.PopAndReturn((variant == kIndexOf)
   1065                                  ? result
   1066                                  : SelectBooleanConstant(SmiGreaterThanOrEqual(
   1067                                        CAST(result), SmiConstant(0))));
   1068     });
   1069   }
   1070   BIND(&call_runtime);
   1071   {
   1072     Comment("Call Runtime");
   1073     Runtime::FunctionId runtime = variant == kIndexOf
   1074                                       ? Runtime::kStringIndexOf
   1075                                       : Runtime::kStringIncludes;
   1076     Node* const result =
   1077         CallRuntime(runtime, context, receiver, var_search_string.value(),
   1078                     var_position.value());
   1079     arguments.PopAndReturn(result);
   1080   }
   1081 }
   1082 
   1083 void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
   1084                                                      Node* const value,
   1085                                                      const char* method_name) {
   1086   Label out(this), throw_exception(this, Label::kDeferred);
   1087   Branch(IsNullOrUndefined(value), &throw_exception, &out);
   1088 
   1089   BIND(&throw_exception);
   1090   ThrowTypeError(context, MessageTemplate::kCalledOnNullOrUndefined,
   1091                  method_name);
   1092 
   1093   BIND(&out);
   1094 }
   1095 
   1096 void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
   1097     Node* const context, Node* const object, Node* const maybe_string,
   1098     Handle<Symbol> symbol, const NodeFunction0& regexp_call,
   1099     const NodeFunction1& generic_call) {
   1100   Label out(this);
   1101 
   1102   // Smis definitely don't have an attached symbol.
   1103   GotoIf(TaggedIsSmi(object), &out);
   1104 
   1105   Node* const object_map = LoadMap(object);
   1106 
   1107   // Skip the slow lookup for Strings.
   1108   {
   1109     Label next(this);
   1110 
   1111     GotoIfNot(IsStringInstanceType(LoadMapInstanceType(object_map)), &next);
   1112 
   1113     Node* const native_context = LoadNativeContext(context);
   1114     Node* const initial_proto_initial_map = LoadContextElement(
   1115         native_context, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX);
   1116 
   1117     Node* const string_fun =
   1118         LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX);
   1119     Node* const initial_map =
   1120         LoadObjectField(string_fun, JSFunction::kPrototypeOrInitialMapOffset);
   1121     Node* const proto_map = LoadMap(LoadMapPrototype(initial_map));
   1122 
   1123     Branch(WordEqual(proto_map, initial_proto_initial_map), &out, &next);
   1124 
   1125     BIND(&next);
   1126   }
   1127 
   1128   // Take the fast path for RegExps.
   1129   // There's two conditions: {object} needs to be a fast regexp, and
   1130   // {maybe_string} must be a string (we can't call ToString on the fast path
   1131   // since it may mutate {object}).
   1132   {
   1133     Label stub_call(this), slow_lookup(this);
   1134 
   1135     GotoIf(TaggedIsSmi(maybe_string), &slow_lookup);
   1136     GotoIfNot(IsString(maybe_string), &slow_lookup);
   1137 
   1138     RegExpBuiltinsAssembler regexp_asm(state());
   1139     regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call,
   1140                                   &slow_lookup);
   1141 
   1142     BIND(&stub_call);
   1143     // TODO(jgruber): Add a no-JS scope once it exists.
   1144     regexp_call();
   1145 
   1146     BIND(&slow_lookup);
   1147   }
   1148 
   1149   GotoIf(IsNullOrUndefined(object), &out);
   1150 
   1151   // Fall back to a slow lookup of {object[symbol]}.
   1152   //
   1153   // The spec uses GetMethod({object}, {symbol}), which has a few quirks:
   1154   // * null values are turned into undefined, and
   1155   // * an exception is thrown if the value is not undefined, null, or callable.
   1156   // We handle the former by jumping to {out} for null values as well, while
   1157   // the latter is already handled by the Call({maybe_func}) operation.
   1158 
   1159   Node* const maybe_func = GetProperty(context, object, symbol);
   1160   GotoIf(IsUndefined(maybe_func), &out);
   1161   GotoIf(IsNull(maybe_func), &out);
   1162 
   1163   // Attempt to call the function.
   1164   generic_call(maybe_func);
   1165 
   1166   BIND(&out);
   1167 }
   1168 
   1169 TNode<Smi> StringBuiltinsAssembler::IndexOfDollarChar(Node* const context,
   1170                                                       Node* const string) {
   1171   CSA_ASSERT(this, IsString(string));
   1172 
   1173   TNode<String> const dollar_string = HeapConstant(
   1174       isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
   1175   TNode<Smi> const dollar_ix =
   1176       CAST(CallBuiltin(Builtins::kStringIndexOf, context, string, dollar_string,
   1177                        SmiConstant(0)));
   1178   return dollar_ix;
   1179 }
   1180 
   1181 compiler::Node* StringBuiltinsAssembler::GetSubstitution(
   1182     Node* context, Node* subject_string, Node* match_start_index,
   1183     Node* match_end_index, Node* replace_string) {
   1184   CSA_ASSERT(this, IsString(subject_string));
   1185   CSA_ASSERT(this, IsString(replace_string));
   1186   CSA_ASSERT(this, TaggedIsPositiveSmi(match_start_index));
   1187   CSA_ASSERT(this, TaggedIsPositiveSmi(match_end_index));
   1188 
   1189   VARIABLE(var_result, MachineRepresentation::kTagged, replace_string);
   1190   Label runtime(this), out(this);
   1191 
   1192   // In this primitive implementation we simply look for the next '$' char in
   1193   // {replace_string}. If it doesn't exist, we can simply return
   1194   // {replace_string} itself. If it does, then we delegate to
   1195   // String::GetSubstitution, passing in the index of the first '$' to avoid
   1196   // repeated scanning work.
   1197   // TODO(jgruber): Possibly extend this in the future to handle more complex
   1198   // cases without runtime calls.
   1199 
   1200   TNode<Smi> const dollar_index = IndexOfDollarChar(context, replace_string);
   1201   Branch(SmiIsNegative(dollar_index), &out, &runtime);
   1202 
   1203   BIND(&runtime);
   1204   {
   1205     CSA_ASSERT(this, TaggedIsPositiveSmi(dollar_index));
   1206 
   1207     Node* const matched =
   1208         CallBuiltin(Builtins::kStringSubstring, context, subject_string,
   1209                     SmiUntag(match_start_index), SmiUntag(match_end_index));
   1210     Node* const replacement_string =
   1211         CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
   1212                     match_start_index, replace_string, dollar_index);
   1213     var_result.Bind(replacement_string);
   1214 
   1215     Goto(&out);
   1216   }
   1217 
   1218   BIND(&out);
   1219   return var_result.value();
   1220 }
   1221 
   1222 // ES6 #sec-string.prototype.repeat
   1223 TF_BUILTIN(StringPrototypeRepeat, StringBuiltinsAssembler) {
   1224   Label invalid_count(this), invalid_string_length(this),
   1225       return_emptystring(this);
   1226 
   1227   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1228   Node* const receiver = Parameter(Descriptor::kReceiver);
   1229   TNode<Object> count = CAST(Parameter(Descriptor::kCount));
   1230   Node* const string =
   1231       ToThisString(context, receiver, "String.prototype.repeat");
   1232   Node* const is_stringempty =
   1233       SmiEqual(LoadStringLengthAsSmi(string), SmiConstant(0));
   1234 
   1235   VARIABLE(
   1236       var_count, MachineRepresentation::kTagged,
   1237       ToInteger_Inline(context, count, CodeStubAssembler::kTruncateMinusZero));
   1238 
   1239   // Verifies a valid count and takes a fast path when the result will be an
   1240   // empty string.
   1241   {
   1242     Label if_count_isheapnumber(this, Label::kDeferred);
   1243 
   1244     GotoIfNot(TaggedIsSmi(var_count.value()), &if_count_isheapnumber);
   1245     {
   1246       // If count is a SMI, throw a RangeError if less than 0 or greater than
   1247       // the maximum string length.
   1248       TNode<Smi> smi_count = CAST(var_count.value());
   1249       GotoIf(SmiLessThan(smi_count, SmiConstant(0)), &invalid_count);
   1250       GotoIf(SmiEqual(smi_count, SmiConstant(0)), &return_emptystring);
   1251       GotoIf(is_stringempty, &return_emptystring);
   1252       GotoIf(SmiGreaterThan(smi_count, SmiConstant(String::kMaxLength)),
   1253              &invalid_string_length);
   1254       Return(CallBuiltin(Builtins::kStringRepeat, context, string, smi_count));
   1255     }
   1256 
   1257     // If count is a Heap Number...
   1258     // 1) If count is Infinity, throw a RangeError exception
   1259     // 2) If receiver is an empty string, return an empty string
   1260     // 3) Otherwise, throw RangeError exception
   1261     BIND(&if_count_isheapnumber);
   1262     {
   1263       CSA_ASSERT(this, IsNumberNormalized(var_count.value()));
   1264       Node* const number_value = LoadHeapNumberValue(var_count.value());
   1265       GotoIf(Float64Equal(number_value, Float64Constant(V8_INFINITY)),
   1266              &invalid_count);
   1267       GotoIf(Float64LessThan(number_value, Float64Constant(0.0)),
   1268              &invalid_count);
   1269       Branch(is_stringempty, &return_emptystring, &invalid_string_length);
   1270     }
   1271   }
   1272 
   1273   BIND(&return_emptystring);
   1274   Return(EmptyStringConstant());
   1275 
   1276   BIND(&invalid_count);
   1277   {
   1278     ThrowRangeError(context, MessageTemplate::kInvalidCountValue,
   1279                     var_count.value());
   1280   }
   1281 
   1282   BIND(&invalid_string_length);
   1283   {
   1284     CallRuntime(Runtime::kThrowInvalidStringLength, context);
   1285     Unreachable();
   1286   }
   1287 }
   1288 
   1289 // Helper with less checks
   1290 TF_BUILTIN(StringRepeat, StringBuiltinsAssembler) {
   1291   Node* const context = Parameter(Descriptor::kContext);
   1292   Node* const string = Parameter(Descriptor::kString);
   1293   TNode<Smi> const count = CAST(Parameter(Descriptor::kCount));
   1294 
   1295   CSA_ASSERT(this, IsString(string));
   1296   CSA_ASSERT(this, Word32BinaryNot(IsEmptyString(string)));
   1297   CSA_ASSERT(this, TaggedIsPositiveSmi(count));
   1298   CSA_ASSERT(this, SmiLessThanOrEqual(count, SmiConstant(String::kMaxLength)));
   1299 
   1300   // The string is repeated with the following algorithm:
   1301   //   let n = count;
   1302   //   let power_of_two_repeats = string;
   1303   //   let result = "";
   1304   //   while (true) {
   1305   //     if (n & 1) result += s;
   1306   //     n >>= 1;
   1307   //     if (n === 0) return result;
   1308   //     power_of_two_repeats += power_of_two_repeats;
   1309   //   }
   1310   VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
   1311   VARIABLE(var_temp, MachineRepresentation::kTagged, string);
   1312   TVARIABLE(Smi, var_count, count);
   1313 
   1314   Callable stringadd_callable =
   1315       CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
   1316 
   1317   Label loop(this, {&var_count, &var_result, &var_temp}), return_result(this);
   1318   Goto(&loop);
   1319   BIND(&loop);
   1320   {
   1321     {
   1322       Label next(this);
   1323       GotoIfNot(SmiToInt32(SmiAnd(var_count.value(), SmiConstant(1))), &next);
   1324       var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
   1325                                var_temp.value()));
   1326       Goto(&next);
   1327       BIND(&next);
   1328     }
   1329 
   1330     var_count = SmiShr(var_count.value(), 1);
   1331     GotoIf(SmiEqual(var_count.value(), SmiConstant(0)), &return_result);
   1332     var_temp.Bind(CallStub(stringadd_callable, context, var_temp.value(),
   1333                            var_temp.value()));
   1334     Goto(&loop);
   1335   }
   1336 
   1337   BIND(&return_result);
   1338   Return(var_result.value());
   1339 }
   1340 
   1341 // ES6 #sec-string.prototype.replace
   1342 TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
   1343   Label out(this);
   1344 
   1345   Node* const receiver = Parameter(Descriptor::kReceiver);
   1346   Node* const search = Parameter(Descriptor::kSearch);
   1347   Node* const replace = Parameter(Descriptor::kReplace);
   1348   Node* const context = Parameter(Descriptor::kContext);
   1349 
   1350   TNode<Smi> const smi_zero = SmiConstant(0);
   1351 
   1352   RequireObjectCoercible(context, receiver, "String.prototype.replace");
   1353 
   1354   // Redirect to replacer method if {search[@@replace]} is not undefined.
   1355 
   1356   MaybeCallFunctionAtSymbol(
   1357       context, search, receiver, isolate()->factory()->replace_symbol(),
   1358       [=]() {
   1359         Return(CallBuiltin(Builtins::kRegExpReplace, context, search, receiver,
   1360                            replace));
   1361       },
   1362       [=](Node* fn) {
   1363         Callable call_callable = CodeFactory::Call(isolate());
   1364         Return(CallJS(call_callable, context, fn, search, receiver, replace));
   1365       });
   1366 
   1367   // Convert {receiver} and {search} to strings.
   1368 
   1369   TNode<String> const subject_string = ToString_Inline(context, receiver);
   1370   TNode<String> const search_string = ToString_Inline(context, search);
   1371 
   1372   TNode<Smi> const subject_length = LoadStringLengthAsSmi(subject_string);
   1373   TNode<Smi> const search_length = LoadStringLengthAsSmi(search_string);
   1374 
   1375   // Fast-path single-char {search}, long cons {receiver}, and simple string
   1376   // {replace}.
   1377   {
   1378     Label next(this);
   1379 
   1380     GotoIfNot(SmiEqual(search_length, SmiConstant(1)), &next);
   1381     GotoIfNot(SmiGreaterThan(subject_length, SmiConstant(0xFF)), &next);
   1382     GotoIf(TaggedIsSmi(replace), &next);
   1383     GotoIfNot(IsString(replace), &next);
   1384 
   1385     Node* const subject_instance_type = LoadInstanceType(subject_string);
   1386     GotoIfNot(IsConsStringInstanceType(subject_instance_type), &next);
   1387 
   1388     GotoIf(TaggedIsPositiveSmi(IndexOfDollarChar(context, replace)), &next);
   1389 
   1390     // Searching by traversing a cons string tree and replace with cons of
   1391     // slices works only when the replaced string is a single character, being
   1392     // replaced by a simple string and only pays off for long strings.
   1393     // TODO(jgruber): Reevaluate if this is still beneficial.
   1394     // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames.
   1395     Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
   1396                        subject_string, search_string, replace));
   1397 
   1398     BIND(&next);
   1399   }
   1400 
   1401   // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and
   1402   // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars
   1403   // (2-byte).
   1404 
   1405   TNode<Smi> const match_start_index =
   1406       CAST(CallBuiltin(Builtins::kStringIndexOf, context, subject_string,
   1407                        search_string, smi_zero));
   1408 
   1409   // Early exit if no match found.
   1410   {
   1411     Label next(this), return_subject(this);
   1412 
   1413     GotoIfNot(SmiIsNegative(match_start_index), &next);
   1414 
   1415     // The spec requires to perform ToString(replace) if the {replace} is not
   1416     // callable even if we are going to exit here.
   1417     // Since ToString() being applied to Smi does not have side effects for
   1418     // numbers we can skip it.
   1419     GotoIf(TaggedIsSmi(replace), &return_subject);
   1420     GotoIf(IsCallableMap(LoadMap(replace)), &return_subject);
   1421 
   1422     // TODO(jgruber): Could introduce ToStringSideeffectsStub which only
   1423     // performs observable parts of ToString.
   1424     ToString_Inline(context, replace);
   1425     Goto(&return_subject);
   1426 
   1427     BIND(&return_subject);
   1428     Return(subject_string);
   1429 
   1430     BIND(&next);
   1431   }
   1432 
   1433   TNode<Smi> const match_end_index = SmiAdd(match_start_index, search_length);
   1434 
   1435   Callable stringadd_callable =
   1436       CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
   1437 
   1438   VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
   1439 
   1440   // Compute the prefix.
   1441   {
   1442     Label next(this);
   1443 
   1444     GotoIf(SmiEqual(match_start_index, smi_zero), &next);
   1445     Node* const prefix =
   1446         CallBuiltin(Builtins::kStringSubstring, context, subject_string,
   1447                     IntPtrConstant(0), SmiUntag(match_start_index));
   1448     var_result.Bind(prefix);
   1449 
   1450     Goto(&next);
   1451     BIND(&next);
   1452   }
   1453 
   1454   // Compute the string to replace with.
   1455 
   1456   Label if_iscallablereplace(this), if_notcallablereplace(this);
   1457   GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
   1458   Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace,
   1459          &if_notcallablereplace);
   1460 
   1461   BIND(&if_iscallablereplace);
   1462   {
   1463     Callable call_callable = CodeFactory::Call(isolate());
   1464     Node* const replacement =
   1465         CallJS(call_callable, context, replace, UndefinedConstant(),
   1466                search_string, match_start_index, subject_string);
   1467     Node* const replacement_string = ToString_Inline(context, replacement);
   1468     var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
   1469                              replacement_string));
   1470     Goto(&out);
   1471   }
   1472 
   1473   BIND(&if_notcallablereplace);
   1474   {
   1475     Node* const replace_string = ToString_Inline(context, replace);
   1476     Node* const replacement =
   1477         GetSubstitution(context, subject_string, match_start_index,
   1478                         match_end_index, replace_string);
   1479     var_result.Bind(
   1480         CallStub(stringadd_callable, context, var_result.value(), replacement));
   1481     Goto(&out);
   1482   }
   1483 
   1484   BIND(&out);
   1485   {
   1486     Node* const suffix =
   1487         CallBuiltin(Builtins::kStringSubstring, context, subject_string,
   1488                     SmiUntag(match_end_index), SmiUntag(subject_length));
   1489     Node* const result =
   1490         CallStub(stringadd_callable, context, var_result.value(), suffix);
   1491     Return(result);
   1492   }
   1493 }
   1494 
   1495 class StringMatchSearchAssembler : public StringBuiltinsAssembler {
   1496  public:
   1497   explicit StringMatchSearchAssembler(compiler::CodeAssemblerState* state)
   1498       : StringBuiltinsAssembler(state) {}
   1499 
   1500  protected:
   1501   enum Variant { kMatch, kSearch };
   1502 
   1503   void Generate(Variant variant, const char* method_name,
   1504                 TNode<Object> receiver, TNode<Object> maybe_regexp,
   1505                 TNode<Context> context) {
   1506     Label call_regexp_match_search(this);
   1507 
   1508     Builtins::Name builtin;
   1509     Handle<Symbol> symbol;
   1510     if (variant == kMatch) {
   1511       builtin = Builtins::kRegExpMatchFast;
   1512       symbol = isolate()->factory()->match_symbol();
   1513     } else {
   1514       builtin = Builtins::kRegExpSearchFast;
   1515       symbol = isolate()->factory()->search_symbol();
   1516     }
   1517 
   1518     RequireObjectCoercible(context, receiver, method_name);
   1519 
   1520     MaybeCallFunctionAtSymbol(
   1521         context, maybe_regexp, receiver, symbol,
   1522         [=] { Return(CallBuiltin(builtin, context, maybe_regexp, receiver)); },
   1523         [=](Node* fn) {
   1524           Callable call_callable = CodeFactory::Call(isolate());
   1525           Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
   1526         });
   1527 
   1528     // maybe_regexp is not a RegExp nor has [@@match / @@search] property.
   1529     {
   1530       RegExpBuiltinsAssembler regexp_asm(state());
   1531 
   1532       TNode<String> receiver_string = ToString_Inline(context, receiver);
   1533       TNode<Context> native_context = LoadNativeContext(context);
   1534       TNode<HeapObject> regexp_function = CAST(
   1535           LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
   1536       TNode<Map> initial_map = CAST(LoadObjectField(
   1537           regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
   1538       TNode<Object> regexp = regexp_asm.RegExpCreate(
   1539           context, initial_map, maybe_regexp, EmptyStringConstant());
   1540 
   1541       Label fast_path(this), slow_path(this);
   1542       regexp_asm.BranchIfFastRegExp(context, regexp, initial_map, &fast_path,
   1543                                     &slow_path);
   1544 
   1545       BIND(&fast_path);
   1546       Return(CallBuiltin(builtin, context, regexp, receiver_string));
   1547 
   1548       BIND(&slow_path);
   1549       {
   1550         TNode<Object> maybe_func = GetProperty(context, regexp, symbol);
   1551         Callable call_callable = CodeFactory::Call(isolate());
   1552         Return(CallJS(call_callable, context, maybe_func, regexp,
   1553                       receiver_string));
   1554       }
   1555     }
   1556   }
   1557 };
   1558 
   1559 // ES6 #sec-string.prototype.match
   1560 TF_BUILTIN(StringPrototypeMatch, StringMatchSearchAssembler) {
   1561   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
   1562   TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
   1563   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1564 
   1565   Generate(kMatch, "String.prototype.match", receiver, maybe_regexp, context);
   1566 }
   1567 
   1568 // ES #sec-string.prototype.matchAll
   1569 TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
   1570   char const* method_name = "String.prototype.matchAll";
   1571 
   1572   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1573   TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
   1574   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
   1575   TNode<Context> native_context = LoadNativeContext(context);
   1576 
   1577   // 1. Let O be ? RequireObjectCoercible(this value).
   1578   RequireObjectCoercible(context, receiver, method_name);
   1579 
   1580   // 2. If regexp is neither undefined nor null, then
   1581   Label return_match_all_iterator(this),
   1582       tostring_and_return_match_all_iterator(this, Label::kDeferred);
   1583   TVARIABLE(BoolT, var_is_fast_regexp);
   1584   TVARIABLE(String, var_receiver_string);
   1585   GotoIf(IsNullOrUndefined(maybe_regexp),
   1586          &tostring_and_return_match_all_iterator);
   1587   {
   1588     // a. Let matcher be ? GetMethod(regexp, @@matchAll).
   1589     // b. If matcher is not undefined, then
   1590     //   i. Return ? Call(matcher, regexp,  O ).
   1591     auto if_regexp_call = [&] {
   1592       // MaybeCallFunctionAtSymbol guarantees fast path is chosen only if
   1593       // maybe_regexp is a fast regexp and receiver is a string.
   1594       var_receiver_string = CAST(receiver);
   1595       CSA_ASSERT(this, IsString(var_receiver_string.value()));
   1596       var_is_fast_regexp = Int32TrueConstant();
   1597       Goto(&return_match_all_iterator);
   1598     };
   1599     auto if_generic_call = [=](Node* fn) {
   1600       Callable call_callable = CodeFactory::Call(isolate());
   1601       Return(CallJS(call_callable, context, fn, maybe_regexp, receiver));
   1602     };
   1603     MaybeCallFunctionAtSymbol(context, maybe_regexp, receiver,
   1604                               isolate()->factory()->match_all_symbol(),
   1605                               if_regexp_call, if_generic_call);
   1606     Goto(&tostring_and_return_match_all_iterator);
   1607   }
   1608   BIND(&tostring_and_return_match_all_iterator);
   1609   {
   1610     var_receiver_string = ToString_Inline(context, receiver);
   1611     var_is_fast_regexp = Int32FalseConstant();
   1612     Goto(&return_match_all_iterator);
   1613   }
   1614   BIND(&return_match_all_iterator);
   1615   {
   1616     // 3. Return ? MatchAllIterator(regexp, O).
   1617     RegExpBuiltinsAssembler regexp_asm(state());
   1618     TNode<Object> iterator = regexp_asm.MatchAllIterator(
   1619         context, native_context, maybe_regexp, var_receiver_string.value(),
   1620         var_is_fast_regexp.value(), method_name);
   1621     Return(iterator);
   1622   }
   1623 }
   1624 
   1625 class StringPadAssembler : public StringBuiltinsAssembler {
   1626  public:
   1627   explicit StringPadAssembler(compiler::CodeAssemblerState* state)
   1628       : StringBuiltinsAssembler(state) {}
   1629 
   1630  protected:
   1631   enum Variant { kStart, kEnd };
   1632 
   1633   void Generate(Variant variant, const char* method_name, TNode<IntPtrT> argc,
   1634                 TNode<Context> context) {
   1635     CodeStubArguments arguments(this, argc);
   1636     Node* const receiver = arguments.GetReceiver();
   1637     Node* const receiver_string = ToThisString(context, receiver, method_name);
   1638     TNode<Smi> const string_length = LoadStringLengthAsSmi(receiver_string);
   1639 
   1640     TVARIABLE(String, var_fill_string, StringConstant(" "));
   1641     TVARIABLE(IntPtrT, var_fill_length, IntPtrConstant(1));
   1642 
   1643     Label check_fill(this), dont_pad(this), invalid_string_length(this),
   1644         pad(this);
   1645 
   1646     // If no max_length was provided, return the string.
   1647     GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &dont_pad);
   1648 
   1649     TNode<Number> const max_length =
   1650         ToLength_Inline(context, arguments.AtIndex(0));
   1651     CSA_ASSERT(this, IsNumberNormalized(max_length));
   1652 
   1653     // If max_length <= string_length, return the string.
   1654     GotoIfNot(TaggedIsSmi(max_length), &check_fill);
   1655     Branch(SmiLessThanOrEqual(CAST(max_length), string_length), &dont_pad,
   1656            &check_fill);
   1657 
   1658     BIND(&check_fill);
   1659     {
   1660       GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &pad);
   1661       Node* const fill = arguments.AtIndex(1);
   1662       GotoIf(IsUndefined(fill), &pad);
   1663 
   1664       var_fill_string = ToString_Inline(context, fill);
   1665       var_fill_length = LoadStringLengthAsWord(var_fill_string.value());
   1666       Branch(WordEqual(var_fill_length.value(), IntPtrConstant(0)), &dont_pad,
   1667              &pad);
   1668     }
   1669 
   1670     BIND(&pad);
   1671     {
   1672       CSA_ASSERT(this,
   1673                  IntPtrGreaterThan(var_fill_length.value(), IntPtrConstant(0)));
   1674 
   1675       // Throw if max_length is greater than String::kMaxLength.
   1676       GotoIfNot(TaggedIsSmi(max_length), &invalid_string_length);
   1677       TNode<Smi> smi_max_length = CAST(max_length);
   1678       GotoIfNot(
   1679           SmiLessThanOrEqual(smi_max_length, SmiConstant(String::kMaxLength)),
   1680           &invalid_string_length);
   1681 
   1682       Callable stringadd_callable =
   1683           CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
   1684       CSA_ASSERT(this, SmiGreaterThan(smi_max_length, string_length));
   1685       TNode<Smi> const pad_length = SmiSub(smi_max_length, string_length);
   1686 
   1687       VARIABLE(var_pad, MachineRepresentation::kTagged);
   1688       Label single_char_fill(this), multi_char_fill(this), return_result(this);
   1689       Branch(IntPtrEqual(var_fill_length.value(), IntPtrConstant(1)),
   1690              &single_char_fill, &multi_char_fill);
   1691 
   1692       // Fast path for a single character fill.  No need to calculate number of
   1693       // repetitions or remainder.
   1694       BIND(&single_char_fill);
   1695       {
   1696         var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
   1697                                  static_cast<Node*>(var_fill_string.value()),
   1698                                  pad_length));
   1699         Goto(&return_result);
   1700       }
   1701       BIND(&multi_char_fill);
   1702       {
   1703         TNode<Int32T> const fill_length_word32 =
   1704             TruncateIntPtrToInt32(var_fill_length.value());
   1705         TNode<Int32T> const pad_length_word32 = SmiToInt32(pad_length);
   1706         TNode<Int32T> const repetitions_word32 =
   1707             Int32Div(pad_length_word32, fill_length_word32);
   1708         TNode<Int32T> const remaining_word32 =
   1709             Int32Mod(pad_length_word32, fill_length_word32);
   1710 
   1711         var_pad.Bind(CallBuiltin(Builtins::kStringRepeat, context,
   1712                                  var_fill_string.value(),
   1713                                  SmiFromInt32(repetitions_word32)));
   1714 
   1715         GotoIfNot(remaining_word32, &return_result);
   1716         {
   1717           Node* const remainder_string = CallBuiltin(
   1718               Builtins::kStringSubstring, context, var_fill_string.value(),
   1719               IntPtrConstant(0), ChangeInt32ToIntPtr(remaining_word32));
   1720           var_pad.Bind(CallStub(stringadd_callable, context, var_pad.value(),
   1721                                 remainder_string));
   1722           Goto(&return_result);
   1723         }
   1724       }
   1725       BIND(&return_result);
   1726       CSA_ASSERT(this,
   1727                  SmiEqual(pad_length, LoadStringLengthAsSmi(var_pad.value())));
   1728       arguments.PopAndReturn(variant == kStart
   1729                                  ? CallStub(stringadd_callable, context,
   1730                                             var_pad.value(), receiver_string)
   1731                                  : CallStub(stringadd_callable, context,
   1732                                             receiver_string, var_pad.value()));
   1733     }
   1734     BIND(&dont_pad);
   1735     arguments.PopAndReturn(receiver_string);
   1736     BIND(&invalid_string_length);
   1737     {
   1738       CallRuntime(Runtime::kThrowInvalidStringLength, context);
   1739       Unreachable();
   1740     }
   1741   }
   1742 };
   1743 
   1744 TF_BUILTIN(StringPrototypePadEnd, StringPadAssembler) {
   1745   TNode<IntPtrT> argc =
   1746       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   1747   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1748 
   1749   Generate(kEnd, "String.prototype.padEnd", argc, context);
   1750 }
   1751 
   1752 TF_BUILTIN(StringPrototypePadStart, StringPadAssembler) {
   1753   TNode<IntPtrT> argc =
   1754       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   1755   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1756 
   1757   Generate(kStart, "String.prototype.padStart", argc, context);
   1758 }
   1759 
   1760 // ES6 #sec-string.prototype.search
   1761 TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
   1762   TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
   1763   TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
   1764   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1765   Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context);
   1766 }
   1767 
   1768 // ES6 section 21.1.3.18 String.prototype.slice ( start, end )
   1769 TF_BUILTIN(StringPrototypeSlice, StringBuiltinsAssembler) {
   1770   Label out(this);
   1771   TVARIABLE(IntPtrT, var_start);
   1772   TVARIABLE(IntPtrT, var_end);
   1773 
   1774   const int kStart = 0;
   1775   const int kEnd = 1;
   1776   Node* argc =
   1777       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   1778   CodeStubArguments args(this, argc);
   1779   Node* const receiver = args.GetReceiver();
   1780   TNode<Object> start = args.GetOptionalArgumentValue(kStart);
   1781   TNode<Object> end = args.GetOptionalArgumentValue(kEnd);
   1782   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1783 
   1784   // 1. Let O be ? RequireObjectCoercible(this value).
   1785   RequireObjectCoercible(context, receiver, "String.prototype.slice");
   1786 
   1787   // 2. Let S be ? ToString(O).
   1788   TNode<String> const subject_string =
   1789       CAST(CallBuiltin(Builtins::kToString, context, receiver));
   1790 
   1791   // 3. Let len be the number of elements in S.
   1792   TNode<IntPtrT> const length = LoadStringLengthAsWord(subject_string);
   1793 
   1794   // Convert {start} to a relative index.
   1795   var_start = ConvertToRelativeIndex(context, start, length);
   1796 
   1797   // 5. If end is undefined, let intEnd be len;
   1798   var_end = length;
   1799   GotoIf(IsUndefined(end), &out);
   1800 
   1801   // Convert {end} to a relative index.
   1802   var_end = ConvertToRelativeIndex(context, end, length);
   1803   Goto(&out);
   1804 
   1805   Label return_emptystring(this);
   1806   BIND(&out);
   1807   {
   1808     GotoIf(IntPtrLessThanOrEqual(var_end.value(), var_start.value()),
   1809            &return_emptystring);
   1810     TNode<String> const result =
   1811         SubString(subject_string, var_start.value(), var_end.value());
   1812     args.PopAndReturn(result);
   1813   }
   1814 
   1815   BIND(&return_emptystring);
   1816   args.PopAndReturn(EmptyStringConstant());
   1817 }
   1818 
   1819 TNode<JSArray> StringBuiltinsAssembler::StringToArray(
   1820     TNode<Context> context, TNode<String> subject_string,
   1821     TNode<Smi> subject_length, TNode<Number> limit_number) {
   1822   CSA_ASSERT(this, SmiGreaterThan(subject_length, SmiConstant(0)));
   1823 
   1824   Label done(this), call_runtime(this, Label::kDeferred),
   1825       fill_thehole_and_call_runtime(this, Label::kDeferred);
   1826   TVARIABLE(JSArray, result_array);
   1827 
   1828   TNode<Int32T> instance_type = LoadInstanceType(subject_string);
   1829   GotoIfNot(IsOneByteStringInstanceType(instance_type), &call_runtime);
   1830 
   1831   // Try to use cached one byte characters.
   1832   {
   1833     TNode<Smi> length_smi =
   1834         Select<Smi>(TaggedIsSmi(limit_number),
   1835                     [=] { return SmiMin(CAST(limit_number), subject_length); },
   1836                     [=] { return subject_length; });
   1837     TNode<IntPtrT> length = SmiToIntPtr(length_smi);
   1838 
   1839     ToDirectStringAssembler to_direct(state(), subject_string);
   1840     to_direct.TryToDirect(&call_runtime);
   1841     TNode<FixedArray> elements = CAST(AllocateFixedArray(
   1842         PACKED_ELEMENTS, length, AllocationFlag::kAllowLargeObjectAllocation));
   1843     // Don't allocate anything while {string_data} is live!
   1844     TNode<RawPtrT> string_data = UncheckedCast<RawPtrT>(
   1845         to_direct.PointerToData(&fill_thehole_and_call_runtime));
   1846     TNode<IntPtrT> string_data_offset = to_direct.offset();
   1847     TNode<Object> cache = LoadRoot(Heap::kSingleCharacterStringCacheRootIndex);
   1848 
   1849     BuildFastLoop(
   1850         IntPtrConstant(0), length,
   1851         [&](Node* index) {
   1852           // TODO(jkummerow): Implement a CSA version of DisallowHeapAllocation
   1853           // and use that to guard ToDirectStringAssembler.PointerToData().
   1854           CSA_ASSERT(this, WordEqual(to_direct.PointerToData(&call_runtime),
   1855                                      string_data));
   1856           TNode<Int32T> char_code =
   1857               UncheckedCast<Int32T>(Load(MachineType::Uint8(), string_data,
   1858                                          IntPtrAdd(index, string_data_offset)));
   1859           Node* code_index = ChangeUint32ToWord(char_code);
   1860           TNode<Object> entry = LoadFixedArrayElement(CAST(cache), code_index);
   1861 
   1862           // If we cannot find a char in the cache, fill the hole for the fixed
   1863           // array, and call runtime.
   1864           GotoIf(IsUndefined(entry), &fill_thehole_and_call_runtime);
   1865 
   1866           StoreFixedArrayElement(elements, index, entry);
   1867         },
   1868         1, ParameterMode::INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
   1869 
   1870     TNode<Map> array_map = LoadJSArrayElementsMap(PACKED_ELEMENTS, context);
   1871     result_array = CAST(
   1872         AllocateUninitializedJSArrayWithoutElements(array_map, length_smi));
   1873     StoreObjectField(result_array.value(), JSObject::kElementsOffset, elements);
   1874     Goto(&done);
   1875 
   1876     BIND(&fill_thehole_and_call_runtime);
   1877     {
   1878       FillFixedArrayWithValue(PACKED_ELEMENTS, elements, IntPtrConstant(0),
   1879                               length, Heap::kTheHoleValueRootIndex);
   1880       Goto(&call_runtime);
   1881     }
   1882   }
   1883 
   1884   BIND(&call_runtime);
   1885   {
   1886     result_array = CAST(CallRuntime(Runtime::kStringToArray, context,
   1887                                     subject_string, limit_number));
   1888     Goto(&done);
   1889   }
   1890 
   1891   BIND(&done);
   1892   return result_array.value();
   1893 }
   1894 
   1895 // ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
   1896 TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
   1897   const int kSeparatorArg = 0;
   1898   const int kLimitArg = 1;
   1899 
   1900   Node* const argc =
   1901       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   1902   CodeStubArguments args(this, argc);
   1903 
   1904   Node* const receiver = args.GetReceiver();
   1905   Node* const separator = args.GetOptionalArgumentValue(kSeparatorArg);
   1906   Node* const limit = args.GetOptionalArgumentValue(kLimitArg);
   1907   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   1908 
   1909   TNode<Smi> smi_zero = SmiConstant(0);
   1910 
   1911   RequireObjectCoercible(context, receiver, "String.prototype.split");
   1912 
   1913   // Redirect to splitter method if {separator[@@split]} is not undefined.
   1914 
   1915   MaybeCallFunctionAtSymbol(
   1916       context, separator, receiver, isolate()->factory()->split_symbol(),
   1917       [&]() {
   1918         args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context,
   1919                                       separator, receiver, limit));
   1920       },
   1921       [&](Node* fn) {
   1922         Callable call_callable = CodeFactory::Call(isolate());
   1923         args.PopAndReturn(
   1924             CallJS(call_callable, context, fn, separator, receiver, limit));
   1925       });
   1926 
   1927   // String and integer conversions.
   1928 
   1929   TNode<String> subject_string = ToString_Inline(context, receiver);
   1930   TNode<Number> limit_number = Select<Number>(
   1931       IsUndefined(limit), [=] { return NumberConstant(kMaxUInt32); },
   1932       [=] { return ToUint32(context, limit); });
   1933   Node* const separator_string = ToString_Inline(context, separator);
   1934 
   1935   Label return_empty_array(this);
   1936 
   1937   // Shortcut for {limit} == 0.
   1938   GotoIf(WordEqual<Object, Object>(limit_number, smi_zero),
   1939          &return_empty_array);
   1940 
   1941   // ECMA-262 says that if {separator} is undefined, the result should
   1942   // be an array of size 1 containing the entire string.
   1943   {
   1944     Label next(this);
   1945     GotoIfNot(IsUndefined(separator), &next);
   1946 
   1947     const ElementsKind kind = PACKED_ELEMENTS;
   1948     Node* const native_context = LoadNativeContext(context);
   1949     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
   1950 
   1951     Node* const length = SmiConstant(1);
   1952     Node* const capacity = IntPtrConstant(1);
   1953     Node* const result = AllocateJSArray(kind, array_map, capacity, length);
   1954 
   1955     TNode<FixedArray> const fixed_array = CAST(LoadElements(result));
   1956     StoreFixedArrayElement(fixed_array, 0, subject_string);
   1957 
   1958     args.PopAndReturn(result);
   1959 
   1960     BIND(&next);
   1961   }
   1962 
   1963   // If the separator string is empty then return the elements in the subject.
   1964   {
   1965     Label next(this);
   1966     GotoIfNot(SmiEqual(LoadStringLengthAsSmi(separator_string), smi_zero),
   1967               &next);
   1968 
   1969     TNode<Smi> subject_length = LoadStringLengthAsSmi(subject_string);
   1970     GotoIf(SmiEqual(subject_length, smi_zero), &return_empty_array);
   1971 
   1972     args.PopAndReturn(
   1973         StringToArray(context, subject_string, subject_length, limit_number));
   1974 
   1975     BIND(&next);
   1976   }
   1977 
   1978   Node* const result =
   1979       CallRuntime(Runtime::kStringSplit, context, subject_string,
   1980                   separator_string, limit_number);
   1981   args.PopAndReturn(result);
   1982 
   1983   BIND(&return_empty_array);
   1984   {
   1985     const ElementsKind kind = PACKED_ELEMENTS;
   1986     Node* const native_context = LoadNativeContext(context);
   1987     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
   1988 
   1989     Node* const length = smi_zero;
   1990     Node* const capacity = IntPtrConstant(0);
   1991     Node* const result = AllocateJSArray(kind, array_map, capacity, length);
   1992 
   1993     args.PopAndReturn(result);
   1994   }
   1995 }
   1996 
   1997 // ES6 #sec-string.prototype.substr
   1998 TF_BUILTIN(StringPrototypeSubstr, StringBuiltinsAssembler) {
   1999   const int kStartArg = 0;
   2000   const int kLengthArg = 1;
   2001 
   2002   Node* const argc =
   2003       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   2004   CodeStubArguments args(this, argc);
   2005 
   2006   Node* const receiver = args.GetReceiver();
   2007   TNode<Object> start = args.GetOptionalArgumentValue(kStartArg);
   2008   TNode<Object> length = args.GetOptionalArgumentValue(kLengthArg);
   2009   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   2010 
   2011   Label out(this);
   2012 
   2013   TVARIABLE(IntPtrT, var_start);
   2014   TVARIABLE(Number, var_length);
   2015 
   2016   TNode<IntPtrT> const zero = IntPtrConstant(0);
   2017 
   2018   // Check that {receiver} is coercible to Object and convert it to a String.
   2019   TNode<String> const string =
   2020       ToThisString(context, receiver, "String.prototype.substr");
   2021 
   2022   TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
   2023 
   2024   // Convert {start} to a relative index.
   2025   var_start = ConvertToRelativeIndex(context, start, string_length);
   2026 
   2027   // Conversions and bounds-checks for {length}.
   2028   Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
   2029 
   2030   // Default to {string_length} if {length} is undefined.
   2031   {
   2032     Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
   2033     Branch(IsUndefined(length), &if_isundefined, &if_isnotundefined);
   2034 
   2035     BIND(&if_isundefined);
   2036     var_length = SmiTag(string_length);
   2037     Goto(&if_issmi);
   2038 
   2039     BIND(&if_isnotundefined);
   2040     var_length = ToInteger_Inline(context, length,
   2041                                   CodeStubAssembler::kTruncateMinusZero);
   2042   }
   2043 
   2044   TVARIABLE(IntPtrT, var_result_length);
   2045 
   2046   Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
   2047 
   2048   // Set {length} to min(max({length}, 0), {string_length} - {start}
   2049   BIND(&if_issmi);
   2050   {
   2051     TNode<IntPtrT> const positive_length =
   2052         IntPtrMax(SmiUntag(CAST(var_length.value())), zero);
   2053     TNode<IntPtrT> const minimal_length =
   2054         IntPtrSub(string_length, var_start.value());
   2055     var_result_length = IntPtrMin(positive_length, minimal_length);
   2056 
   2057     GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
   2058     args.PopAndReturn(EmptyStringConstant());
   2059   }
   2060 
   2061   BIND(&if_isheapnumber);
   2062   {
   2063     // If {length} is a heap number, it is definitely out of bounds. There are
   2064     // two cases according to the spec: if it is negative, "" is returned; if
   2065     // it is positive, then length is set to {string_length} - {start}.
   2066 
   2067     CSA_ASSERT(this, IsHeapNumber(CAST(var_length.value())));
   2068 
   2069     Label if_isnegative(this), if_ispositive(this);
   2070     TNode<Float64T> const float_zero = Float64Constant(0.);
   2071     TNode<Float64T> const length_float =
   2072         LoadHeapNumberValue(CAST(var_length.value()));
   2073     Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
   2074            &if_ispositive);
   2075 
   2076     BIND(&if_isnegative);
   2077     args.PopAndReturn(EmptyStringConstant());
   2078 
   2079     BIND(&if_ispositive);
   2080     {
   2081       var_result_length = IntPtrSub(string_length, var_start.value());
   2082       GotoIfNot(IntPtrLessThanOrEqual(var_result_length.value(), zero), &out);
   2083       args.PopAndReturn(EmptyStringConstant());
   2084     }
   2085   }
   2086 
   2087   BIND(&out);
   2088   {
   2089     TNode<IntPtrT> const end =
   2090         IntPtrAdd(var_start.value(), var_result_length.value());
   2091     args.PopAndReturn(SubString(string, var_start.value(), end));
   2092   }
   2093 }
   2094 
   2095 TNode<Smi> StringBuiltinsAssembler::ToSmiBetweenZeroAnd(
   2096     SloppyTNode<Context> context, SloppyTNode<Object> value,
   2097     SloppyTNode<Smi> limit) {
   2098   Label out(this);
   2099   TVARIABLE(Smi, var_result);
   2100 
   2101   TNode<Number> const value_int =
   2102       ToInteger_Inline(context, value, CodeStubAssembler::kTruncateMinusZero);
   2103 
   2104   Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
   2105   Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
   2106 
   2107   BIND(&if_issmi);
   2108   {
   2109     TNode<Smi> value_smi = CAST(value_int);
   2110     Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
   2111     Branch(SmiAbove(value_smi, limit), &if_isoutofbounds, &if_isinbounds);
   2112 
   2113     BIND(&if_isinbounds);
   2114     {
   2115       var_result = CAST(value_int);
   2116       Goto(&out);
   2117     }
   2118 
   2119     BIND(&if_isoutofbounds);
   2120     {
   2121       TNode<Smi> const zero = SmiConstant(0);
   2122       var_result =
   2123           SelectConstant<Smi>(SmiLessThan(value_smi, zero), zero, limit);
   2124       Goto(&out);
   2125     }
   2126   }
   2127 
   2128   BIND(&if_isnotsmi);
   2129   {
   2130     // {value} is a heap number - in this case, it is definitely out of bounds.
   2131     TNode<HeapNumber> value_int_hn = CAST(value_int);
   2132 
   2133     TNode<Float64T> const float_zero = Float64Constant(0.);
   2134     TNode<Smi> const smi_zero = SmiConstant(0);
   2135     TNode<Float64T> const value_float = LoadHeapNumberValue(value_int_hn);
   2136     var_result = SelectConstant<Smi>(Float64LessThan(value_float, float_zero),
   2137                                      smi_zero, limit);
   2138     Goto(&out);
   2139   }
   2140 
   2141   BIND(&out);
   2142   return var_result.value();
   2143 }
   2144 
   2145 TF_BUILTIN(StringSubstring, CodeStubAssembler) {
   2146   TNode<String> string = CAST(Parameter(Descriptor::kString));
   2147   TNode<IntPtrT> from = UncheckedCast<IntPtrT>(Parameter(Descriptor::kFrom));
   2148   TNode<IntPtrT> to = UncheckedCast<IntPtrT>(Parameter(Descriptor::kTo));
   2149 
   2150   Return(SubString(string, from, to));
   2151 }
   2152 
   2153 // ES6 #sec-string.prototype.substring
   2154 TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
   2155   const int kStartArg = 0;
   2156   const int kEndArg = 1;
   2157 
   2158   Node* const argc =
   2159       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   2160   CodeStubArguments args(this, argc);
   2161 
   2162   Node* const receiver = args.GetReceiver();
   2163   Node* const start = args.GetOptionalArgumentValue(kStartArg);
   2164   Node* const end = args.GetOptionalArgumentValue(kEndArg);
   2165   Node* const context = Parameter(Descriptor::kContext);
   2166 
   2167   Label out(this);
   2168 
   2169   TVARIABLE(Smi, var_start);
   2170   TVARIABLE(Smi, var_end);
   2171 
   2172   // Check that {receiver} is coercible to Object and convert it to a String.
   2173   TNode<String> const string =
   2174       ToThisString(context, receiver, "String.prototype.substring");
   2175 
   2176   TNode<Smi> const length = LoadStringLengthAsSmi(string);
   2177 
   2178   // Conversion and bounds-checks for {start}.
   2179   var_start = ToSmiBetweenZeroAnd(context, start, length);
   2180 
   2181   // Conversion and bounds-checks for {end}.
   2182   {
   2183     var_end = length;
   2184     GotoIf(IsUndefined(end), &out);
   2185 
   2186     var_end = ToSmiBetweenZeroAnd(context, end, length);
   2187 
   2188     Label if_endislessthanstart(this);
   2189     Branch(SmiLessThan(var_end.value(), var_start.value()),
   2190            &if_endislessthanstart, &out);
   2191 
   2192     BIND(&if_endislessthanstart);
   2193     {
   2194       TNode<Smi> const tmp = var_end.value();
   2195       var_end = var_start.value();
   2196       var_start = tmp;
   2197       Goto(&out);
   2198     }
   2199   }
   2200 
   2201   BIND(&out);
   2202   {
   2203     args.PopAndReturn(SubString(string, SmiUntag(var_start.value()),
   2204                                 SmiUntag(var_end.value())));
   2205   }
   2206 }
   2207 
   2208 // ES6 #sec-string.prototype.trim
   2209 TF_BUILTIN(StringPrototypeTrim, StringTrimAssembler) {
   2210   TNode<IntPtrT> argc =
   2211       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   2212   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   2213 
   2214   Generate(String::kTrim, "String.prototype.trim", argc, context);
   2215 }
   2216 
   2217 // https://github.com/tc39/proposal-string-left-right-trim
   2218 TF_BUILTIN(StringPrototypeTrimStart, StringTrimAssembler) {
   2219   TNode<IntPtrT> argc =
   2220       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   2221   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   2222 
   2223   Generate(String::kTrimStart, "String.prototype.trimLeft", argc, context);
   2224 }
   2225 
   2226 // https://github.com/tc39/proposal-string-left-right-trim
   2227 TF_BUILTIN(StringPrototypeTrimEnd, StringTrimAssembler) {
   2228   TNode<IntPtrT> argc =
   2229       ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
   2230   TNode<Context> context = CAST(Parameter(Descriptor::kContext));
   2231 
   2232   Generate(String::kTrimEnd, "String.prototype.trimRight", argc, context);
   2233 }
   2234 
   2235 void StringTrimAssembler::Generate(String::TrimMode mode,
   2236                                    const char* method_name, TNode<IntPtrT> argc,
   2237                                    TNode<Context> context) {
   2238   Label return_emptystring(this), if_runtime(this);
   2239 
   2240   CodeStubArguments arguments(this, argc);
   2241   Node* const receiver = arguments.GetReceiver();
   2242 
   2243   // Check that {receiver} is coercible to Object and convert it to a String.
   2244   TNode<String> const string = ToThisString(context, receiver, method_name);
   2245   TNode<IntPtrT> const string_length = LoadStringLengthAsWord(string);
   2246 
   2247   ToDirectStringAssembler to_direct(state(), string);
   2248   to_direct.TryToDirect(&if_runtime);
   2249   Node* const string_data = to_direct.PointerToData(&if_runtime);
   2250   Node* const instance_type = to_direct.instance_type();
   2251   Node* const is_stringonebyte = IsOneByteStringInstanceType(instance_type);
   2252   Node* const string_data_offset = to_direct.offset();
   2253 
   2254   TVARIABLE(IntPtrT, var_start, IntPtrConstant(0));
   2255   TVARIABLE(IntPtrT, var_end, IntPtrSub(string_length, IntPtrConstant(1)));
   2256 
   2257   if (mode == String::kTrimStart || mode == String::kTrim) {
   2258     ScanForNonWhiteSpaceOrLineTerminator(string_data, string_data_offset,
   2259                                          is_stringonebyte, &var_start,
   2260                                          string_length, 1, &return_emptystring);
   2261   }
   2262   if (mode == String::kTrimEnd || mode == String::kTrim) {
   2263     ScanForNonWhiteSpaceOrLineTerminator(
   2264         string_data, string_data_offset, is_stringonebyte, &var_end,
   2265         IntPtrConstant(-1), -1, &return_emptystring);
   2266   }
   2267 
   2268   arguments.PopAndReturn(
   2269       SubString(string, var_start.value(),
   2270                 IntPtrAdd(var_end.value(), IntPtrConstant(1))));
   2271 
   2272   BIND(&if_runtime);
   2273   arguments.PopAndReturn(
   2274       CallRuntime(Runtime::kStringTrim, context, string, SmiConstant(mode)));
   2275 
   2276   BIND(&return_emptystring);
   2277   arguments.PopAndReturn(EmptyStringConstant());
   2278 }
   2279 
   2280 void StringTrimAssembler::ScanForNonWhiteSpaceOrLineTerminator(
   2281     Node* const string_data, Node* const string_data_offset,
   2282     Node* const is_stringonebyte, Variable* const var_index, Node* const end,
   2283     int increment, Label* const if_none_found) {
   2284   Label if_stringisonebyte(this), out(this);
   2285 
   2286   GotoIf(is_stringonebyte, &if_stringisonebyte);
   2287 
   2288   // Two Byte String
   2289   BuildLoop(
   2290       var_index, end, increment, if_none_found, &out, [&](Node* const index) {
   2291         return Load(
   2292             MachineType::Uint16(), string_data,
   2293             WordShl(IntPtrAdd(index, string_data_offset), IntPtrConstant(1)));
   2294       });
   2295 
   2296   BIND(&if_stringisonebyte);
   2297   BuildLoop(var_index, end, increment, if_none_found, &out,
   2298             [&](Node* const index) {
   2299               return Load(MachineType::Uint8(), string_data,
   2300                           IntPtrAdd(index, string_data_offset));
   2301             });
   2302 
   2303   BIND(&out);
   2304 }
   2305 
   2306 void StringTrimAssembler::BuildLoop(Variable* const var_index, Node* const end,
   2307                                     int increment, Label* const if_none_found,
   2308                                     Label* const out,
   2309                                     std::function<Node*(Node*)> get_character) {
   2310   Label loop(this, var_index);
   2311   Goto(&loop);
   2312   BIND(&loop);
   2313   {
   2314     Node* const index = var_index->value();
   2315     GotoIf(IntPtrEqual(index, end), if_none_found);
   2316     GotoIfNotWhiteSpaceOrLineTerminator(
   2317         UncheckedCast<Uint32T>(get_character(index)), out);
   2318     Increment(var_index, increment);
   2319     Goto(&loop);
   2320   }
   2321 }
   2322 
   2323 void StringTrimAssembler::GotoIfNotWhiteSpaceOrLineTerminator(
   2324     Node* const char_code, Label* const if_not_whitespace) {
   2325   Label out(this);
   2326 
   2327   // 0x0020 - SPACE (Intentionally out of order to fast path a commmon case)
   2328   GotoIf(Word32Equal(char_code, Int32Constant(0x0020)), &out);
   2329 
   2330   // 0x0009 - HORIZONTAL TAB
   2331   GotoIf(Uint32LessThan(char_code, Int32Constant(0x0009)), if_not_whitespace);
   2332   // 0x000A - LINE FEED OR NEW LINE
   2333   // 0x000B - VERTICAL TAB
   2334   // 0x000C - FORMFEED
   2335   // 0x000D - HORIZONTAL TAB
   2336   GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x000D)), &out);
   2337 
   2338   // Common Non-whitespace characters
   2339   GotoIf(Uint32LessThan(char_code, Int32Constant(0x00A0)), if_not_whitespace);
   2340 
   2341   // 0x00A0 - NO-BREAK SPACE
   2342   GotoIf(Word32Equal(char_code, Int32Constant(0x00A0)), &out);
   2343 
   2344   // 0x1680 - Ogham Space Mark
   2345   GotoIf(Word32Equal(char_code, Int32Constant(0x1680)), &out);
   2346 
   2347   // 0x2000 - EN QUAD
   2348   GotoIf(Uint32LessThan(char_code, Int32Constant(0x2000)), if_not_whitespace);
   2349   // 0x2001 - EM QUAD
   2350   // 0x2002 - EN SPACE
   2351   // 0x2003 - EM SPACE
   2352   // 0x2004 - THREE-PER-EM SPACE
   2353   // 0x2005 - FOUR-PER-EM SPACE
   2354   // 0x2006 - SIX-PER-EM SPACE
   2355   // 0x2007 - FIGURE SPACE
   2356   // 0x2008 - PUNCTUATION SPACE
   2357   // 0x2009 - THIN SPACE
   2358   // 0x200A - HAIR SPACE
   2359   GotoIf(Uint32LessThanOrEqual(char_code, Int32Constant(0x200A)), &out);
   2360 
   2361   // 0x2028 - LINE SEPARATOR
   2362   GotoIf(Word32Equal(char_code, Int32Constant(0x2028)), &out);
   2363   // 0x2029 - PARAGRAPH SEPARATOR
   2364   GotoIf(Word32Equal(char_code, Int32Constant(0x2029)), &out);
   2365   // 0x202F - NARROW NO-BREAK SPACE
   2366   GotoIf(Word32Equal(char_code, Int32Constant(0x202F)), &out);
   2367   // 0x205F - MEDIUM MATHEMATICAL SPACE
   2368   GotoIf(Word32Equal(char_code, Int32Constant(0x205F)), &out);
   2369   // 0xFEFF - BYTE ORDER MARK
   2370   GotoIf(Word32Equal(char_code, Int32Constant(0xFEFF)), &out);
   2371   // 0x3000 - IDEOGRAPHIC SPACE
   2372   Branch(Word32Equal(char_code, Int32Constant(0x3000)), &out,
   2373          if_not_whitespace);
   2374 
   2375   BIND(&out);
   2376 }
   2377 
   2378 // ES6 #sec-string.prototype.tostring
   2379 TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) {
   2380   Node* context = Parameter(Descriptor::kContext);
   2381   Node* receiver = Parameter(Descriptor::kReceiver);
   2382 
   2383   Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
   2384                              "String.prototype.toString");
   2385   Return(result);
   2386 }
   2387 
   2388 // ES6 #sec-string.prototype.valueof
   2389 TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) {
   2390   Node* context = Parameter(Descriptor::kContext);
   2391   Node* receiver = Parameter(Descriptor::kReceiver);
   2392 
   2393   Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
   2394                              "String.prototype.valueOf");
   2395   Return(result);
   2396 }
   2397 
   2398 TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) {
   2399   Node* context = Parameter(Descriptor::kContext);
   2400   Node* receiver = Parameter(Descriptor::kReceiver);
   2401 
   2402   Node* string =
   2403       ToThisString(context, receiver, "String.prototype[Symbol.iterator]");
   2404 
   2405   Node* native_context = LoadNativeContext(context);
   2406   Node* map =
   2407       LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX);
   2408   Node* iterator = Allocate(JSStringIterator::kSize);
   2409   StoreMapNoWriteBarrier(iterator, map);
   2410   StoreObjectFieldRoot(iterator, JSValue::kPropertiesOrHashOffset,
   2411                        Heap::kEmptyFixedArrayRootIndex);
   2412   StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
   2413                        Heap::kEmptyFixedArrayRootIndex);
   2414   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset,
   2415                                  string);
   2416   Node* index = SmiConstant(0);
   2417   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
   2418                                  index);
   2419   Return(iterator);
   2420 }
   2421 
   2422 // Return the |word32| codepoint at {index}. Supports SeqStrings and
   2423 // ExternalStrings.
   2424 TNode<Int32T> StringBuiltinsAssembler::LoadSurrogatePairAt(
   2425     SloppyTNode<String> string, SloppyTNode<IntPtrT> length,
   2426     SloppyTNode<IntPtrT> index, UnicodeEncoding encoding) {
   2427   Label handle_surrogate_pair(this), return_result(this);
   2428   TVARIABLE(Int32T, var_result);
   2429   TVARIABLE(Int32T, var_trail);
   2430   var_result = StringCharCodeAt(string, index);
   2431   var_trail = Int32Constant(0);
   2432 
   2433   GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
   2434                         Int32Constant(0xD800)),
   2435          &return_result);
   2436   TNode<IntPtrT> next_index = IntPtrAdd(index, IntPtrConstant(1));
   2437 
   2438   GotoIfNot(IntPtrLessThan(next_index, length), &return_result);
   2439   var_trail = StringCharCodeAt(string, next_index);
   2440   Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
   2441                      Int32Constant(0xDC00)),
   2442          &handle_surrogate_pair, &return_result);
   2443 
   2444   BIND(&handle_surrogate_pair);
   2445   {
   2446     TNode<Int32T> lead = var_result.value();
   2447     TNode<Int32T> trail = var_trail.value();
   2448 
   2449     // Check that this path is only taken if a surrogate pair is found
   2450     CSA_SLOW_ASSERT(this,
   2451                     Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
   2452     CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00)));
   2453     CSA_SLOW_ASSERT(this,
   2454                     Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
   2455     CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000)));
   2456 
   2457     switch (encoding) {
   2458       case UnicodeEncoding::UTF16:
   2459         var_result = Signed(Word32Or(
   2460 // Need to swap the order for big-endian platforms
   2461 #if V8_TARGET_BIG_ENDIAN
   2462             Word32Shl(lead, Int32Constant(16)), trail));
   2463 #else
   2464             Word32Shl(trail, Int32Constant(16)), lead));
   2465 #endif
   2466         break;
   2467 
   2468       case UnicodeEncoding::UTF32: {
   2469         // Convert UTF16 surrogate pair into |word32| code point, encoded as
   2470         // UTF32.
   2471         TNode<Int32T> surrogate_offset =
   2472             Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
   2473 
   2474         // (lead << 10) + trail + SURROGATE_OFFSET
   2475         var_result = Signed(Int32Add(Word32Shl(lead, Int32Constant(10)),
   2476                                      Int32Add(trail, surrogate_offset)));
   2477         break;
   2478       }
   2479     }
   2480     Goto(&return_result);
   2481   }
   2482 
   2483   BIND(&return_result);
   2484   return var_result.value();
   2485 }
   2486 
   2487 // ES6 #sec-%stringiteratorprototype%.next
   2488 TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
   2489   VARIABLE(var_value, MachineRepresentation::kTagged);
   2490   VARIABLE(var_done, MachineRepresentation::kTagged);
   2491 
   2492   var_value.Bind(UndefinedConstant());
   2493   var_done.Bind(TrueConstant());
   2494 
   2495   Label throw_bad_receiver(this), next_codepoint(this), return_result(this);
   2496 
   2497   Node* context = Parameter(Descriptor::kContext);
   2498   Node* iterator = Parameter(Descriptor::kReceiver);
   2499 
   2500   GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
   2501   GotoIfNot(
   2502       InstanceTypeEqual(LoadInstanceType(iterator), JS_STRING_ITERATOR_TYPE),
   2503       &throw_bad_receiver);
   2504 
   2505   Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset);
   2506   TNode<IntPtrT> position = SmiUntag(
   2507       CAST(LoadObjectField(iterator, JSStringIterator::kNextIndexOffset)));
   2508   TNode<IntPtrT> length = LoadStringLengthAsWord(string);
   2509 
   2510   Branch(IntPtrLessThan(position, length), &next_codepoint, &return_result);
   2511 
   2512   BIND(&next_codepoint);
   2513   {
   2514     UnicodeEncoding encoding = UnicodeEncoding::UTF16;
   2515     TNode<Int32T> ch = LoadSurrogatePairAt(string, length, position, encoding);
   2516     TNode<String> value = StringFromSingleCodePoint(ch, encoding);
   2517     var_value.Bind(value);
   2518     TNode<IntPtrT> length = LoadStringLengthAsWord(value);
   2519     StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
   2520                                    SmiTag(Signed(IntPtrAdd(position, length))));
   2521     var_done.Bind(FalseConstant());
   2522     Goto(&return_result);
   2523   }
   2524 
   2525   BIND(&return_result);
   2526   {
   2527     Node* result =
   2528         AllocateJSIteratorResult(context, var_value.value(), var_done.value());
   2529     Return(result);
   2530   }
   2531 
   2532   BIND(&throw_bad_receiver);
   2533   {
   2534     // The {receiver} is not a valid JSGeneratorObject.
   2535     ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver,
   2536                    StringConstant("String Iterator.prototype.next"), iterator);
   2537   }
   2538 }
   2539 
   2540 // -----------------------------------------------------------------------------
   2541 // ES6 section B.2.3 Additional Properties of the String.prototype object
   2542 
   2543 class StringHtmlAssembler : public StringBuiltinsAssembler {
   2544  public:
   2545   explicit StringHtmlAssembler(compiler::CodeAssemblerState* state)
   2546       : StringBuiltinsAssembler(state) {}
   2547 
   2548  protected:
   2549   void Generate(Node* const context, Node* const receiver,
   2550                 const char* method_name, const char* tag_name) {
   2551     Node* const string = ToThisString(context, receiver, method_name);
   2552     std::string open_tag = "<" + std::string(tag_name) + ">";
   2553     std::string close_tag = "</" + std::string(tag_name) + ">";
   2554 
   2555     Node* strings[] = {StringConstant(open_tag.c_str()), string,
   2556                        StringConstant(close_tag.c_str())};
   2557     Return(ConcatStrings(context, strings, arraysize(strings)));
   2558   }
   2559 
   2560   void GenerateWithAttribute(Node* const context, Node* const receiver,
   2561                              const char* method_name, const char* tag_name,
   2562                              const char* attr, Node* const value) {
   2563     Node* const string = ToThisString(context, receiver, method_name);
   2564     Node* const value_string =
   2565         EscapeQuotes(context, ToString_Inline(context, value));
   2566     std::string open_tag_attr =
   2567         "<" + std::string(tag_name) + " " + std::string(attr) + "=\"";
   2568     std::string close_tag = "</" + std::string(tag_name) + ">";
   2569 
   2570     Node* strings[] = {StringConstant(open_tag_attr.c_str()), value_string,
   2571                        StringConstant("\">"), string,
   2572                        StringConstant(close_tag.c_str())};
   2573     Return(ConcatStrings(context, strings, arraysize(strings)));
   2574   }
   2575 
   2576   Node* ConcatStrings(Node* const context, Node** strings, int len) {
   2577     VARIABLE(var_result, MachineRepresentation::kTagged, strings[0]);
   2578     for (int i = 1; i < len; i++) {
   2579       var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
   2580                                var_result.value(), strings[i]));
   2581     }
   2582     return var_result.value();
   2583   }
   2584 
   2585   Node* EscapeQuotes(Node* const context, Node* const string) {
   2586     CSA_ASSERT(this, IsString(string));
   2587     Node* const regexp_function = LoadContextElement(
   2588         LoadNativeContext(context), Context::REGEXP_FUNCTION_INDEX);
   2589     Node* const initial_map = LoadObjectField(
   2590         regexp_function, JSFunction::kPrototypeOrInitialMapOffset);
   2591     // TODO(pwong): Refactor to not allocate RegExp
   2592     Node* const regexp =
   2593         CallRuntime(Runtime::kRegExpInitializeAndCompile, context,
   2594                     AllocateJSObjectFromMap(initial_map), StringConstant("\""),
   2595                     StringConstant("g"));
   2596 
   2597     return CallRuntime(Runtime::kRegExpInternalReplace, context, regexp, string,
   2598                        StringConstant("&quot;"));
   2599   }
   2600 };
   2601 
   2602 // ES6 #sec-string.prototype.anchor
   2603 TF_BUILTIN(StringPrototypeAnchor, StringHtmlAssembler) {
   2604   Node* const context = Parameter(Descriptor::kContext);
   2605   Node* const receiver = Parameter(Descriptor::kReceiver);
   2606   Node* const value = Parameter(Descriptor::kValue);
   2607   GenerateWithAttribute(context, receiver, "String.prototype.anchor", "a",
   2608                         "name", value);
   2609 }
   2610 
   2611 // ES6 #sec-string.prototype.big
   2612 TF_BUILTIN(StringPrototypeBig, StringHtmlAssembler) {
   2613   Node* const context = Parameter(Descriptor::kContext);
   2614   Node* const receiver = Parameter(Descriptor::kReceiver);
   2615   Generate(context, receiver, "String.prototype.big", "big");
   2616 }
   2617 
   2618 // ES6 #sec-string.prototype.blink
   2619 TF_BUILTIN(StringPrototypeBlink, StringHtmlAssembler) {
   2620   Node* const context = Parameter(Descriptor::kContext);
   2621   Node* const receiver = Parameter(Descriptor::kReceiver);
   2622   Generate(context, receiver, "String.prototype.blink", "blink");
   2623 }
   2624 
   2625 // ES6 #sec-string.prototype.bold
   2626 TF_BUILTIN(StringPrototypeBold, StringHtmlAssembler) {
   2627   Node* const context = Parameter(Descriptor::kContext);
   2628   Node* const receiver = Parameter(Descriptor::kReceiver);
   2629   Generate(context, receiver, "String.prototype.bold", "b");
   2630 }
   2631 
   2632 // ES6 #sec-string.prototype.fontcolor
   2633 TF_BUILTIN(StringPrototypeFontcolor, StringHtmlAssembler) {
   2634   Node* const context = Parameter(Descriptor::kContext);
   2635   Node* const receiver = Parameter(Descriptor::kReceiver);
   2636   Node* const value = Parameter(Descriptor::kValue);
   2637   GenerateWithAttribute(context, receiver, "String.prototype.fontcolor", "font",
   2638                         "color", value);
   2639 }
   2640 
   2641 // ES6 #sec-string.prototype.fontsize
   2642 TF_BUILTIN(StringPrototypeFontsize, StringHtmlAssembler) {
   2643   Node* const context = Parameter(Descriptor::kContext);
   2644   Node* const receiver = Parameter(Descriptor::kReceiver);
   2645   Node* const value = Parameter(Descriptor::kValue);
   2646   GenerateWithAttribute(context, receiver, "String.prototype.fontsize", "font",
   2647                         "size", value);
   2648 }
   2649 
   2650 // ES6 #sec-string.prototype.fixed
   2651 TF_BUILTIN(StringPrototypeFixed, StringHtmlAssembler) {
   2652   Node* const context = Parameter(Descriptor::kContext);
   2653   Node* const receiver = Parameter(Descriptor::kReceiver);
   2654   Generate(context, receiver, "String.prototype.fixed", "tt");
   2655 }
   2656 
   2657 // ES6 #sec-string.prototype.italics
   2658 TF_BUILTIN(StringPrototypeItalics, StringHtmlAssembler) {
   2659   Node* const context = Parameter(Descriptor::kContext);
   2660   Node* const receiver = Parameter(Descriptor::kReceiver);
   2661   Generate(context, receiver, "String.prototype.italics", "i");
   2662 }
   2663 
   2664 // ES6 #sec-string.prototype.link
   2665 TF_BUILTIN(StringPrototypeLink, StringHtmlAssembler) {
   2666   Node* const context = Parameter(Descriptor::kContext);
   2667   Node* const receiver = Parameter(Descriptor::kReceiver);
   2668   Node* const value = Parameter(Descriptor::kValue);
   2669   GenerateWithAttribute(context, receiver, "String.prototype.link", "a", "href",
   2670                         value);
   2671 }
   2672 
   2673 // ES6 #sec-string.prototype.small
   2674 TF_BUILTIN(StringPrototypeSmall, StringHtmlAssembler) {
   2675   Node* const context = Parameter(Descriptor::kContext);
   2676   Node* const receiver = Parameter(Descriptor::kReceiver);
   2677   Generate(context, receiver, "String.prototype.small", "small");
   2678 }
   2679 
   2680 // ES6 #sec-string.prototype.strike
   2681 TF_BUILTIN(StringPrototypeStrike, StringHtmlAssembler) {
   2682   Node* const context = Parameter(Descriptor::kContext);
   2683   Node* const receiver = Parameter(Descriptor::kReceiver);
   2684   Generate(context, receiver, "String.prototype.strike", "strike");
   2685 }
   2686 
   2687 // ES6 #sec-string.prototype.sub
   2688 TF_BUILTIN(StringPrototypeSub, StringHtmlAssembler) {
   2689   Node* const context = Parameter(Descriptor::kContext);
   2690   Node* const receiver = Parameter(Descriptor::kReceiver);
   2691   Generate(context, receiver, "String.prototype.sub", "sub");
   2692 }
   2693 
   2694 // ES6 #sec-string.prototype.sup
   2695 TF_BUILTIN(StringPrototypeSup, StringHtmlAssembler) {
   2696   Node* const context = Parameter(Descriptor::kContext);
   2697   Node* const receiver = Parameter(Descriptor::kReceiver);
   2698   Generate(context, receiver, "String.prototype.sup", "sup");
   2699 }
   2700 
   2701 }  // namespace internal
   2702 }  // namespace v8
   2703