LCOV - code coverage report
Current view: top level - src/builtins - builtins-string-gen.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 717 720 99.6 %
Date: 2017-04-26 Functions: 68 68 100.0 %

          Line data    Source code
       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-regexp-gen.h"
       6             : #include "src/builtins/builtins-utils-gen.h"
       7             : #include "src/builtins/builtins.h"
       8             : #include "src/code-factory.h"
       9             : #include "src/code-stub-assembler.h"
      10             : #include "src/objects.h"
      11             : 
      12             : namespace v8 {
      13             : namespace internal {
      14             : 
      15             : typedef CodeStubAssembler::RelationalComparisonMode RelationalComparisonMode;
      16             : 
      17             : class StringBuiltinsAssembler : public CodeStubAssembler {
      18             :  public:
      19             :   explicit StringBuiltinsAssembler(compiler::CodeAssemblerState* state)
      20         473 :       : CodeStubAssembler(state) {}
      21             : 
      22             :   // ES#sec-getsubstitution
      23             :   Node* GetSubstitution(Node* context, Node* subject_string,
      24             :                         Node* match_start_index, Node* match_end_index,
      25             :                         Node* replace_string);
      26             : 
      27             :  protected:
      28          86 :   Node* DirectStringData(Node* string, Node* string_instance_type) {
      29             :     // Compute the effective offset of the first character.
      30          86 :     VARIABLE(var_data, MachineType::PointerRepresentation());
      31          86 :     Label if_sequential(this), if_external(this), if_join(this);
      32             :     Branch(Word32Equal(Word32And(string_instance_type,
      33             :                                  Int32Constant(kStringRepresentationMask)),
      34             :                        Int32Constant(kSeqStringTag)),
      35          86 :            &if_sequential, &if_external);
      36             : 
      37          86 :     BIND(&if_sequential);
      38             :     {
      39             :       var_data.Bind(IntPtrAdd(
      40             :           IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag),
      41          86 :           BitcastTaggedToWord(string)));
      42          86 :       Goto(&if_join);
      43             :     }
      44             : 
      45          86 :     BIND(&if_external);
      46             :     {
      47             :       // This is only valid for ExternalStrings where the resource data
      48             :       // pointer is cached (i.e. no short external strings).
      49             :       CSA_ASSERT(this, Word32NotEqual(
      50             :                            Word32And(string_instance_type,
      51             :                                      Int32Constant(kShortExternalStringMask)),
      52             :                            Int32Constant(kShortExternalStringTag)));
      53             :       var_data.Bind(LoadObjectField(string, ExternalString::kResourceDataOffset,
      54          86 :                                     MachineType::Pointer()));
      55          86 :       Goto(&if_join);
      56             :     }
      57             : 
      58          86 :     BIND(&if_join);
      59         172 :     return var_data.value();
      60             :   }
      61             : 
      62          86 :   void DispatchOnStringEncodings(Node* const lhs_instance_type,
      63             :                                  Node* const rhs_instance_type,
      64             :                                  Label* if_one_one, Label* if_one_two,
      65             :                                  Label* if_two_one, Label* if_two_two) {
      66             :     STATIC_ASSERT(kStringEncodingMask == 0x8);
      67             :     STATIC_ASSERT(kTwoByteStringTag == 0x0);
      68             :     STATIC_ASSERT(kOneByteStringTag == 0x8);
      69             : 
      70             :     // First combine the encodings.
      71             : 
      72          86 :     Node* const encoding_mask = Int32Constant(kStringEncodingMask);
      73          86 :     Node* const lhs_encoding = Word32And(lhs_instance_type, encoding_mask);
      74          86 :     Node* const rhs_encoding = Word32And(rhs_instance_type, encoding_mask);
      75             : 
      76             :     Node* const combined_encodings =
      77          86 :         Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1));
      78             : 
      79             :     // Then dispatch on the combined encoding.
      80             : 
      81             :     Label unreachable(this, Label::kDeferred);
      82             : 
      83             :     int32_t values[] = {
      84             :         kOneByteStringTag | (kOneByteStringTag >> 1),
      85             :         kOneByteStringTag | (kTwoByteStringTag >> 1),
      86             :         kTwoByteStringTag | (kOneByteStringTag >> 1),
      87             :         kTwoByteStringTag | (kTwoByteStringTag >> 1),
      88          86 :     };
      89             :     Label* labels[] = {
      90             :         if_one_one, if_one_two, if_two_one, if_two_two,
      91          86 :     };
      92             : 
      93             :     STATIC_ASSERT(arraysize(values) == arraysize(labels));
      94          86 :     Switch(combined_encodings, &unreachable, values, labels, arraysize(values));
      95             : 
      96          86 :     BIND(&unreachable);
      97          86 :     Unreachable();
      98          86 :   }
      99             : 
     100             :   template <typename SubjectChar, typename PatternChar>
     101         344 :   Node* CallSearchStringRaw(Node* const subject_ptr, Node* const subject_length,
     102             :                             Node* const search_ptr, Node* const search_length,
     103             :                             Node* const start_position) {
     104             :     Node* const function_addr = ExternalConstant(
     105             :         ExternalReference::search_string_raw<SubjectChar, PatternChar>(
     106         344 :             isolate()));
     107             :     Node* const isolate_ptr =
     108         344 :         ExternalConstant(ExternalReference::isolate_address(isolate()));
     109             : 
     110         344 :     MachineType type_ptr = MachineType::Pointer();
     111         344 :     MachineType type_intptr = MachineType::IntPtr();
     112             : 
     113             :     Node* const result = CallCFunction6(
     114             :         type_intptr, type_ptr, type_ptr, type_intptr, type_ptr, type_intptr,
     115             :         type_intptr, function_addr, isolate_ptr, subject_ptr, subject_length,
     116         344 :         search_ptr, search_length, start_position);
     117             : 
     118         344 :     return result;
     119             :   }
     120             : 
     121         688 :   Node* PointerToStringDataAtIndex(Node* const string_data, Node* const index,
     122             :                                    String::Encoding encoding) {
     123             :     const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING)
     124             :                                   ? UINT8_ELEMENTS
     125         688 :                                   : UINT16_ELEMENTS;
     126             : 
     127             :     Node* const offset_in_bytes =
     128         688 :         ElementOffsetFromIndex(index, kind, INTPTR_PARAMETERS);
     129         688 :     return IntPtrAdd(string_data, offset_in_bytes);
     130             :   }
     131             : 
     132             :   void GenerateStringEqual(Node* context, Node* left, Node* right);
     133             :   void GenerateStringRelationalComparison(Node* context, Node* left,
     134             :                                           Node* right,
     135             :                                           RelationalComparisonMode mode);
     136             : 
     137             :   Node* ToSmiBetweenZeroAnd(Node* context, Node* value, Node* limit);
     138             : 
     139             :   Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index,
     140             :                             UnicodeEncoding encoding);
     141             : 
     142             :   void StringIndexOf(Node* const subject_string,
     143             :                      Node* const subject_instance_type,
     144             :                      Node* const search_string,
     145             :                      Node* const search_instance_type, Node* const position,
     146             :                      std::function<void(Node*)> f_return);
     147             : 
     148             :   Node* IndexOfDollarChar(Node* const context, Node* const string);
     149             : 
     150             :   Node* IsNullOrUndefined(Node* const value);
     151             :   void RequireObjectCoercible(Node* const context, Node* const value,
     152             :                               const char* method_name);
     153             : 
     154          86 :   Node* SmiIsNegative(Node* const value) {
     155          86 :     return SmiLessThan(value, SmiConstant(0));
     156             :   }
     157             : 
     158             :   // Implements boilerplate logic for {match, split, replace, search} of the
     159             :   // form:
     160             :   //
     161             :   //  if (!IS_NULL_OR_UNDEFINED(object)) {
     162             :   //    var maybe_function = object[symbol];
     163             :   //    if (!IS_UNDEFINED(maybe_function)) {
     164             :   //      return %_Call(maybe_function, ...);
     165             :   //    }
     166             :   //  }
     167             :   //
     168             :   // Contains fast paths for Smi and RegExp objects.
     169             :   typedef std::function<Node*()> NodeFunction0;
     170             :   typedef std::function<Node*(Node* fn)> NodeFunction1;
     171             :   void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
     172             :                                  Handle<Symbol> symbol,
     173             :                                  const NodeFunction0& regexp_call,
     174             :                                  const NodeFunction1& generic_call);
     175             : };
     176             : 
     177          43 : void StringBuiltinsAssembler::GenerateStringEqual(Node* context, Node* left,
     178             :                                                   Node* right) {
     179             :   // Here's pseudo-code for the algorithm below:
     180             :   //
     181             :   // if (lhs == rhs) return true;
     182             :   // if (lhs->length() != rhs->length()) return false;
     183             :   // if (lhs->IsInternalizedString() && rhs->IsInternalizedString()) {
     184             :   //   return false;
     185             :   // }
     186             :   // if (lhs->IsSeqOneByteString() && rhs->IsSeqOneByteString()) {
     187             :   //   for (i = 0; i != lhs->length(); ++i) {
     188             :   //     if (lhs[i] != rhs[i]) return false;
     189             :   //   }
     190             :   //   return true;
     191             :   // }
     192             :   // if (lhs and/or rhs are indirect strings) {
     193             :   //   unwrap them and restart from the beginning;
     194             :   // }
     195             :   // return %StringEqual(lhs, rhs);
     196             : 
     197          43 :   VARIABLE(var_left, MachineRepresentation::kTagged, left);
     198          86 :   VARIABLE(var_right, MachineRepresentation::kTagged, right);
     199             : 
     200          43 :   Variable* input_vars[2] = {&var_left, &var_right};
     201          86 :   Label if_equal(this), if_notequal(this), restart(this, 2, input_vars);
     202          43 :   Goto(&restart);
     203          43 :   BIND(&restart);
     204          43 :   Node* lhs = var_left.value();
     205          43 :   Node* rhs = var_right.value();
     206             : 
     207             :   // Fast check to see if {lhs} and {rhs} refer to the same String object.
     208          43 :   GotoIf(WordEqual(lhs, rhs), &if_equal);
     209             : 
     210             :   // Load the length of {lhs} and {rhs}.
     211          43 :   Node* lhs_length = LoadStringLength(lhs);
     212          43 :   Node* rhs_length = LoadStringLength(rhs);
     213             : 
     214             :   // Strings with different lengths cannot be equal.
     215          43 :   GotoIf(WordNotEqual(lhs_length, rhs_length), &if_notequal);
     216             : 
     217             :   // Load instance types of {lhs} and {rhs}.
     218          43 :   Node* lhs_instance_type = LoadInstanceType(lhs);
     219          43 :   Node* rhs_instance_type = LoadInstanceType(rhs);
     220             : 
     221             :   // Combine the instance types into a single 16-bit value, so we can check
     222             :   // both of them at once.
     223             :   Node* both_instance_types = Word32Or(
     224          43 :       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
     225             : 
     226             :   // Check if both {lhs} and {rhs} are internalized. Since we already know
     227             :   // that they're not the same object, they're not equal in that case.
     228             :   int const kBothInternalizedMask =
     229             :       kIsNotInternalizedMask | (kIsNotInternalizedMask << 8);
     230             :   int const kBothInternalizedTag = kInternalizedTag | (kInternalizedTag << 8);
     231             :   GotoIf(Word32Equal(Word32And(both_instance_types,
     232             :                                Int32Constant(kBothInternalizedMask)),
     233             :                      Int32Constant(kBothInternalizedTag)),
     234          43 :          &if_notequal);
     235             : 
     236             :   // Check that both {lhs} and {rhs} are flat one-byte strings, and that
     237             :   // in case of ExternalStrings the data pointer is cached..
     238             :   STATIC_ASSERT(kShortExternalStringTag != 0);
     239             :   int const kBothDirectOneByteStringMask =
     240             :       kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask |
     241             :       ((kStringEncodingMask | kIsIndirectStringMask | kShortExternalStringMask)
     242             :        << 8);
     243             :   int const kBothDirectOneByteStringTag =
     244             :       kOneByteStringTag | (kOneByteStringTag << 8);
     245          43 :   Label if_bothdirectonebytestrings(this), if_notbothdirectonebytestrings(this);
     246             :   Branch(Word32Equal(Word32And(both_instance_types,
     247             :                                Int32Constant(kBothDirectOneByteStringMask)),
     248             :                      Int32Constant(kBothDirectOneByteStringTag)),
     249          43 :          &if_bothdirectonebytestrings, &if_notbothdirectonebytestrings);
     250             : 
     251          43 :   BIND(&if_bothdirectonebytestrings);
     252             :   {
     253             :     // Compute the effective offset of the first character.
     254          43 :     Node* lhs_data = DirectStringData(lhs, lhs_instance_type);
     255          43 :     Node* rhs_data = DirectStringData(rhs, rhs_instance_type);
     256             : 
     257             :     // Compute the first offset after the string from the length.
     258          43 :     Node* length = SmiUntag(lhs_length);
     259             : 
     260             :     // Loop over the {lhs} and {rhs} strings to see if they are equal.
     261          43 :     VARIABLE(var_offset, MachineType::PointerRepresentation());
     262          43 :     Label loop(this, &var_offset);
     263          43 :     var_offset.Bind(IntPtrConstant(0));
     264          43 :     Goto(&loop);
     265          43 :     BIND(&loop);
     266             :     {
     267             :       // If {offset} equals {end}, no difference was found, so the
     268             :       // strings are equal.
     269          43 :       Node* offset = var_offset.value();
     270          43 :       GotoIf(WordEqual(offset, length), &if_equal);
     271             : 
     272             :       // Load the next characters from {lhs} and {rhs}.
     273          43 :       Node* lhs_value = Load(MachineType::Uint8(), lhs_data, offset);
     274          43 :       Node* rhs_value = Load(MachineType::Uint8(), rhs_data, offset);
     275             : 
     276             :       // Check if the characters match.
     277          43 :       GotoIf(Word32NotEqual(lhs_value, rhs_value), &if_notequal);
     278             : 
     279             :       // Advance to next character.
     280          43 :       var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
     281          43 :       Goto(&loop);
     282          43 :     }
     283             :   }
     284             : 
     285          43 :   BIND(&if_notbothdirectonebytestrings);
     286             :   {
     287             :     // Try to unwrap indirect strings, restart the above attempt on success.
     288             :     MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
     289          43 :                               rhs_instance_type, &restart);
     290             :     // TODO(bmeurer): Add support for two byte string equality checks.
     291             : 
     292          43 :     TailCallRuntime(Runtime::kStringEqual, context, lhs, rhs);
     293             :   }
     294             : 
     295          43 :   BIND(&if_equal);
     296          43 :   Return(TrueConstant());
     297             : 
     298          43 :   BIND(&if_notequal);
     299          86 :   Return(FalseConstant());
     300          43 : }
     301             : 
     302         172 : void StringBuiltinsAssembler::GenerateStringRelationalComparison(
     303             :     Node* context, Node* left, Node* right, RelationalComparisonMode mode) {
     304         172 :   VARIABLE(var_left, MachineRepresentation::kTagged, left);
     305         344 :   VARIABLE(var_right, MachineRepresentation::kTagged, right);
     306             : 
     307         172 :   Variable* input_vars[2] = {&var_left, &var_right};
     308         172 :   Label if_less(this), if_equal(this), if_greater(this);
     309         344 :   Label restart(this, 2, input_vars);
     310         172 :   Goto(&restart);
     311         172 :   BIND(&restart);
     312             : 
     313         172 :   Node* lhs = var_left.value();
     314         172 :   Node* rhs = var_right.value();
     315             :   // Fast check to see if {lhs} and {rhs} refer to the same String object.
     316         172 :   GotoIf(WordEqual(lhs, rhs), &if_equal);
     317             : 
     318             :   // Load instance types of {lhs} and {rhs}.
     319         172 :   Node* lhs_instance_type = LoadInstanceType(lhs);
     320         172 :   Node* rhs_instance_type = LoadInstanceType(rhs);
     321             : 
     322             :   // Combine the instance types into a single 16-bit value, so we can check
     323             :   // both of them at once.
     324             :   Node* both_instance_types = Word32Or(
     325         172 :       lhs_instance_type, Word32Shl(rhs_instance_type, Int32Constant(8)));
     326             : 
     327             :   // Check that both {lhs} and {rhs} are flat one-byte strings.
     328             :   int const kBothSeqOneByteStringMask =
     329             :       kStringEncodingMask | kStringRepresentationMask |
     330             :       ((kStringEncodingMask | kStringRepresentationMask) << 8);
     331             :   int const kBothSeqOneByteStringTag =
     332             :       kOneByteStringTag | kSeqStringTag |
     333             :       ((kOneByteStringTag | kSeqStringTag) << 8);
     334         172 :   Label if_bothonebyteseqstrings(this), if_notbothonebyteseqstrings(this);
     335             :   Branch(Word32Equal(Word32And(both_instance_types,
     336             :                                Int32Constant(kBothSeqOneByteStringMask)),
     337             :                      Int32Constant(kBothSeqOneByteStringTag)),
     338         172 :          &if_bothonebyteseqstrings, &if_notbothonebyteseqstrings);
     339             : 
     340         172 :   BIND(&if_bothonebyteseqstrings);
     341             :   {
     342             :     // Load the length of {lhs} and {rhs}.
     343         172 :     Node* lhs_length = LoadStringLength(lhs);
     344         172 :     Node* rhs_length = LoadStringLength(rhs);
     345             : 
     346             :     // Determine the minimum length.
     347         172 :     Node* length = SmiMin(lhs_length, rhs_length);
     348             : 
     349             :     // Compute the effective offset of the first character.
     350             :     Node* begin =
     351         172 :         IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag);
     352             : 
     353             :     // Compute the first offset after the string from the length.
     354         172 :     Node* end = IntPtrAdd(begin, SmiUntag(length));
     355             : 
     356             :     // Loop over the {lhs} and {rhs} strings to see if they are equal.
     357         172 :     VARIABLE(var_offset, MachineType::PointerRepresentation());
     358         172 :     Label loop(this, &var_offset);
     359         172 :     var_offset.Bind(begin);
     360         172 :     Goto(&loop);
     361         172 :     BIND(&loop);
     362             :     {
     363             :       // Check if {offset} equals {end}.
     364         172 :       Node* offset = var_offset.value();
     365         172 :       Label if_done(this), if_notdone(this);
     366         172 :       Branch(WordEqual(offset, end), &if_done, &if_notdone);
     367             : 
     368         172 :       BIND(&if_notdone);
     369             :       {
     370             :         // Load the next characters from {lhs} and {rhs}.
     371         172 :         Node* lhs_value = Load(MachineType::Uint8(), lhs, offset);
     372         172 :         Node* rhs_value = Load(MachineType::Uint8(), rhs, offset);
     373             : 
     374             :         // Check if the characters match.
     375         172 :         Label if_valueissame(this), if_valueisnotsame(this);
     376             :         Branch(Word32Equal(lhs_value, rhs_value), &if_valueissame,
     377         172 :                &if_valueisnotsame);
     378             : 
     379         172 :         BIND(&if_valueissame);
     380             :         {
     381             :           // Advance to next character.
     382         172 :           var_offset.Bind(IntPtrAdd(offset, IntPtrConstant(1)));
     383             :         }
     384         172 :         Goto(&loop);
     385             : 
     386         172 :         BIND(&if_valueisnotsame);
     387         344 :         Branch(Uint32LessThan(lhs_value, rhs_value), &if_less, &if_greater);
     388             :       }
     389             : 
     390         172 :       BIND(&if_done);
     391             :       {
     392             :         // All characters up to the min length are equal, decide based on
     393             :         // string length.
     394         172 :         GotoIf(SmiEqual(lhs_length, rhs_length), &if_equal);
     395         172 :         BranchIfSmiLessThan(lhs_length, rhs_length, &if_less, &if_greater);
     396         172 :       }
     397         172 :     }
     398             :   }
     399             : 
     400         172 :   BIND(&if_notbothonebyteseqstrings);
     401             :   {
     402             :     // Try to unwrap indirect strings, restart the above attempt on success.
     403             :     MaybeDerefIndirectStrings(&var_left, lhs_instance_type, &var_right,
     404         172 :                               rhs_instance_type, &restart);
     405             :     // TODO(bmeurer): Add support for two byte string relational comparisons.
     406         172 :     switch (mode) {
     407             :       case RelationalComparisonMode::kLessThan:
     408          43 :         TailCallRuntime(Runtime::kStringLessThan, context, lhs, rhs);
     409          43 :         break;
     410             :       case RelationalComparisonMode::kLessThanOrEqual:
     411          43 :         TailCallRuntime(Runtime::kStringLessThanOrEqual, context, lhs, rhs);
     412          43 :         break;
     413             :       case RelationalComparisonMode::kGreaterThan:
     414          43 :         TailCallRuntime(Runtime::kStringGreaterThan, context, lhs, rhs);
     415          43 :         break;
     416             :       case RelationalComparisonMode::kGreaterThanOrEqual:
     417          43 :         TailCallRuntime(Runtime::kStringGreaterThanOrEqual, context, lhs, rhs);
     418          43 :         break;
     419             :     }
     420             :   }
     421             : 
     422         172 :   BIND(&if_less);
     423         172 :   switch (mode) {
     424             :     case RelationalComparisonMode::kLessThan:
     425             :     case RelationalComparisonMode::kLessThanOrEqual:
     426          86 :       Return(BooleanConstant(true));
     427          86 :       break;
     428             : 
     429             :     case RelationalComparisonMode::kGreaterThan:
     430             :     case RelationalComparisonMode::kGreaterThanOrEqual:
     431          86 :       Return(BooleanConstant(false));
     432          86 :       break;
     433             :   }
     434             : 
     435         172 :   BIND(&if_equal);
     436         172 :   switch (mode) {
     437             :     case RelationalComparisonMode::kLessThan:
     438             :     case RelationalComparisonMode::kGreaterThan:
     439          86 :       Return(BooleanConstant(false));
     440          86 :       break;
     441             : 
     442             :     case RelationalComparisonMode::kLessThanOrEqual:
     443             :     case RelationalComparisonMode::kGreaterThanOrEqual:
     444          86 :       Return(BooleanConstant(true));
     445          86 :       break;
     446             :   }
     447             : 
     448         172 :   BIND(&if_greater);
     449         172 :   switch (mode) {
     450             :     case RelationalComparisonMode::kLessThan:
     451             :     case RelationalComparisonMode::kLessThanOrEqual:
     452          86 :       Return(BooleanConstant(false));
     453          86 :       break;
     454             : 
     455             :     case RelationalComparisonMode::kGreaterThan:
     456             :     case RelationalComparisonMode::kGreaterThanOrEqual:
     457          86 :       Return(BooleanConstant(true));
     458          86 :       break;
     459         172 :   }
     460         172 : }
     461             : 
     462         172 : TF_BUILTIN(StringEqual, StringBuiltinsAssembler) {
     463             :   Node* context = Parameter(Descriptor::kContext);
     464             :   Node* left = Parameter(Descriptor::kLeft);
     465             :   Node* right = Parameter(Descriptor::kRight);
     466          43 :   GenerateStringEqual(context, left, right);
     467          43 : }
     468             : 
     469         172 : TF_BUILTIN(StringLessThan, StringBuiltinsAssembler) {
     470             :   Node* context = Parameter(Descriptor::kContext);
     471             :   Node* left = Parameter(Descriptor::kLeft);
     472             :   Node* right = Parameter(Descriptor::kRight);
     473             :   GenerateStringRelationalComparison(context, left, right,
     474          43 :                                      RelationalComparisonMode::kLessThan);
     475          43 : }
     476             : 
     477         172 : TF_BUILTIN(StringLessThanOrEqual, StringBuiltinsAssembler) {
     478             :   Node* context = Parameter(Descriptor::kContext);
     479             :   Node* left = Parameter(Descriptor::kLeft);
     480             :   Node* right = Parameter(Descriptor::kRight);
     481             :   GenerateStringRelationalComparison(
     482          43 :       context, left, right, RelationalComparisonMode::kLessThanOrEqual);
     483          43 : }
     484             : 
     485         172 : TF_BUILTIN(StringGreaterThan, StringBuiltinsAssembler) {
     486             :   Node* context = Parameter(Descriptor::kContext);
     487             :   Node* left = Parameter(Descriptor::kLeft);
     488             :   Node* right = Parameter(Descriptor::kRight);
     489             :   GenerateStringRelationalComparison(context, left, right,
     490          43 :                                      RelationalComparisonMode::kGreaterThan);
     491          43 : }
     492             : 
     493         172 : TF_BUILTIN(StringGreaterThanOrEqual, StringBuiltinsAssembler) {
     494             :   Node* context = Parameter(Descriptor::kContext);
     495             :   Node* left = Parameter(Descriptor::kLeft);
     496             :   Node* right = Parameter(Descriptor::kRight);
     497             :   GenerateStringRelationalComparison(
     498          43 :       context, left, right, RelationalComparisonMode::kGreaterThanOrEqual);
     499          43 : }
     500             : 
     501         129 : TF_BUILTIN(StringCharAt, CodeStubAssembler) {
     502             :   Node* receiver = Parameter(Descriptor::kReceiver);
     503             :   Node* position = Parameter(Descriptor::kPosition);
     504             : 
     505             :   // Load the character code at the {position} from the {receiver}.
     506          43 :   Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS);
     507             : 
     508             :   // And return the single character string with only that {code}
     509          43 :   Node* result = StringFromCharCode(code);
     510          43 :   Return(result);
     511          43 : }
     512             : 
     513         129 : TF_BUILTIN(StringCharCodeAt, CodeStubAssembler) {
     514             :   Node* receiver = Parameter(Descriptor::kReceiver);
     515             :   Node* position = Parameter(Descriptor::kPosition);
     516             : 
     517             :   // Load the character code at the {position} from the {receiver}.
     518          43 :   Node* code = StringCharCodeAt(receiver, position, INTPTR_PARAMETERS);
     519             : 
     520             :   // And return it as TaggedSigned value.
     521             :   // TODO(turbofan): Allow builtins to return values untagged.
     522          43 :   Node* result = SmiFromWord32(code);
     523          43 :   Return(result);
     524          43 : }
     525             : 
     526             : // -----------------------------------------------------------------------------
     527             : // ES6 section 21.1 String Objects
     528             : 
     529             : // ES6 #sec-string.fromcharcode
     530         129 : TF_BUILTIN(StringFromCharCode, CodeStubAssembler) {
     531             :   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
     532             :   // arguments are reordered.
     533             :   Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
     534             :   Node* context = Parameter(BuiltinDescriptor::kContext);
     535             : 
     536          43 :   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
     537             :   // From now on use word-size argc value.
     538          43 :   argc = arguments.GetLength();
     539             : 
     540             :   // Check if we have exactly one argument (plus the implicit receiver), i.e.
     541             :   // if the parent frame is not an arguments adaptor frame.
     542          43 :   Label if_oneargument(this), if_notoneargument(this);
     543             :   Branch(WordEqual(argc, IntPtrConstant(1)), &if_oneargument,
     544          43 :          &if_notoneargument);
     545             : 
     546          43 :   BIND(&if_oneargument);
     547             :   {
     548             :     // Single argument case, perform fast single character string cache lookup
     549             :     // for one-byte code units, or fall back to creating a single character
     550             :     // string on the fly otherwise.
     551          43 :     Node* code = arguments.AtIndex(0);
     552          43 :     Node* code32 = TruncateTaggedToWord32(context, code);
     553          43 :     Node* code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
     554          43 :     Node* result = StringFromCharCode(code16);
     555          43 :     arguments.PopAndReturn(result);
     556             :   }
     557             : 
     558          43 :   Node* code16 = nullptr;
     559          43 :   BIND(&if_notoneargument);
     560             :   {
     561             :     Label two_byte(this);
     562             :     // Assume that the resulting string contains only one-byte characters.
     563          43 :     Node* one_byte_result = AllocateSeqOneByteString(context, argc);
     564             : 
     565          86 :     VARIABLE(max_index, MachineType::PointerRepresentation());
     566          43 :     max_index.Bind(IntPtrConstant(0));
     567             : 
     568             :     // Iterate over the incoming arguments, converting them to 8-bit character
     569             :     // codes. Stop if any of the conversions generates a code that doesn't fit
     570             :     // in 8 bits.
     571          43 :     CodeStubAssembler::VariableList vars({&max_index}, zone());
     572             :     arguments.ForEach(vars, [this, context, &two_byte, &max_index, &code16,
     573          43 :                              one_byte_result](Node* arg) {
     574          43 :       Node* code32 = TruncateTaggedToWord32(context, arg);
     575          43 :       code16 = Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
     576             : 
     577             :       GotoIf(
     578             :           Int32GreaterThan(code16, Int32Constant(String::kMaxOneByteCharCode)),
     579          43 :           &two_byte);
     580             : 
     581             :       // The {code16} fits into the SeqOneByteString {one_byte_result}.
     582             :       Node* offset = ElementOffsetFromIndex(
     583             :           max_index.value(), UINT8_ELEMENTS,
     584             :           CodeStubAssembler::INTPTR_PARAMETERS,
     585          43 :           SeqOneByteString::kHeaderSize - kHeapObjectTag);
     586             :       StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
     587          43 :                           offset, code16);
     588          43 :       max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
     589         129 :     });
     590          43 :     arguments.PopAndReturn(one_byte_result);
     591             : 
     592          43 :     BIND(&two_byte);
     593             : 
     594             :     // At least one of the characters in the string requires a 16-bit
     595             :     // representation.  Allocate a SeqTwoByteString to hold the resulting
     596             :     // string.
     597          43 :     Node* two_byte_result = AllocateSeqTwoByteString(context, argc);
     598             : 
     599             :     // Copy the characters that have already been put in the 8-bit string into
     600             :     // their corresponding positions in the new 16-bit string.
     601          43 :     Node* zero = IntPtrConstant(0);
     602             :     CopyStringCharacters(one_byte_result, two_byte_result, zero, zero,
     603             :                          max_index.value(), String::ONE_BYTE_ENCODING,
     604             :                          String::TWO_BYTE_ENCODING,
     605          43 :                          CodeStubAssembler::INTPTR_PARAMETERS);
     606             : 
     607             :     // Write the character that caused the 8-bit to 16-bit fault.
     608             :     Node* max_index_offset =
     609             :         ElementOffsetFromIndex(max_index.value(), UINT16_ELEMENTS,
     610             :                                CodeStubAssembler::INTPTR_PARAMETERS,
     611          43 :                                SeqTwoByteString::kHeaderSize - kHeapObjectTag);
     612             :     StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
     613          43 :                         max_index_offset, code16);
     614          43 :     max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
     615             : 
     616             :     // Resume copying the passed-in arguments from the same place where the
     617             :     // 8-bit copy stopped, but this time copying over all of the characters
     618             :     // using a 16-bit representation.
     619             :     arguments.ForEach(
     620             :         vars,
     621          43 :         [this, context, two_byte_result, &max_index](Node* arg) {
     622          43 :           Node* code32 = TruncateTaggedToWord32(context, arg);
     623             :           Node* code16 =
     624          43 :               Word32And(code32, Int32Constant(String::kMaxUtf16CodeUnit));
     625             : 
     626             :           Node* offset = ElementOffsetFromIndex(
     627             :               max_index.value(), UINT16_ELEMENTS,
     628             :               CodeStubAssembler::INTPTR_PARAMETERS,
     629          43 :               SeqTwoByteString::kHeaderSize - kHeapObjectTag);
     630             :           StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
     631          43 :                               offset, code16);
     632          43 :           max_index.Bind(IntPtrAdd(max_index.value(), IntPtrConstant(1)));
     633          43 :         },
     634         129 :         max_index.value());
     635             : 
     636          86 :     arguments.PopAndReturn(two_byte_result);
     637          43 :   }
     638          43 : }
     639             : 
     640             : // ES6 #sec-string.prototype.charat
     641         129 : TF_BUILTIN(StringPrototypeCharAt, CodeStubAssembler) {
     642             :   Node* receiver = Parameter(Descriptor::kReceiver);
     643             :   Node* position = Parameter(Descriptor::kPosition);
     644             :   Node* context = Parameter(Descriptor::kContext);
     645             : 
     646             :   // Check that {receiver} is coercible to Object and convert it to a String.
     647          43 :   receiver = ToThisString(context, receiver, "String.prototype.charAt");
     648             : 
     649             :   // Convert the {position} to a Smi and check that it's in bounds of the
     650             :   // {receiver}.
     651             :   {
     652             :     Label return_emptystring(this, Label::kDeferred);
     653             :     position =
     654          43 :         ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero);
     655          43 :     GotoIfNot(TaggedIsSmi(position), &return_emptystring);
     656             : 
     657             :     // Determine the actual length of the {receiver} String.
     658          43 :     Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset);
     659             : 
     660             :     // Return "" if the Smi {position} is outside the bounds of the {receiver}.
     661          43 :     Label if_positioninbounds(this);
     662             :     Branch(SmiAboveOrEqual(position, receiver_length), &return_emptystring,
     663          43 :            &if_positioninbounds);
     664             : 
     665          43 :     BIND(&return_emptystring);
     666          43 :     Return(EmptyStringConstant());
     667             : 
     668          86 :     BIND(&if_positioninbounds);
     669             :   }
     670             : 
     671             :   // Load the character code at the {position} from the {receiver}.
     672          43 :   Node* code = StringCharCodeAt(receiver, position);
     673             : 
     674             :   // And return the single character string with only that {code}.
     675          43 :   Node* result = StringFromCharCode(code);
     676          43 :   Return(result);
     677          43 : }
     678             : 
     679             : // ES6 #sec-string.prototype.charcodeat
     680         129 : TF_BUILTIN(StringPrototypeCharCodeAt, CodeStubAssembler) {
     681             :   Node* receiver = Parameter(Descriptor::kReceiver);
     682             :   Node* position = Parameter(Descriptor::kPosition);
     683             :   Node* context = Parameter(Descriptor::kContext);
     684             : 
     685             :   // Check that {receiver} is coercible to Object and convert it to a String.
     686          43 :   receiver = ToThisString(context, receiver, "String.prototype.charCodeAt");
     687             : 
     688             :   // Convert the {position} to a Smi and check that it's in bounds of the
     689             :   // {receiver}.
     690             :   {
     691             :     Label return_nan(this, Label::kDeferred);
     692             :     position =
     693          43 :         ToInteger(context, position, CodeStubAssembler::kTruncateMinusZero);
     694          43 :     GotoIfNot(TaggedIsSmi(position), &return_nan);
     695             : 
     696             :     // Determine the actual length of the {receiver} String.
     697          43 :     Node* receiver_length = LoadObjectField(receiver, String::kLengthOffset);
     698             : 
     699             :     // Return NaN if the Smi {position} is outside the bounds of the {receiver}.
     700          43 :     Label if_positioninbounds(this);
     701             :     Branch(SmiAboveOrEqual(position, receiver_length), &return_nan,
     702          43 :            &if_positioninbounds);
     703             : 
     704          43 :     BIND(&return_nan);
     705          43 :     Return(NaNConstant());
     706             : 
     707          86 :     BIND(&if_positioninbounds);
     708             :   }
     709             : 
     710             :   // Load the character at the {position} from the {receiver}.
     711          43 :   Node* value = StringCharCodeAt(receiver, position);
     712          43 :   Node* result = SmiFromWord32(value);
     713          43 :   Return(result);
     714          43 : }
     715             : 
     716             : // ES6 String.prototype.concat(...args)
     717             : // ES6 #sec-string.prototype.concat
     718         172 : TF_BUILTIN(StringPrototypeConcat, CodeStubAssembler) {
     719             :   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
     720             :   // arguments are reordered.
     721             :   CodeStubArguments arguments(
     722          43 :       this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount)));
     723          43 :   Node* receiver = arguments.GetReceiver();
     724             :   Node* context = Parameter(BuiltinDescriptor::kContext);
     725             : 
     726             :   // Check that {receiver} is coercible to Object and convert it to a String.
     727          43 :   receiver = ToThisString(context, receiver, "String.prototype.concat");
     728             : 
     729             :   // Concatenate all the arguments passed to this builtin.
     730          43 :   VARIABLE(var_result, MachineRepresentation::kTagged);
     731          43 :   var_result.Bind(receiver);
     732             :   arguments.ForEach(
     733             :       CodeStubAssembler::VariableList({&var_result}, zone()),
     734          43 :       [this, context, &var_result](Node* arg) {
     735          86 :         arg = CallStub(CodeFactory::ToString(isolate()), context, arg);
     736             :         var_result.Bind(CallStub(CodeFactory::StringAdd(isolate()), context,
     737          43 :                                  var_result.value(), arg));
     738         129 :       });
     739          43 :   arguments.PopAndReturn(var_result.value());
     740          43 : }
     741             : 
     742          86 : void StringBuiltinsAssembler::StringIndexOf(
     743             :     Node* const subject_string, Node* const subject_instance_type,
     744             :     Node* const search_string, Node* const search_instance_type,
     745             :     Node* const position, std::function<void(Node*)> f_return) {
     746             :   CSA_ASSERT(this, IsString(subject_string));
     747             :   CSA_ASSERT(this, IsString(search_string));
     748             :   CSA_ASSERT(this, TaggedIsSmi(position));
     749             : 
     750          86 :   Node* const int_zero = IntPtrConstant(0);
     751             : 
     752          86 :   VARIABLE(var_needle_byte, MachineType::PointerRepresentation(), int_zero);
     753         172 :   VARIABLE(var_string_addr, MachineType::PointerRepresentation(), int_zero);
     754             : 
     755          86 :   Node* const search_length = SmiUntag(LoadStringLength(search_string));
     756          86 :   Node* const subject_length = SmiUntag(LoadStringLength(subject_string));
     757          86 :   Node* const start_position = IntPtrMax(SmiUntag(position), int_zero);
     758             : 
     759          86 :   Label zero_length_needle(this), return_minus_1(this);
     760             :   {
     761          86 :     GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle);
     762             : 
     763             :     // Check that the needle fits in the start position.
     764             :     GotoIfNot(IntPtrLessThanOrEqual(search_length,
     765             :                                     IntPtrSub(subject_length, start_position)),
     766          86 :               &return_minus_1);
     767             :   }
     768             : 
     769             :   // Try to unpack subject and search strings. Bail to runtime if either needs
     770             :   // to be flattened.
     771         172 :   ToDirectStringAssembler subject_to_direct(state(), subject_string);
     772         172 :   ToDirectStringAssembler search_to_direct(state(), search_string);
     773             : 
     774          86 :   Label call_runtime_unchecked(this, Label::kDeferred);
     775             : 
     776          86 :   subject_to_direct.TryToDirect(&call_runtime_unchecked);
     777          86 :   search_to_direct.TryToDirect(&call_runtime_unchecked);
     778             : 
     779             :   // Load pointers to string data.
     780             :   Node* const subject_ptr =
     781             :       subject_to_direct.PointerToData(&call_runtime_unchecked);
     782             :   Node* const search_ptr =
     783             :       search_to_direct.PointerToData(&call_runtime_unchecked);
     784             : 
     785             :   Node* const subject_offset = subject_to_direct.offset();
     786             :   Node* const search_offset = search_to_direct.offset();
     787             : 
     788             :   // Like String::IndexOf, the actual matching is done by the optimized
     789             :   // SearchString method in string-search.h. Dispatch based on string instance
     790             :   // types, then call straight into C++ for matching.
     791             : 
     792             :   CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero));
     793             :   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero));
     794             :   CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position));
     795             :   CSA_ASSERT(this,
     796             :              IntPtrLessThanOrEqual(search_length,
     797             :                                    IntPtrSub(subject_length, start_position)));
     798             : 
     799          86 :   Label one_one(this), one_two(this), two_one(this), two_two(this);
     800             :   DispatchOnStringEncodings(subject_to_direct.instance_type(),
     801             :                             search_to_direct.instance_type(), &one_one,
     802          86 :                             &one_two, &two_one, &two_two);
     803             : 
     804             :   typedef const uint8_t onebyte_t;
     805             :   typedef const uc16 twobyte_t;
     806             : 
     807          86 :   BIND(&one_one);
     808             :   {
     809             :     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
     810          86 :         subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
     811             :     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
     812          86 :         search_ptr, search_offset, String::ONE_BYTE_ENCODING);
     813             : 
     814          86 :     Label direct_memchr_call(this), generic_fast_path(this);
     815             :     Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call,
     816          86 :            &generic_fast_path);
     817             : 
     818             :     // An additional fast path that calls directly into memchr for 1-length
     819             :     // search strings.
     820          86 :     BIND(&direct_memchr_call);
     821             :     {
     822          86 :       Node* const string_addr = IntPtrAdd(adjusted_subject_ptr, start_position);
     823          86 :       Node* const search_length = IntPtrSub(subject_length, start_position);
     824             :       Node* const search_byte =
     825          86 :           ChangeInt32ToIntPtr(Load(MachineType::Uint8(), adjusted_search_ptr));
     826             : 
     827             :       Node* const memchr =
     828          86 :           ExternalConstant(ExternalReference::libc_memchr_function(isolate()));
     829             :       Node* const result_address =
     830             :           CallCFunction3(MachineType::Pointer(), MachineType::Pointer(),
     831             :                          MachineType::IntPtr(), MachineType::UintPtr(), memchr,
     832          86 :                          string_addr, search_byte, search_length);
     833          86 :       GotoIf(WordEqual(result_address, int_zero), &return_minus_1);
     834             :       Node* const result_index =
     835          86 :           IntPtrAdd(IntPtrSub(result_address, string_addr), start_position);
     836          86 :       f_return(SmiTag(result_index));
     837             :     }
     838             : 
     839          86 :     BIND(&generic_fast_path);
     840             :     {
     841             :       Node* const result = CallSearchStringRaw<onebyte_t, onebyte_t>(
     842             :           adjusted_subject_ptr, subject_length, adjusted_search_ptr,
     843          86 :           search_length, start_position);
     844          86 :       f_return(SmiTag(result));
     845          86 :     }
     846             :   }
     847             : 
     848          86 :   BIND(&one_two);
     849             :   {
     850             :     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
     851          86 :         subject_ptr, subject_offset, String::ONE_BYTE_ENCODING);
     852             :     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
     853          86 :         search_ptr, search_offset, String::TWO_BYTE_ENCODING);
     854             : 
     855             :     Node* const result = CallSearchStringRaw<onebyte_t, twobyte_t>(
     856             :         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
     857          86 :         search_length, start_position);
     858          86 :     f_return(SmiTag(result));
     859             :   }
     860             : 
     861          86 :   BIND(&two_one);
     862             :   {
     863             :     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
     864          86 :         subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
     865             :     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
     866          86 :         search_ptr, search_offset, String::ONE_BYTE_ENCODING);
     867             : 
     868             :     Node* const result = CallSearchStringRaw<twobyte_t, onebyte_t>(
     869             :         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
     870          86 :         search_length, start_position);
     871          86 :     f_return(SmiTag(result));
     872             :   }
     873             : 
     874          86 :   BIND(&two_two);
     875             :   {
     876             :     Node* const adjusted_subject_ptr = PointerToStringDataAtIndex(
     877          86 :         subject_ptr, subject_offset, String::TWO_BYTE_ENCODING);
     878             :     Node* const adjusted_search_ptr = PointerToStringDataAtIndex(
     879          86 :         search_ptr, search_offset, String::TWO_BYTE_ENCODING);
     880             : 
     881             :     Node* const result = CallSearchStringRaw<twobyte_t, twobyte_t>(
     882             :         adjusted_subject_ptr, subject_length, adjusted_search_ptr,
     883          86 :         search_length, start_position);
     884          86 :     f_return(SmiTag(result));
     885             :   }
     886             : 
     887          86 :   BIND(&return_minus_1);
     888          86 :   f_return(SmiConstant(-1));
     889             : 
     890          86 :   BIND(&zero_length_needle);
     891             :   {
     892          86 :     Comment("0-length search_string");
     893          86 :     f_return(SmiTag(IntPtrMin(subject_length, start_position)));
     894             :   }
     895             : 
     896          86 :   BIND(&call_runtime_unchecked);
     897             :   {
     898             :     // Simplified version of the runtime call where the types of the arguments
     899             :     // are already known due to type checks in this stub.
     900          86 :     Comment("Call Runtime Unchecked");
     901             :     Node* result = CallRuntime(Runtime::kStringIndexOfUnchecked, SmiConstant(0),
     902          86 :                                subject_string, search_string, position);
     903          86 :     f_return(result);
     904          86 :   }
     905          86 : }
     906             : 
     907             : // ES6 String.prototype.indexOf(searchString [, position])
     908             : // #sec-string.prototype.indexof
     909             : // Unchecked helper for builtins lowering.
     910         172 : TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) {
     911             :   Node* receiver = Parameter(Descriptor::kReceiver);
     912             :   Node* search_string = Parameter(Descriptor::kSearchString);
     913             :   Node* position = Parameter(Descriptor::kPosition);
     914             : 
     915          43 :   Node* instance_type = LoadInstanceType(receiver);
     916          43 :   Node* search_string_instance_type = LoadInstanceType(search_string);
     917             : 
     918             :   StringIndexOf(receiver, instance_type, search_string,
     919             :                 search_string_instance_type, position,
     920         430 :                 [this](Node* result) { this->Return(result); });
     921          43 : }
     922             : 
     923             : // ES6 String.prototype.indexOf(searchString [, position])
     924             : // #sec-string.prototype.indexof
     925         215 : TF_BUILTIN(StringPrototypeIndexOf, StringBuiltinsAssembler) {
     926          43 :   VARIABLE(search_string, MachineRepresentation::kTagged);
     927          86 :   VARIABLE(position, MachineRepresentation::kTagged);
     928          43 :   Label call_runtime(this), call_runtime_unchecked(this), argc_0(this),
     929          43 :       no_argc_0(this), argc_1(this), no_argc_1(this), argc_2(this),
     930          43 :       fast_path(this), return_minus_1(this);
     931             : 
     932             :   // TODO(ishell): use constants from Descriptor once the JSFunction linkage
     933             :   // arguments are reordered.
     934             :   Node* argc = Parameter(BuiltinDescriptor::kArgumentsCount);
     935             :   Node* context = Parameter(BuiltinDescriptor::kContext);
     936             : 
     937          43 :   CodeStubArguments arguments(this, ChangeInt32ToIntPtr(argc));
     938          43 :   Node* receiver = arguments.GetReceiver();
     939             :   // From now on use word-size argc value.
     940          43 :   argc = arguments.GetLength();
     941             : 
     942          43 :   GotoIf(IntPtrEqual(argc, IntPtrConstant(0)), &argc_0);
     943          43 :   GotoIf(IntPtrEqual(argc, IntPtrConstant(1)), &argc_1);
     944          43 :   Goto(&argc_2);
     945          43 :   BIND(&argc_0);
     946             :   {
     947          43 :     Comment("0 Argument case");
     948          43 :     Node* undefined = UndefinedConstant();
     949          43 :     search_string.Bind(undefined);
     950          43 :     position.Bind(undefined);
     951          43 :     Goto(&call_runtime);
     952             :   }
     953          43 :   BIND(&argc_1);
     954             :   {
     955          43 :     Comment("1 Argument case");
     956          43 :     search_string.Bind(arguments.AtIndex(0));
     957          43 :     position.Bind(SmiConstant(0));
     958          43 :     Goto(&fast_path);
     959             :   }
     960          43 :   BIND(&argc_2);
     961             :   {
     962          43 :     Comment("2 Argument case");
     963          43 :     search_string.Bind(arguments.AtIndex(0));
     964          43 :     position.Bind(arguments.AtIndex(1));
     965          43 :     GotoIfNot(TaggedIsSmi(position.value()), &call_runtime);
     966          43 :     Goto(&fast_path);
     967             :   }
     968             : 
     969          43 :   BIND(&fast_path);
     970             :   {
     971          43 :     Comment("Fast Path");
     972          43 :     GotoIf(TaggedIsSmi(receiver), &call_runtime);
     973          43 :     Node* needle = search_string.value();
     974          43 :     GotoIf(TaggedIsSmi(needle), &call_runtime);
     975             : 
     976          43 :     Node* instance_type = LoadInstanceType(receiver);
     977          43 :     GotoIfNot(IsStringInstanceType(instance_type), &call_runtime);
     978             : 
     979          43 :     Node* needle_instance_type = LoadInstanceType(needle);
     980          43 :     GotoIfNot(IsStringInstanceType(needle_instance_type), &call_runtime);
     981             : 
     982             :     StringIndexOf(
     983             :         receiver, instance_type, needle, needle_instance_type, position.value(),
     984         430 :         [&arguments](Node* result) { arguments.PopAndReturn(result); });
     985             :   }
     986             : 
     987          43 :   BIND(&call_runtime);
     988             :   {
     989          43 :     Comment("Call Runtime");
     990             :     Node* result = CallRuntime(Runtime::kStringIndexOf, context, receiver,
     991          43 :                                search_string.value(), position.value());
     992          43 :     arguments.PopAndReturn(result);
     993          43 :   }
     994          43 : }
     995             : 
     996         172 : compiler::Node* StringBuiltinsAssembler::IsNullOrUndefined(Node* const value) {
     997         172 :   return Word32Or(IsUndefined(value), IsNull(value));
     998             : }
     999             : 
    1000          86 : void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
    1001             :                                                      Node* const value,
    1002             :                                                      const char* method_name) {
    1003         172 :   Label out(this), throw_exception(this, Label::kDeferred);
    1004          86 :   Branch(IsNullOrUndefined(value), &throw_exception, &out);
    1005             : 
    1006          86 :   BIND(&throw_exception);
    1007             :   TailCallRuntime(
    1008             :       Runtime::kThrowCalledOnNullOrUndefined, context,
    1009         172 :       HeapConstant(factory()->NewStringFromAsciiChecked(method_name, TENURED)));
    1010             : 
    1011         172 :   BIND(&out);
    1012          86 : }
    1013             : 
    1014          86 : void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
    1015             :     Node* const context, Node* const object, Handle<Symbol> symbol,
    1016             :     const NodeFunction0& regexp_call, const NodeFunction1& generic_call) {
    1017          86 :   Label out(this);
    1018             : 
    1019             :   // Smis definitely don't have an attached symbol.
    1020          86 :   GotoIf(TaggedIsSmi(object), &out);
    1021             : 
    1022          86 :   Node* const object_map = LoadMap(object);
    1023             : 
    1024             :   // Skip the slow lookup for Strings.
    1025             :   {
    1026             :     Label next(this);
    1027             : 
    1028          86 :     GotoIfNot(IsStringInstanceType(LoadMapInstanceType(object_map)), &next);
    1029             : 
    1030          86 :     Node* const native_context = LoadNativeContext(context);
    1031             :     Node* const initial_proto_initial_map = LoadContextElement(
    1032          86 :         native_context, Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX);
    1033             : 
    1034             :     Node* const string_fun =
    1035          86 :         LoadContextElement(native_context, Context::STRING_FUNCTION_INDEX);
    1036             :     Node* const initial_map =
    1037          86 :         LoadObjectField(string_fun, JSFunction::kPrototypeOrInitialMapOffset);
    1038          86 :     Node* const proto_map = LoadMap(LoadMapPrototype(initial_map));
    1039             : 
    1040          86 :     Branch(WordEqual(proto_map, initial_proto_initial_map), &out, &next);
    1041             : 
    1042          86 :     BIND(&next);
    1043             :   }
    1044             : 
    1045             :   // Take the fast path for RegExps.
    1046             :   {
    1047          86 :     Label stub_call(this), slow_lookup(this);
    1048             : 
    1049             :     RegExpBuiltinsAssembler regexp_asm(state());
    1050             :     regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call,
    1051          86 :                                   &slow_lookup);
    1052             : 
    1053          86 :     BIND(&stub_call);
    1054          86 :     Return(regexp_call());
    1055             : 
    1056         172 :     BIND(&slow_lookup);
    1057             :   }
    1058             : 
    1059          86 :   GotoIf(IsNullOrUndefined(object), &out);
    1060             : 
    1061             :   // Fall back to a slow lookup of {object[symbol]}.
    1062             : 
    1063          86 :   Node* const maybe_func = GetProperty(context, object, symbol);
    1064          86 :   GotoIf(IsUndefined(maybe_func), &out);
    1065             : 
    1066             :   // Attempt to call the function.
    1067             : 
    1068          86 :   Node* const result = generic_call(maybe_func);
    1069          86 :   Return(result);
    1070             : 
    1071          86 :   BIND(&out);
    1072          86 : }
    1073             : 
    1074          86 : compiler::Node* StringBuiltinsAssembler::IndexOfDollarChar(Node* const context,
    1075             :                                                            Node* const string) {
    1076             :   CSA_ASSERT(this, IsString(string));
    1077             : 
    1078             :   Node* const dollar_string = HeapConstant(
    1079         172 :       isolate()->factory()->LookupSingleCharacterStringFromCode('$'));
    1080             :   Node* const dollar_ix = CallBuiltin(Builtins::kStringIndexOf, context, string,
    1081          86 :                                       dollar_string, SmiConstant(0));
    1082             : 
    1083             :   CSA_ASSERT(this, TaggedIsSmi(dollar_ix));
    1084          86 :   return dollar_ix;
    1085             : }
    1086             : 
    1087          43 : compiler::Node* StringBuiltinsAssembler::GetSubstitution(
    1088             :     Node* context, Node* subject_string, Node* match_start_index,
    1089             :     Node* match_end_index, Node* replace_string) {
    1090             :   CSA_ASSERT(this, IsString(subject_string));
    1091             :   CSA_ASSERT(this, IsString(replace_string));
    1092             :   CSA_ASSERT(this, TaggedIsPositiveSmi(match_start_index));
    1093             :   CSA_ASSERT(this, TaggedIsPositiveSmi(match_end_index));
    1094             : 
    1095          43 :   VARIABLE(var_result, MachineRepresentation::kTagged, replace_string);
    1096          43 :   Label runtime(this), out(this);
    1097             : 
    1098             :   // In this primitive implementation we simply look for the next '$' char in
    1099             :   // {replace_string}. If it doesn't exist, we can simply return
    1100             :   // {replace_string} itself. If it does, then we delegate to
    1101             :   // String::GetSubstitution, passing in the index of the first '$' to avoid
    1102             :   // repeated scanning work.
    1103             :   // TODO(jgruber): Possibly extend this in the future to handle more complex
    1104             :   // cases without runtime calls.
    1105             : 
    1106          43 :   Node* const dollar_index = IndexOfDollarChar(context, replace_string);
    1107          43 :   Branch(SmiIsNegative(dollar_index), &out, &runtime);
    1108             : 
    1109          43 :   BIND(&runtime);
    1110             :   {
    1111             :     CSA_ASSERT(this, TaggedIsPositiveSmi(dollar_index));
    1112             : 
    1113          43 :     Callable substring_callable = CodeFactory::SubString(isolate());
    1114             :     Node* const matched = CallStub(substring_callable, context, subject_string,
    1115          43 :                                    match_start_index, match_end_index);
    1116             :     Node* const replacement_string =
    1117             :         CallRuntime(Runtime::kGetSubstitution, context, matched, subject_string,
    1118          43 :                     match_start_index, replace_string, dollar_index);
    1119          43 :     var_result.Bind(replacement_string);
    1120             : 
    1121          43 :     Goto(&out);
    1122             :   }
    1123             : 
    1124          43 :   BIND(&out);
    1125          86 :   return var_result.value();
    1126             : }
    1127             : 
    1128             : // ES6 #sec-string.prototype.replace
    1129         215 : TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
    1130          43 :   Label out(this);
    1131             : 
    1132             :   Node* const receiver = Parameter(Descriptor::kReceiver);
    1133             :   Node* const search = Parameter(Descriptor::kSearch);
    1134             :   Node* const replace = Parameter(Descriptor::kReplace);
    1135             :   Node* const context = Parameter(Descriptor::kContext);
    1136             : 
    1137          43 :   Node* const smi_zero = SmiConstant(0);
    1138             : 
    1139          43 :   RequireObjectCoercible(context, receiver, "String.prototype.replace");
    1140             : 
    1141             :   // Redirect to replacer method if {search[@@replace]} is not undefined.
    1142             : 
    1143             :   MaybeCallFunctionAtSymbol(
    1144             :       context, search, isolate()->factory()->replace_symbol(),
    1145          43 :       [=]() {
    1146          43 :         Callable tostring_callable = CodeFactory::ToString(isolate());
    1147             :         Node* const subject_string =
    1148          43 :             CallStub(tostring_callable, context, receiver);
    1149             : 
    1150          43 :         Callable replace_callable = CodeFactory::RegExpReplace(isolate());
    1151             :         return CallStub(replace_callable, context, search, subject_string,
    1152          43 :                         replace);
    1153             :       },
    1154          43 :       [=](Node* fn) {
    1155          43 :         Callable call_callable = CodeFactory::Call(isolate());
    1156          43 :         return CallJS(call_callable, context, fn, search, receiver, replace);
    1157         172 :       });
    1158             : 
    1159             :   // Convert {receiver} and {search} to strings.
    1160             : 
    1161          43 :   Callable tostring_callable = CodeFactory::ToString(isolate());
    1162          43 :   Callable indexof_callable = CodeFactory::StringIndexOf(isolate());
    1163             : 
    1164          43 :   Node* const subject_string = CallStub(tostring_callable, context, receiver);
    1165          43 :   Node* const search_string = CallStub(tostring_callable, context, search);
    1166             : 
    1167          43 :   Node* const subject_length = LoadStringLength(subject_string);
    1168          43 :   Node* const search_length = LoadStringLength(search_string);
    1169             : 
    1170             :   // Fast-path single-char {search}, long cons {receiver}, and simple string
    1171             :   // {replace}.
    1172             :   {
    1173             :     Label next(this);
    1174             : 
    1175          43 :     GotoIfNot(SmiEqual(search_length, SmiConstant(1)), &next);
    1176          43 :     GotoIfNot(SmiGreaterThan(subject_length, SmiConstant(0xFF)), &next);
    1177          43 :     GotoIf(TaggedIsSmi(replace), &next);
    1178          43 :     GotoIfNot(IsString(replace), &next);
    1179             : 
    1180          43 :     Node* const subject_instance_type = LoadInstanceType(subject_string);
    1181          43 :     GotoIfNot(IsConsStringInstanceType(subject_instance_type), &next);
    1182             : 
    1183          43 :     GotoIf(TaggedIsPositiveSmi(IndexOfDollarChar(context, replace)), &next);
    1184             : 
    1185             :     // Searching by traversing a cons string tree and replace with cons of
    1186             :     // slices works only when the replaced string is a single character, being
    1187             :     // replaced by a simple string and only pays off for long strings.
    1188             :     // TODO(jgruber): Reevaluate if this is still beneficial.
    1189             :     // TODO(jgruber): TailCallRuntime when it correctly handles adapter frames.
    1190             :     Return(CallRuntime(Runtime::kStringReplaceOneCharWithString, context,
    1191          43 :                        subject_string, search_string, replace));
    1192             : 
    1193          43 :     BIND(&next);
    1194             :   }
    1195             : 
    1196             :   // TODO(jgruber): Extend StringIndexOf to handle two-byte strings and
    1197             :   // longer substrings - we can handle up to 8 chars (one-byte) / 4 chars
    1198             :   // (2-byte).
    1199             : 
    1200             :   Node* const match_start_index = CallStub(
    1201          43 :       indexof_callable, context, subject_string, search_string, smi_zero);
    1202             :   CSA_ASSERT(this, TaggedIsSmi(match_start_index));
    1203             : 
    1204             :   // Early exit if no match found.
    1205             :   {
    1206          43 :     Label next(this), return_subject(this);
    1207             : 
    1208          43 :     GotoIfNot(SmiIsNegative(match_start_index), &next);
    1209             : 
    1210             :     // The spec requires to perform ToString(replace) if the {replace} is not
    1211             :     // callable even if we are going to exit here.
    1212             :     // Since ToString() being applied to Smi does not have side effects for
    1213             :     // numbers we can skip it.
    1214          43 :     GotoIf(TaggedIsSmi(replace), &return_subject);
    1215          43 :     GotoIf(IsCallableMap(LoadMap(replace)), &return_subject);
    1216             : 
    1217             :     // TODO(jgruber): Could introduce ToStringSideeffectsStub which only
    1218             :     // performs observable parts of ToString.
    1219          43 :     CallStub(tostring_callable, context, replace);
    1220          43 :     Goto(&return_subject);
    1221             : 
    1222          43 :     BIND(&return_subject);
    1223          43 :     Return(subject_string);
    1224             : 
    1225          86 :     BIND(&next);
    1226             :   }
    1227             : 
    1228          43 :   Node* const match_end_index = SmiAdd(match_start_index, search_length);
    1229             : 
    1230          43 :   Callable substring_callable = CodeFactory::SubString(isolate());
    1231             :   Callable stringadd_callable =
    1232          43 :       CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
    1233             : 
    1234          86 :   VARIABLE(var_result, MachineRepresentation::kTagged, EmptyStringConstant());
    1235             : 
    1236             :   // Compute the prefix.
    1237             :   {
    1238             :     Label next(this);
    1239             : 
    1240          43 :     GotoIf(SmiEqual(match_start_index, smi_zero), &next);
    1241             :     Node* const prefix = CallStub(substring_callable, context, subject_string,
    1242          43 :                                   smi_zero, match_start_index);
    1243          43 :     var_result.Bind(prefix);
    1244             : 
    1245          43 :     Goto(&next);
    1246          43 :     BIND(&next);
    1247             :   }
    1248             : 
    1249             :   // Compute the string to replace with.
    1250             : 
    1251          43 :   Label if_iscallablereplace(this), if_notcallablereplace(this);
    1252          43 :   GotoIf(TaggedIsSmi(replace), &if_notcallablereplace);
    1253             :   Branch(IsCallableMap(LoadMap(replace)), &if_iscallablereplace,
    1254          43 :          &if_notcallablereplace);
    1255             : 
    1256          43 :   BIND(&if_iscallablereplace);
    1257             :   {
    1258          43 :     Callable call_callable = CodeFactory::Call(isolate());
    1259             :     Node* const replacement =
    1260             :         CallJS(call_callable, context, replace, UndefinedConstant(),
    1261          43 :                search_string, match_start_index, subject_string);
    1262             :     Node* const replacement_string =
    1263          43 :         CallStub(tostring_callable, context, replacement);
    1264             :     var_result.Bind(CallStub(stringadd_callable, context, var_result.value(),
    1265          43 :                              replacement_string));
    1266          43 :     Goto(&out);
    1267             :   }
    1268             : 
    1269          43 :   BIND(&if_notcallablereplace);
    1270             :   {
    1271          43 :     Node* const replace_string = CallStub(tostring_callable, context, replace);
    1272             :     Node* const replacement =
    1273             :         GetSubstitution(context, subject_string, match_start_index,
    1274          43 :                         match_end_index, replace_string);
    1275             :     var_result.Bind(
    1276          43 :         CallStub(stringadd_callable, context, var_result.value(), replacement));
    1277          43 :     Goto(&out);
    1278             :   }
    1279             : 
    1280          43 :   BIND(&out);
    1281             :   {
    1282             :     Node* const suffix = CallStub(substring_callable, context, subject_string,
    1283          43 :                                   match_end_index, subject_length);
    1284             :     Node* const result =
    1285          43 :         CallStub(stringadd_callable, context, var_result.value(), suffix);
    1286          43 :     Return(result);
    1287          43 :   }
    1288          43 : }
    1289             : 
    1290             : // ES6 section 21.1.3.19 String.prototype.split ( separator, limit )
    1291         215 : TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
    1292          43 :   Label out(this);
    1293             : 
    1294             :   Node* const receiver = Parameter(Descriptor::kReceiver);
    1295             :   Node* const separator = Parameter(Descriptor::kSeparator);
    1296             :   Node* const limit = Parameter(Descriptor::kLimit);
    1297             :   Node* const context = Parameter(Descriptor::kContext);
    1298             : 
    1299          43 :   Node* const smi_zero = SmiConstant(0);
    1300             : 
    1301          43 :   RequireObjectCoercible(context, receiver, "String.prototype.split");
    1302             : 
    1303             :   // Redirect to splitter method if {separator[@@split]} is not undefined.
    1304             : 
    1305             :   MaybeCallFunctionAtSymbol(
    1306             :       context, separator, isolate()->factory()->split_symbol(),
    1307          43 :       [=]() {
    1308          43 :         Callable tostring_callable = CodeFactory::ToString(isolate());
    1309             :         Node* const subject_string =
    1310          43 :             CallStub(tostring_callable, context, receiver);
    1311             : 
    1312          43 :         Callable split_callable = CodeFactory::RegExpSplit(isolate());
    1313             :         return CallStub(split_callable, context, separator, subject_string,
    1314          43 :                         limit);
    1315             :       },
    1316          43 :       [=](Node* fn) {
    1317          43 :         Callable call_callable = CodeFactory::Call(isolate());
    1318          43 :         return CallJS(call_callable, context, fn, separator, receiver, limit);
    1319         172 :       });
    1320             : 
    1321             :   // String and integer conversions.
    1322             :   // TODO(jgruber): The old implementation used Uint32Max instead of SmiMax -
    1323             :   // but AFAIK there should not be a difference since arrays are capped at Smi
    1324             :   // lengths.
    1325             : 
    1326          43 :   Callable tostring_callable = CodeFactory::ToString(isolate());
    1327          43 :   Node* const subject_string = CallStub(tostring_callable, context, receiver);
    1328             :   Node* const limit_number =
    1329          43 :       Select(IsUndefined(limit), [=]() { return SmiConstant(Smi::kMaxValue); },
    1330          43 :              [=]() { return ToUint32(context, limit); },
    1331         129 :              MachineRepresentation::kTagged);
    1332             :   Node* const separator_string =
    1333          43 :       CallStub(tostring_callable, context, separator);
    1334             : 
    1335             :   // Shortcut for {limit} == 0.
    1336             :   {
    1337             :     Label next(this);
    1338          43 :     GotoIfNot(SmiEqual(limit_number, smi_zero), &next);
    1339             : 
    1340             :     const ElementsKind kind = FAST_ELEMENTS;
    1341          43 :     Node* const native_context = LoadNativeContext(context);
    1342          43 :     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
    1343             : 
    1344             :     Node* const length = smi_zero;
    1345          43 :     Node* const capacity = IntPtrConstant(0);
    1346          43 :     Node* const result = AllocateJSArray(kind, array_map, capacity, length);
    1347             : 
    1348          43 :     Return(result);
    1349             : 
    1350          43 :     BIND(&next);
    1351             :   }
    1352             : 
    1353             :   // ECMA-262 says that if {separator} is undefined, the result should
    1354             :   // be an array of size 1 containing the entire string.
    1355             :   {
    1356             :     Label next(this);
    1357          43 :     GotoIfNot(IsUndefined(separator), &next);
    1358             : 
    1359             :     const ElementsKind kind = FAST_ELEMENTS;
    1360          43 :     Node* const native_context = LoadNativeContext(context);
    1361          43 :     Node* const array_map = LoadJSArrayElementsMap(kind, native_context);
    1362             : 
    1363          43 :     Node* const length = SmiConstant(1);
    1364          43 :     Node* const capacity = IntPtrConstant(1);
    1365          43 :     Node* const result = AllocateJSArray(kind, array_map, capacity, length);
    1366             : 
    1367          43 :     Node* const fixed_array = LoadElements(result);
    1368          43 :     StoreFixedArrayElement(fixed_array, 0, subject_string);
    1369             : 
    1370          43 :     Return(result);
    1371             : 
    1372          43 :     BIND(&next);
    1373             :   }
    1374             : 
    1375             :   // If the separator string is empty then return the elements in the subject.
    1376             :   {
    1377             :     Label next(this);
    1378          43 :     GotoIfNot(SmiEqual(LoadStringLength(separator_string), smi_zero), &next);
    1379             : 
    1380             :     Node* const result = CallRuntime(Runtime::kStringToArray, context,
    1381          43 :                                      subject_string, limit_number);
    1382          43 :     Return(result);
    1383             : 
    1384          43 :     BIND(&next);
    1385             :   }
    1386             : 
    1387             :   Node* const result =
    1388             :       CallRuntime(Runtime::kStringSplit, context, subject_string,
    1389          43 :                   separator_string, limit_number);
    1390          86 :   Return(result);
    1391          43 : }
    1392             : 
    1393             : // ES6 #sec-string.prototype.substr
    1394         172 : TF_BUILTIN(StringPrototypeSubstr, CodeStubAssembler) {
    1395          86 :   Label out(this), handle_length(this);
    1396             : 
    1397          86 :   VARIABLE(var_start, MachineRepresentation::kTagged);
    1398          86 :   VARIABLE(var_length, MachineRepresentation::kTagged);
    1399             : 
    1400             :   Node* const receiver = Parameter(Descriptor::kReceiver);
    1401             :   Node* const start = Parameter(Descriptor::kStart);
    1402             :   Node* const length = Parameter(Descriptor::kLength);
    1403             :   Node* const context = Parameter(Descriptor::kContext);
    1404             : 
    1405          43 :   Node* const zero = SmiConstant(Smi::kZero);
    1406             : 
    1407             :   // Check that {receiver} is coercible to Object and convert it to a String.
    1408             :   Node* const string =
    1409          43 :       ToThisString(context, receiver, "String.prototype.substr");
    1410             : 
    1411          43 :   Node* const string_length = LoadStringLength(string);
    1412             : 
    1413             :   // Conversions and bounds-checks for {start}.
    1414             :   {
    1415             :     Node* const start_int =
    1416          43 :         ToInteger(context, start, CodeStubAssembler::kTruncateMinusZero);
    1417             : 
    1418          43 :     Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
    1419          43 :     Branch(TaggedIsSmi(start_int), &if_issmi, &if_isheapnumber);
    1420             : 
    1421          43 :     BIND(&if_issmi);
    1422             :     {
    1423          43 :       Node* const length_plus_start = SmiAdd(string_length, start_int);
    1424             :       var_start.Bind(Select(SmiLessThan(start_int, zero),
    1425          43 :                             [&] { return SmiMax(length_plus_start, zero); },
    1426          43 :                             [&] { return start_int; },
    1427         129 :                             MachineRepresentation::kTagged));
    1428          43 :       Goto(&handle_length);
    1429             :     }
    1430             : 
    1431          43 :     BIND(&if_isheapnumber);
    1432             :     {
    1433             :       // If {start} is a heap number, it is definitely out of bounds. If it is
    1434             :       // negative, {start} = max({string_length} + {start}),0) = 0'. If it is
    1435             :       // positive, set {start} to {string_length} which ultimately results in
    1436             :       // returning an empty string.
    1437          43 :       Node* const float_zero = Float64Constant(0.);
    1438          43 :       Node* const start_float = LoadHeapNumberValue(start_int);
    1439             :       var_start.Bind(SelectTaggedConstant(
    1440          43 :           Float64LessThan(start_float, float_zero), zero, string_length));
    1441          43 :       Goto(&handle_length);
    1442          43 :     }
    1443             :   }
    1444             : 
    1445             :   // Conversions and bounds-checks for {length}.
    1446          43 :   BIND(&handle_length);
    1447             :   {
    1448          43 :     Label if_issmi(this), if_isheapnumber(this, Label::kDeferred);
    1449             : 
    1450             :     // Default to {string_length} if {length} is undefined.
    1451             :     {
    1452          43 :       Label if_isundefined(this, Label::kDeferred), if_isnotundefined(this);
    1453             :       Branch(WordEqual(length, UndefinedConstant()), &if_isundefined,
    1454          43 :              &if_isnotundefined);
    1455             : 
    1456          43 :       BIND(&if_isundefined);
    1457          43 :       var_length.Bind(string_length);
    1458          43 :       Goto(&if_issmi);
    1459             : 
    1460          43 :       BIND(&if_isnotundefined);
    1461             :       var_length.Bind(
    1462          86 :           ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero));
    1463             :     }
    1464             : 
    1465          43 :     Branch(TaggedIsSmi(var_length.value()), &if_issmi, &if_isheapnumber);
    1466             : 
    1467             :     // Set {length} to min(max({length}, 0), {string_length} - {start}
    1468          43 :     BIND(&if_issmi);
    1469             :     {
    1470          43 :       Node* const positive_length = SmiMax(var_length.value(), zero);
    1471             : 
    1472          43 :       Node* const minimal_length = SmiSub(string_length, var_start.value());
    1473          43 :       var_length.Bind(SmiMin(positive_length, minimal_length));
    1474             : 
    1475          43 :       GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
    1476          43 :       Return(EmptyStringConstant());
    1477             :     }
    1478             : 
    1479          43 :     BIND(&if_isheapnumber);
    1480             :     {
    1481             :       // If {length} is a heap number, it is definitely out of bounds. There are
    1482             :       // two cases according to the spec: if it is negative, "" is returned; if
    1483             :       // it is positive, then length is set to {string_length} - {start}.
    1484             : 
    1485             :       CSA_ASSERT(this, IsHeapNumberMap(LoadMap(var_length.value())));
    1486             : 
    1487          43 :       Label if_isnegative(this), if_ispositive(this);
    1488          43 :       Node* const float_zero = Float64Constant(0.);
    1489          43 :       Node* const length_float = LoadHeapNumberValue(var_length.value());
    1490             :       Branch(Float64LessThan(length_float, float_zero), &if_isnegative,
    1491          43 :              &if_ispositive);
    1492             : 
    1493          43 :       BIND(&if_isnegative);
    1494          43 :       Return(EmptyStringConstant());
    1495             : 
    1496          43 :       BIND(&if_ispositive);
    1497             :       {
    1498          43 :         var_length.Bind(SmiSub(string_length, var_start.value()));
    1499          43 :         GotoIfNot(SmiLessThanOrEqual(var_length.value(), zero), &out);
    1500          43 :         Return(EmptyStringConstant());
    1501          43 :       }
    1502          43 :     }
    1503             :   }
    1504             : 
    1505          43 :   BIND(&out);
    1506             :   {
    1507          43 :     Node* const end = SmiAdd(var_start.value(), var_length.value());
    1508          43 :     Node* const result = SubString(context, string, var_start.value(), end);
    1509          43 :     Return(result);
    1510          43 :   }
    1511          43 : }
    1512             : 
    1513          86 : compiler::Node* StringBuiltinsAssembler::ToSmiBetweenZeroAnd(Node* context,
    1514             :                                                              Node* value,
    1515             :                                                              Node* limit) {
    1516          86 :   Label out(this);
    1517         172 :   VARIABLE(var_result, MachineRepresentation::kTagged);
    1518             : 
    1519             :   Node* const value_int =
    1520          86 :       this->ToInteger(context, value, CodeStubAssembler::kTruncateMinusZero);
    1521             : 
    1522          86 :   Label if_issmi(this), if_isnotsmi(this, Label::kDeferred);
    1523          86 :   Branch(TaggedIsSmi(value_int), &if_issmi, &if_isnotsmi);
    1524             : 
    1525          86 :   BIND(&if_issmi);
    1526             :   {
    1527          86 :     Label if_isinbounds(this), if_isoutofbounds(this, Label::kDeferred);
    1528          86 :     Branch(SmiAbove(value_int, limit), &if_isoutofbounds, &if_isinbounds);
    1529             : 
    1530          86 :     BIND(&if_isinbounds);
    1531             :     {
    1532          86 :       var_result.Bind(value_int);
    1533          86 :       Goto(&out);
    1534             :     }
    1535             : 
    1536          86 :     BIND(&if_isoutofbounds);
    1537             :     {
    1538          86 :       Node* const zero = SmiConstant(Smi::kZero);
    1539             :       var_result.Bind(
    1540          86 :           SelectTaggedConstant(SmiLessThan(value_int, zero), zero, limit));
    1541          86 :       Goto(&out);
    1542          86 :     }
    1543             :   }
    1544             : 
    1545          86 :   BIND(&if_isnotsmi);
    1546             :   {
    1547             :     // {value} is a heap number - in this case, it is definitely out of bounds.
    1548             :     CSA_ASSERT(this, IsHeapNumberMap(LoadMap(value_int)));
    1549             : 
    1550          86 :     Node* const float_zero = Float64Constant(0.);
    1551          86 :     Node* const smi_zero = SmiConstant(Smi::kZero);
    1552          86 :     Node* const value_float = LoadHeapNumberValue(value_int);
    1553             :     var_result.Bind(SelectTaggedConstant(
    1554          86 :         Float64LessThan(value_float, float_zero), smi_zero, limit));
    1555          86 :     Goto(&out);
    1556             :   }
    1557             : 
    1558          86 :   BIND(&out);
    1559         172 :   return var_result.value();
    1560             : }
    1561             : 
    1562             : // ES6 #sec-string.prototype.substring
    1563         215 : TF_BUILTIN(StringPrototypeSubstring, StringBuiltinsAssembler) {
    1564          43 :   Label out(this);
    1565             : 
    1566          86 :   VARIABLE(var_start, MachineRepresentation::kTagged);
    1567          86 :   VARIABLE(var_end, MachineRepresentation::kTagged);
    1568             : 
    1569             :   Node* const receiver = Parameter(Descriptor::kReceiver);
    1570             :   Node* const start = Parameter(Descriptor::kStart);
    1571             :   Node* const end = Parameter(Descriptor::kEnd);
    1572             :   Node* const context = Parameter(Descriptor::kContext);
    1573             : 
    1574             :   // Check that {receiver} is coercible to Object and convert it to a String.
    1575             :   Node* const string =
    1576          43 :       ToThisString(context, receiver, "String.prototype.substring");
    1577             : 
    1578          43 :   Node* const length = LoadStringLength(string);
    1579             : 
    1580             :   // Conversion and bounds-checks for {start}.
    1581          43 :   var_start.Bind(ToSmiBetweenZeroAnd(context, start, length));
    1582             : 
    1583             :   // Conversion and bounds-checks for {end}.
    1584             :   {
    1585          43 :     var_end.Bind(length);
    1586          43 :     GotoIf(WordEqual(end, UndefinedConstant()), &out);
    1587             : 
    1588          43 :     var_end.Bind(ToSmiBetweenZeroAnd(context, end, length));
    1589             : 
    1590             :     Label if_endislessthanstart(this);
    1591             :     Branch(SmiLessThan(var_end.value(), var_start.value()),
    1592          43 :            &if_endislessthanstart, &out);
    1593             : 
    1594          43 :     BIND(&if_endislessthanstart);
    1595             :     {
    1596          43 :       Node* const tmp = var_end.value();
    1597          43 :       var_end.Bind(var_start.value());
    1598          43 :       var_start.Bind(tmp);
    1599          43 :       Goto(&out);
    1600          43 :     }
    1601             :   }
    1602             : 
    1603          43 :   BIND(&out);
    1604             :   {
    1605             :     Node* result =
    1606          43 :         SubString(context, string, var_start.value(), var_end.value());
    1607          43 :     Return(result);
    1608          43 :   }
    1609          43 : }
    1610             : 
    1611             : // ES6 #sec-string.prototype.tostring
    1612         129 : TF_BUILTIN(StringPrototypeToString, CodeStubAssembler) {
    1613             :   Node* context = Parameter(Descriptor::kContext);
    1614             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1615             : 
    1616             :   Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
    1617          43 :                              "String.prototype.toString");
    1618          43 :   Return(result);
    1619          43 : }
    1620             : 
    1621             : // ES6 #sec-string.prototype.valueof
    1622         129 : TF_BUILTIN(StringPrototypeValueOf, CodeStubAssembler) {
    1623             :   Node* context = Parameter(Descriptor::kContext);
    1624             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1625             : 
    1626             :   Node* result = ToThisValue(context, receiver, PrimitiveType::kString,
    1627          43 :                              "String.prototype.valueOf");
    1628          43 :   Return(result);
    1629          43 : }
    1630             : 
    1631         129 : TF_BUILTIN(StringPrototypeIterator, CodeStubAssembler) {
    1632             :   Node* context = Parameter(Descriptor::kContext);
    1633             :   Node* receiver = Parameter(Descriptor::kReceiver);
    1634             : 
    1635             :   Node* string =
    1636          43 :       ToThisString(context, receiver, "String.prototype[Symbol.iterator]");
    1637             : 
    1638          43 :   Node* native_context = LoadNativeContext(context);
    1639             :   Node* map =
    1640          43 :       LoadContextElement(native_context, Context::STRING_ITERATOR_MAP_INDEX);
    1641          43 :   Node* iterator = Allocate(JSStringIterator::kSize);
    1642          43 :   StoreMapNoWriteBarrier(iterator, map);
    1643             :   StoreObjectFieldRoot(iterator, JSValue::kPropertiesOffset,
    1644          43 :                        Heap::kEmptyFixedArrayRootIndex);
    1645             :   StoreObjectFieldRoot(iterator, JSObject::kElementsOffset,
    1646          43 :                        Heap::kEmptyFixedArrayRootIndex);
    1647             :   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kStringOffset,
    1648          43 :                                  string);
    1649          43 :   Node* index = SmiConstant(Smi::kZero);
    1650             :   StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
    1651          43 :                                  index);
    1652          43 :   Return(iterator);
    1653          43 : }
    1654             : 
    1655             : // Return the |word32| codepoint at {index}. Supports SeqStrings and
    1656             : // ExternalStrings.
    1657          43 : compiler::Node* StringBuiltinsAssembler::LoadSurrogatePairAt(
    1658             :     compiler::Node* string, compiler::Node* length, compiler::Node* index,
    1659             :     UnicodeEncoding encoding) {
    1660          86 :   Label handle_surrogate_pair(this), return_result(this);
    1661          86 :   VARIABLE(var_result, MachineRepresentation::kWord32);
    1662          86 :   VARIABLE(var_trail, MachineRepresentation::kWord32);
    1663          43 :   var_result.Bind(StringCharCodeAt(string, index));
    1664          43 :   var_trail.Bind(Int32Constant(0));
    1665             : 
    1666             :   GotoIf(Word32NotEqual(Word32And(var_result.value(), Int32Constant(0xFC00)),
    1667             :                         Int32Constant(0xD800)),
    1668          43 :          &return_result);
    1669          43 :   Node* next_index = SmiAdd(index, SmiConstant(Smi::FromInt(1)));
    1670             : 
    1671          43 :   GotoIfNot(SmiLessThan(next_index, length), &return_result);
    1672          43 :   var_trail.Bind(StringCharCodeAt(string, next_index));
    1673             :   Branch(Word32Equal(Word32And(var_trail.value(), Int32Constant(0xFC00)),
    1674             :                      Int32Constant(0xDC00)),
    1675          43 :          &handle_surrogate_pair, &return_result);
    1676             : 
    1677          43 :   BIND(&handle_surrogate_pair);
    1678             :   {
    1679          43 :     Node* lead = var_result.value();
    1680          43 :     Node* trail = var_trail.value();
    1681             : 
    1682             :     // Check that this path is only taken if a surrogate pair is found
    1683             :     CSA_SLOW_ASSERT(this,
    1684             :                     Uint32GreaterThanOrEqual(lead, Int32Constant(0xD800)));
    1685             :     CSA_SLOW_ASSERT(this, Uint32LessThan(lead, Int32Constant(0xDC00)));
    1686             :     CSA_SLOW_ASSERT(this,
    1687             :                     Uint32GreaterThanOrEqual(trail, Int32Constant(0xDC00)));
    1688             :     CSA_SLOW_ASSERT(this, Uint32LessThan(trail, Int32Constant(0xE000)));
    1689             : 
    1690          43 :     switch (encoding) {
    1691             :       case UnicodeEncoding::UTF16:
    1692             :         var_result.Bind(Word32Or(
    1693             : // Need to swap the order for big-endian platforms
    1694             : #if V8_TARGET_BIG_ENDIAN
    1695             :             Word32Shl(lead, Int32Constant(16)), trail));
    1696             : #else
    1697          43 :             Word32Shl(trail, Int32Constant(16)), lead));
    1698             : #endif
    1699          43 :         break;
    1700             : 
    1701             :       case UnicodeEncoding::UTF32: {
    1702             :         // Convert UTF16 surrogate pair into |word32| code point, encoded as
    1703             :         // UTF32.
    1704             :         Node* surrogate_offset =
    1705           0 :             Int32Constant(0x10000 - (0xD800 << 10) - 0xDC00);
    1706             : 
    1707             :         // (lead << 10) + trail + SURROGATE_OFFSET
    1708             :         var_result.Bind(Int32Add(WordShl(lead, Int32Constant(10)),
    1709           0 :                                  Int32Add(trail, surrogate_offset)));
    1710           0 :         break;
    1711             :       }
    1712             :     }
    1713          43 :     Goto(&return_result);
    1714             :   }
    1715             : 
    1716          43 :   BIND(&return_result);
    1717          86 :   return var_result.value();
    1718             : }
    1719             : 
    1720             : // ES6 #sec-%stringiteratorprototype%.next
    1721         215 : TF_BUILTIN(StringIteratorPrototypeNext, StringBuiltinsAssembler) {
    1722          43 :   VARIABLE(var_value, MachineRepresentation::kTagged);
    1723          86 :   VARIABLE(var_done, MachineRepresentation::kTagged);
    1724             : 
    1725          43 :   var_value.Bind(UndefinedConstant());
    1726          43 :   var_done.Bind(BooleanConstant(true));
    1727             : 
    1728          43 :   Label throw_bad_receiver(this), next_codepoint(this), return_result(this);
    1729             : 
    1730             :   Node* context = Parameter(Descriptor::kContext);
    1731             :   Node* iterator = Parameter(Descriptor::kReceiver);
    1732             : 
    1733          43 :   GotoIf(TaggedIsSmi(iterator), &throw_bad_receiver);
    1734             :   GotoIfNot(Word32Equal(LoadInstanceType(iterator),
    1735             :                         Int32Constant(JS_STRING_ITERATOR_TYPE)),
    1736          43 :             &throw_bad_receiver);
    1737             : 
    1738          43 :   Node* string = LoadObjectField(iterator, JSStringIterator::kStringOffset);
    1739             :   Node* position =
    1740          43 :       LoadObjectField(iterator, JSStringIterator::kNextIndexOffset);
    1741          43 :   Node* length = LoadObjectField(string, String::kLengthOffset);
    1742             : 
    1743          43 :   Branch(SmiLessThan(position, length), &next_codepoint, &return_result);
    1744             : 
    1745          43 :   BIND(&next_codepoint);
    1746             :   {
    1747             :     UnicodeEncoding encoding = UnicodeEncoding::UTF16;
    1748          43 :     Node* ch = LoadSurrogatePairAt(string, length, position, encoding);
    1749          43 :     Node* value = StringFromCodePoint(ch, encoding);
    1750          43 :     var_value.Bind(value);
    1751          43 :     Node* length = LoadObjectField(value, String::kLengthOffset);
    1752             :     StoreObjectFieldNoWriteBarrier(iterator, JSStringIterator::kNextIndexOffset,
    1753          43 :                                    SmiAdd(position, length));
    1754          43 :     var_done.Bind(BooleanConstant(false));
    1755          43 :     Goto(&return_result);
    1756             :   }
    1757             : 
    1758          43 :   BIND(&return_result);
    1759             :   {
    1760          43 :     Node* native_context = LoadNativeContext(context);
    1761             :     Node* map =
    1762          43 :         LoadContextElement(native_context, Context::ITERATOR_RESULT_MAP_INDEX);
    1763          43 :     Node* result = Allocate(JSIteratorResult::kSize);
    1764          43 :     StoreMapNoWriteBarrier(result, map);
    1765             :     StoreObjectFieldRoot(result, JSIteratorResult::kPropertiesOffset,
    1766          43 :                          Heap::kEmptyFixedArrayRootIndex);
    1767             :     StoreObjectFieldRoot(result, JSIteratorResult::kElementsOffset,
    1768          43 :                          Heap::kEmptyFixedArrayRootIndex);
    1769             :     StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kValueOffset,
    1770          43 :                                    var_value.value());
    1771             :     StoreObjectFieldNoWriteBarrier(result, JSIteratorResult::kDoneOffset,
    1772          43 :                                    var_done.value());
    1773          43 :     Return(result);
    1774             :   }
    1775             : 
    1776          43 :   BIND(&throw_bad_receiver);
    1777             :   {
    1778             :     // The {receiver} is not a valid JSGeneratorObject.
    1779             :     CallRuntime(Runtime::kThrowIncompatibleMethodReceiver, context,
    1780             :                 HeapConstant(factory()->NewStringFromAsciiChecked(
    1781             :                     "String Iterator.prototype.next", TENURED)),
    1782          86 :                 iterator);
    1783          43 :     Unreachable();
    1784          43 :   }
    1785          43 : }
    1786             : 
    1787             : }  // namespace internal
    1788             : }  // namespace v8

Generated by: LCOV version 1.10