| Index: src/builtins/builtins-regexp.cc
 | 
| diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc
 | 
| index baf163fd456e07699eda1d3ec7e0fab54a1155fd..731e4dd82491dcdc33f63df82173ed14e27453a3 100644
 | 
| --- a/src/builtins/builtins-regexp.cc
 | 
| +++ b/src/builtins/builtins-regexp.cc
 | 
| @@ -215,6 +215,285 @@ Node* RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
 | 
|    return result;
 | 
|  }
 | 
|  
 | 
| +void RegExpBuiltinsAssembler::GetStringPointers(
 | 
| +    Node* const string, Node* const offset, Node* const last_index,
 | 
| +    Node* const string_length, bool is_one_byte, Variable* var_string_start,
 | 
| +    Variable* var_string_end) {
 | 
| +  DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
 | 
| +  DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
 | 
| +
 | 
| +  STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
 | 
| +  const int kHeaderSize = SeqOneByteString::kHeaderSize - kHeapObjectTag;
 | 
| +  const ElementsKind kind = is_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
 | 
| +
 | 
| +  Node* const from_offset = ElementOffsetFromIndex(
 | 
| +      IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS, kHeaderSize);
 | 
| +  var_string_start->Bind(IntPtrAdd(string, from_offset));
 | 
| +
 | 
| +  Node* const to_offset = ElementOffsetFromIndex(
 | 
| +      IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS, kHeaderSize);
 | 
| +  var_string_end->Bind(IntPtrAdd(string, to_offset));
 | 
| +}
 | 
| +
 | 
| +Node* RegExpBuiltinsAssembler::IrregexpExec(Node* const context,
 | 
| +                                            Node* const regexp,
 | 
| +                                            Node* const string,
 | 
| +                                            Node* const last_index,
 | 
| +                                            Node* const match_info) {
 | 
| +// Just jump directly to runtime if native RegExp is not selected at compile
 | 
| +// time or if regexp entry in generated code is turned off runtime switch or
 | 
| +// at compilation.
 | 
| +#ifdef V8_INTERPRETED_REGEXP
 | 
| +  return CallRuntime(Runtime::kRegExpExec, context, regexp, string, last_index,
 | 
| +                     match_info);
 | 
| +#else   // V8_INTERPRETED_REGEXP
 | 
| +  CSA_ASSERT(this, TaggedIsNotSmi(regexp));
 | 
| +  CSA_ASSERT(this, HasInstanceType(regexp, JS_REGEXP_TYPE));
 | 
| +
 | 
| +  CSA_ASSERT(this, TaggedIsNotSmi(string));
 | 
| +  CSA_ASSERT(this, IsString(string));
 | 
| +
 | 
| +  CSA_ASSERT(this, IsHeapNumberMap(LoadReceiverMap(last_index)));
 | 
| +  CSA_ASSERT(this, IsFixedArrayMap(LoadReceiverMap(match_info)));
 | 
| +
 | 
| +  Node* const int_zero = IntPtrConstant(0);
 | 
| +
 | 
| +  Variable var_result(this, MachineRepresentation::kTagged);
 | 
| +  Variable var_string(this, MachineType::PointerRepresentation(), int_zero);
 | 
| +  Variable var_string_offset(this, MachineType::PointerRepresentation(),
 | 
| +                             int_zero);
 | 
| +  Variable var_string_instance_type(this, MachineRepresentation::kWord32,
 | 
| +                                    Int32Constant(0));
 | 
| +
 | 
| +  Label out(this), runtime(this, Label::kDeferred);
 | 
| +
 | 
| +  // External constants.
 | 
| +  Node* const regexp_stack_memory_size_address = ExternalConstant(
 | 
| +      ExternalReference::address_of_regexp_stack_memory_size(isolate()));
 | 
| +  Node* const static_offsets_vector_address = ExternalConstant(
 | 
| +      ExternalReference::address_of_static_offsets_vector(isolate()));
 | 
| +  Node* const pending_exception_address = ExternalConstant(
 | 
| +      ExternalReference(Isolate::kPendingExceptionAddress, isolate()));
 | 
| +
 | 
| +  // Ensure that a RegExp stack is allocated.
 | 
| +  {
 | 
| +    Node* const stack_size =
 | 
| +        Load(MachineType::IntPtr(), regexp_stack_memory_size_address);
 | 
| +    GotoIf(IntPtrEqual(stack_size, int_zero), &runtime);
 | 
| +  }
 | 
| +
 | 
| +  Node* const data = LoadObjectField(regexp, JSRegExp::kDataOffset);
 | 
| +  {
 | 
| +    // Check that the RegExp has been compiled (data contains a fixed array).
 | 
| +    CSA_ASSERT(this, TaggedIsNotSmi(data));
 | 
| +    CSA_ASSERT(this, HasInstanceType(data, FIXED_ARRAY_TYPE));
 | 
| +
 | 
| +    // Check the type of the RegExp. Only continue if type is
 | 
| +    // JSRegExp::IRREGEXP.
 | 
| +    Node* const tag = LoadFixedArrayElement(data, JSRegExp::kTagIndex);
 | 
| +    GotoIfNot(SmiEqual(tag, SmiConstant(JSRegExp::IRREGEXP)), &runtime);
 | 
| +
 | 
| +    // Check (number_of_captures + 1) * 2 <= offsets vector size
 | 
| +    // Or              number_of_captures <= offsets vector size / 2 - 1
 | 
| +    Node* const capture_count =
 | 
| +        LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex);
 | 
| +    CSA_ASSERT(this, TaggedIsSmi(capture_count));
 | 
| +
 | 
| +    STATIC_ASSERT(Isolate::kJSRegexpStaticOffsetsVectorSize >= 2);
 | 
| +    GotoIf(SmiAbove(
 | 
| +               capture_count,
 | 
| +               SmiConstant(Isolate::kJSRegexpStaticOffsetsVectorSize / 2 - 1)),
 | 
| +           &runtime);
 | 
| +  }
 | 
| +
 | 
| +  // Unpack the string if possible.
 | 
| +
 | 
| +  var_string.Bind(BitcastTaggedToWord(string));
 | 
| +  var_string_offset.Bind(int_zero);
 | 
| +  var_string_instance_type.Bind(LoadInstanceType(string));
 | 
| +
 | 
| +  {
 | 
| +    TryUnpackString(&var_string, &var_string_offset, &var_string_instance_type,
 | 
| +                    &runtime);
 | 
| +
 | 
| +    // At this point, {var_string} may contain a faked sequential string (i.e.
 | 
| +    // an external string with an adjusted offset) so we cannot assert
 | 
| +    // IsString({var_string}). We also cannot allocate after this point since
 | 
| +    // GC could move {var_string}'s underlying string.
 | 
| +  }
 | 
| +
 | 
| +  Node* const smi_string_length = LoadStringLength(string);
 | 
| +
 | 
| +  // Bail out to runtime for invalid {last_index} values.
 | 
| +  GotoIfNot(TaggedIsSmi(last_index), &runtime);
 | 
| +  GotoIf(SmiAboveOrEqual(last_index, smi_string_length), &runtime);
 | 
| +
 | 
| +  // Load the irregexp code object and offsets into the subject string. Both
 | 
| +  // depend on whether the string is one- or two-byte.
 | 
| +
 | 
| +  Node* const int_last_index = SmiUntag(last_index);
 | 
| +
 | 
| +  Variable var_string_start(this, MachineType::PointerRepresentation());
 | 
| +  Variable var_string_end(this, MachineType::PointerRepresentation());
 | 
| +  Variable var_code(this, MachineRepresentation::kTagged);
 | 
| +
 | 
| +  {
 | 
| +    Node* const int_string_length = SmiUntag(smi_string_length);
 | 
| +
 | 
| +    Node* const string_instance_type = var_string_instance_type.value();
 | 
| +    CSA_ASSERT(this, IsSequentialStringInstanceType(string_instance_type));
 | 
| +
 | 
| +    Label next(this), if_isonebyte(this), if_istwobyte(this, Label::kDeferred);
 | 
| +    Branch(IsOneByteStringInstanceType(string_instance_type), &if_isonebyte,
 | 
| +           &if_istwobyte);
 | 
| +
 | 
| +    Bind(&if_isonebyte);
 | 
| +    {
 | 
| +      const bool kIsOneByte = true;
 | 
| +      GetStringPointers(var_string.value(), var_string_offset.value(),
 | 
| +                        int_last_index, int_string_length, kIsOneByte,
 | 
| +                        &var_string_start, &var_string_end);
 | 
| +      var_code.Bind(
 | 
| +          LoadFixedArrayElement(data, JSRegExp::kIrregexpLatin1CodeIndex));
 | 
| +      Goto(&next);
 | 
| +    }
 | 
| +
 | 
| +    Bind(&if_istwobyte);
 | 
| +    {
 | 
| +      const bool kIsOneByte = false;
 | 
| +      GetStringPointers(var_string.value(), var_string_offset.value(),
 | 
| +                        int_last_index, int_string_length, kIsOneByte,
 | 
| +                        &var_string_start, &var_string_end);
 | 
| +      var_code.Bind(
 | 
| +          LoadFixedArrayElement(data, JSRegExp::kIrregexpUC16CodeIndex));
 | 
| +      Goto(&next);
 | 
| +    }
 | 
| +
 | 
| +    Bind(&next);
 | 
| +  }
 | 
| +
 | 
| +  // Check that the irregexp code has been generated for the actual string
 | 
| +  // encoding. If it has, the field contains a code object otherwise it contains
 | 
| +  // smi (code flushing support).
 | 
| +
 | 
| +  Node* const code = var_code.value();
 | 
| +  GotoIf(TaggedIsSmi(code), &runtime);
 | 
| +  CSA_ASSERT(this, HasInstanceType(code, CODE_TYPE));
 | 
| +
 | 
| +  Label if_success(this), if_failure(this),
 | 
| +      if_exception(this, Label::kDeferred);
 | 
| +  {
 | 
| +    IncrementCounter(isolate()->counters()->regexp_entry_native(), 1);
 | 
| +
 | 
| +    Callable exec_callable = CodeFactory::RegExpExec(isolate());
 | 
| +    Node* const result = CallStub(
 | 
| +        exec_callable, context, string, TruncateWordToWord32(int_last_index),
 | 
| +        var_string_start.value(), var_string_end.value(), code);
 | 
| +
 | 
| +    // Check the result.
 | 
| +    // We expect exactly one result since the stub forces the called regexp to
 | 
| +    // behave as non-global.
 | 
| +    GotoIf(SmiEqual(result, SmiConstant(1)), &if_success);
 | 
| +    GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::FAILURE)),
 | 
| +           &if_failure);
 | 
| +    GotoIf(SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::EXCEPTION)),
 | 
| +           &if_exception);
 | 
| +
 | 
| +    CSA_ASSERT(
 | 
| +        this, SmiEqual(result, SmiConstant(NativeRegExpMacroAssembler::RETRY)));
 | 
| +    Goto(&runtime);
 | 
| +  }
 | 
| +
 | 
| +  Bind(&if_success);
 | 
| +  {
 | 
| +    // Check that the last match info has space for the capture registers and
 | 
| +    // the additional information. Ensure no overflow in add.
 | 
| +    STATIC_ASSERT(FixedArray::kMaxLength < kMaxInt - FixedArray::kLengthOffset);
 | 
| +    Node* const available_slots =
 | 
| +        SmiSub(LoadFixedArrayBaseLength(match_info),
 | 
| +               SmiConstant(RegExpMatchInfo::kLastMatchOverhead));
 | 
| +    Node* const capture_count =
 | 
| +        LoadFixedArrayElement(data, JSRegExp::kIrregexpCaptureCountIndex);
 | 
| +    // Calculate number of register_count = (capture_count + 1) * 2.
 | 
| +    Node* const register_count =
 | 
| +        SmiShl(SmiAdd(capture_count, SmiConstant(1)), 1);
 | 
| +    GotoIf(SmiGreaterThan(register_count, available_slots), &runtime);
 | 
| +
 | 
| +    // Fill match_info.
 | 
| +
 | 
| +    StoreFixedArrayElement(match_info, RegExpMatchInfo::kNumberOfCapturesIndex,
 | 
| +                           register_count, SKIP_WRITE_BARRIER);
 | 
| +    StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastSubjectIndex,
 | 
| +                           string);
 | 
| +    StoreFixedArrayElement(match_info, RegExpMatchInfo::kLastInputIndex,
 | 
| +                           string);
 | 
| +
 | 
| +    // Fill match and capture offsets in match_info.
 | 
| +    {
 | 
| +      Node* const limit_offset = ElementOffsetFromIndex(
 | 
| +          register_count, INT32_ELEMENTS, SMI_PARAMETERS, 0);
 | 
| +
 | 
| +      Node* const to_offset = ElementOffsetFromIndex(
 | 
| +          IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), FAST_ELEMENTS,
 | 
| +          INTPTR_PARAMETERS, RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
 | 
| +      Variable var_to_offset(this, MachineType::PointerRepresentation(),
 | 
| +                             to_offset);
 | 
| +
 | 
| +      VariableList vars({&var_to_offset}, zone());
 | 
| +      BuildFastLoop(
 | 
| +          vars, int_zero, limit_offset,
 | 
| +          [=, &var_to_offset](Node* offset) {
 | 
| +            Node* const value = Load(MachineType::Int32(),
 | 
| +                                     static_offsets_vector_address, offset);
 | 
| +            Node* const smi_value = SmiFromWord32(value);
 | 
| +            StoreNoWriteBarrier(MachineRepresentation::kTagged, match_info,
 | 
| +                                var_to_offset.value(), smi_value);
 | 
| +            Increment(var_to_offset, kPointerSize);
 | 
| +          },
 | 
| +          kInt32Size, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
 | 
| +    }
 | 
| +
 | 
| +    var_result.Bind(match_info);
 | 
| +    Goto(&out);
 | 
| +  }
 | 
| +
 | 
| +  Bind(&if_failure);
 | 
| +  {
 | 
| +    var_result.Bind(NullConstant());
 | 
| +    Goto(&out);
 | 
| +  }
 | 
| +
 | 
| +  Bind(&if_exception);
 | 
| +  {
 | 
| +    Node* const pending_exception =
 | 
| +        Load(MachineType::AnyTagged(), pending_exception_address);
 | 
| +
 | 
| +    // If there is no pending exception, a
 | 
| +    // stack overflow (on the backtrack stack) was detected in RegExp code.
 | 
| +
 | 
| +    Label stack_overflow(this), rethrow(this);
 | 
| +    Branch(IsTheHole(pending_exception), &stack_overflow, &rethrow);
 | 
| +
 | 
| +    Bind(&stack_overflow);
 | 
| +    TailCallRuntime(Runtime::kThrowStackOverflow, context);
 | 
| +
 | 
| +    Bind(&rethrow);
 | 
| +    TailCallRuntime(Runtime::kRegExpExecReThrow, context);
 | 
| +  }
 | 
| +
 | 
| +  Bind(&runtime);
 | 
| +  {
 | 
| +    Node* const result = CallRuntime(Runtime::kRegExpExec, context, regexp,
 | 
| +                                     string, last_index, match_info);
 | 
| +    var_result.Bind(result);
 | 
| +    Goto(&out);
 | 
| +  }
 | 
| +
 | 
| +  Bind(&out);
 | 
| +  return var_result.value();
 | 
| +#endif  // V8_INTERPRETED_REGEXP
 | 
| +}
 | 
| +
 | 
|  // ES#sec-regexp.prototype.exec
 | 
|  // RegExp.prototype.exec ( string )
 | 
|  // Implements the core of RegExp.prototype.exec but without actually
 | 
| @@ -311,9 +590,8 @@ Node* RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
 | 
|          native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
 | 
|  
 | 
|      // Call the exec stub.
 | 
| -    Callable exec_callable = CodeFactory::RegExpExec(isolate);
 | 
| -    match_indices = CallStub(exec_callable, context, regexp, string,
 | 
| -                             var_lastindex.value(), last_match_info);
 | 
| +    match_indices = IrregexpExec(context, regexp, string, var_lastindex.value(),
 | 
| +                                 last_match_info);
 | 
|      var_result.Bind(match_indices);
 | 
|  
 | 
|      // {match_indices} is either null or the RegExpMatchInfo array.
 | 
| @@ -1810,8 +2088,6 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
 | 
|                                                         Node* const regexp,
 | 
|                                                         Node* const string,
 | 
|                                                         Node* const limit) {
 | 
| -  Isolate* isolate = this->isolate();
 | 
| -
 | 
|    Node* const null = NullConstant();
 | 
|    Node* const smi_zero = SmiConstant(0);
 | 
|    Node* const int_zero = IntPtrConstant(0);
 | 
| @@ -1846,9 +2122,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
 | 
|        Node* const last_match_info = LoadContextElement(
 | 
|            native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
 | 
|  
 | 
| -      Callable exec_callable = CodeFactory::RegExpExec(isolate);
 | 
| -      Node* const match_indices = CallStub(exec_callable, context, regexp,
 | 
| -                                           string, smi_zero, last_match_info);
 | 
| +      Node* const match_indices =
 | 
| +          IrregexpExec(context, regexp, string, smi_zero, last_match_info);
 | 
|  
 | 
|        Label return_singleton_array(this);
 | 
|        Branch(WordEqual(match_indices, null), &return_singleton_array,
 | 
| @@ -1906,9 +2181,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
 | 
|      Node* const last_match_info = LoadContextElement(
 | 
|          native_context, Context::REGEXP_LAST_MATCH_INFO_INDEX);
 | 
|  
 | 
| -    Callable exec_callable = CodeFactory::RegExpExec(isolate);
 | 
| -    Node* const match_indices = CallStub(exec_callable, context, regexp, string,
 | 
| -                                         next_search_from, last_match_info);
 | 
| +    Node* const match_indices = IrregexpExec(context, regexp, string,
 | 
| +                                             next_search_from, last_match_info);
 | 
|  
 | 
|      // We're done if no match was found.
 | 
|      {
 | 
| @@ -2555,9 +2829,8 @@ TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) {
 | 
|    Node* const internal_match_info = LoadContextElement(
 | 
|        native_context, Context::REGEXP_INTERNAL_MATCH_INFO_INDEX);
 | 
|  
 | 
| -  Callable exec_callable = CodeFactory::RegExpExec(isolate());
 | 
| -  Node* const match_indices = CallStub(exec_callable, context, regexp, string,
 | 
| -                                       smi_zero, internal_match_info);
 | 
| +  Node* const match_indices =
 | 
| +      IrregexpExec(context, regexp, string, smi_zero, internal_match_info);
 | 
|  
 | 
|    Label if_matched(this), if_didnotmatch(this);
 | 
|    Branch(WordEqual(match_indices, null), &if_didnotmatch, &if_matched);
 | 
| 
 |