Commit ee0581c3 authored by Z Nguyen-Huu's avatar Z Nguyen-Huu Committed by Commit Bot

[builtins] Port RegExp Split to Torque

Bug: v8:8976
Change-Id: I1ffc6637e26ee217750d099d758fd67ed2130131
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1796316
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63733}
parent 7b4f4b2e
...@@ -998,6 +998,7 @@ torque_files = [ ...@@ -998,6 +998,7 @@ torque_files = [
"src/builtins/regexp-replace.tq", "src/builtins/regexp-replace.tq",
"src/builtins/regexp-search.tq", "src/builtins/regexp-search.tq",
"src/builtins/regexp-source.tq", "src/builtins/regexp-source.tq",
"src/builtins/regexp-split.tq",
"src/builtins/regexp-test.tq", "src/builtins/regexp-test.tq",
"src/builtins/regexp.tq", "src/builtins/regexp.tq",
"src/builtins/string.tq", "src/builtins/string.tq",
......
...@@ -854,14 +854,11 @@ namespace internal { ...@@ -854,14 +854,11 @@ namespace internal {
CPP(RegExpPrototypeToString) \ CPP(RegExpPrototypeToString) \
CPP(RegExpRightContextGetter) \ CPP(RegExpRightContextGetter) \
\ \
/* ES #sec-regexp.prototype-@@split */ \
TFJ(RegExpPrototypeSplit, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* RegExp helpers */ \ /* RegExp helpers */ \
TFS(RegExpExecAtom, kRegExp, kString, kLastIndex, kMatchInfo) \ TFS(RegExpExecAtom, kRegExp, kString, kLastIndex, kMatchInfo) \
TFS(RegExpExecInternal, kRegExp, kString, kLastIndex, kMatchInfo) \ TFS(RegExpExecInternal, kRegExp, kString, kLastIndex, kMatchInfo) \
ASM(RegExpInterpreterTrampoline, CCall) \ ASM(RegExpInterpreterTrampoline, CCall) \
TFS(RegExpPrototypeExecSlow, kReceiver, kString) \ TFS(RegExpPrototypeExecSlow, kReceiver, kString) \
TFS(RegExpSplit, kRegExp, kString, kLimit) \
\ \
/* RegExp String Iterator */ \ /* RegExp String Iterator */ \
/* https://tc39.github.io/proposal-string-matchall/ */ \ /* https://tc39.github.io/proposal-string-matchall/ */ \
......
...@@ -2042,10 +2042,9 @@ TF_BUILTIN(RegExpPrototypeMatchAll, RegExpMatchAllAssembler) { ...@@ -2042,10 +2042,9 @@ TF_BUILTIN(RegExpPrototypeMatchAll, RegExpMatchAllAssembler) {
// Generates the fast path for @@split. {regexp} is an unmodified, non-sticky // Generates the fast path for @@split. {regexp} is an unmodified, non-sticky
// JSRegExp, {string} is a String, and {limit} is a Smi. // JSRegExp, {string} is a String, and {limit} is a Smi.
void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context, TNode<JSArray> RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
TNode<JSRegExp> regexp, TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
TNode<String> string, TNode<Smi> const limit) {
TNode<Smi> const limit) {
CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp)); CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp));
CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky))); CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky)));
...@@ -2059,6 +2058,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context, ...@@ -2059,6 +2058,8 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context); TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
Label return_empty_array(this, Label::kDeferred); Label return_empty_array(this, Label::kDeferred);
TVARIABLE(JSArray, var_result);
Label done(this);
// If limit is zero, return an empty array. // If limit is zero, return an empty array.
{ {
...@@ -2092,13 +2093,13 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context, ...@@ -2092,13 +2093,13 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
{ {
TNode<Smi> length = SmiConstant(1); TNode<Smi> length = SmiConstant(1);
TNode<IntPtrT> capacity = IntPtrConstant(1); TNode<IntPtrT> capacity = IntPtrConstant(1);
TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, var_result = AllocateJSArray(kind, array_map, capacity, length,
length, allocation_site, mode); allocation_site, mode);
TNode<FixedArray> fixed_array = CAST(LoadElements(result)); TNode<FixedArray> fixed_array = CAST(LoadElements(var_result.value()));
UnsafeStoreFixedArrayElement(fixed_array, 0, string); UnsafeStoreFixedArrayElement(fixed_array, 0, string);
Return(result); Goto(&done);
} }
} }
...@@ -2262,110 +2263,21 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context, ...@@ -2262,110 +2263,21 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
BIND(&out); BIND(&out);
{ {
TNode<JSArray> const result = array.ToJSArray(context); var_result = array.ToJSArray(context);
Return(result); Goto(&done);
} }
BIND(&return_empty_array); BIND(&return_empty_array);
{ {
TNode<Smi> length = SmiZero(); TNode<Smi> length = SmiZero();
TNode<IntPtrT> capacity = IntPtrZero(); TNode<IntPtrT> capacity = IntPtrZero();
TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length, var_result = AllocateJSArray(kind, array_map, capacity, length,
allocation_site, mode); allocation_site, mode);
Return(result); Goto(&done);
}
}
// Helper that skips a few initial checks.
TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
TNode<String> string = CAST(Parameter(Descriptor::kString));
TNode<Object> maybe_limit = CAST(Parameter(Descriptor::kLimit));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) {
BranchIfFastRegExp_Strict(context, regexp, ok, not_ok);
});
// Verify {maybe_limit}.
VARIABLE(var_limit, MachineRepresentation::kTagged, maybe_limit);
Label if_limitissmimax(this), runtime(this, Label::kDeferred);
{
Label next(this);
GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
Branch(TaggedIsPositiveSmi(maybe_limit), &next, &runtime);
// We need to be extra-strict and require the given limit to be either
// undefined or a positive smi. We can't call ToUint32(maybe_limit) since
// that might move us onto the slow path, resulting in ordering spec
// violations (see https://crbug.com/801171).
BIND(&if_limitissmimax);
{
// TODO(jgruber): In this case, we can probably avoid generation of limit
// checks in Generate_RegExpPrototypeSplitBody.
var_limit.Bind(SmiConstant(Smi::kMaxValue));
Goto(&next);
}
BIND(&next);
} }
// Due to specific shortcuts we take on the fast path (specifically, we don't BIND(&done);
// allocate a new regexp instance as specced), we need to ensure that the return var_result.value();
// given regexp is non-sticky to avoid invalid results. See crbug.com/v8/6706.
GotoIf(FastFlagGetter(regexp, JSRegExp::kSticky), &runtime);
// We're good to go on the fast path, which is inlined here.
RegExpPrototypeSplitBody(context, regexp, string, CAST(var_limit.value()));
BIND(&runtime);
Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string,
var_limit.value()));
}
// ES#sec-regexp.prototype-@@split
// RegExp.prototype [ @@split ] ( string, limit )
TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
const int kStringArg = 0;
const int kLimitArg = 1;
TNode<IntPtrT> argc =
ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
CodeStubArguments args(this, argc);
TNode<Object> maybe_receiver = args.GetReceiver();
TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
TNode<Object> maybe_limit = args.GetOptionalArgumentValue(kLimitArg);
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
// Ensure {maybe_receiver} is a JSReceiver.
ThrowIfNotJSReceiver(context, maybe_receiver,
MessageTemplate::kIncompatibleMethodReceiver,
"RegExp.prototype.@@split");
TNode<JSReceiver> receiver = CAST(maybe_receiver);
// Convert {maybe_string} to a String.
TNode<String> string = ToString_Inline(context, maybe_string);
// Strict: Reads the flags property.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
Label stub(this), runtime(this, Label::kDeferred);
BranchIfFastRegExp_Strict(context, receiver, &stub, &runtime);
BIND(&stub);
args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, receiver,
string, maybe_limit));
BIND(&runtime);
args.PopAndReturn(CallRuntime(Runtime::kRegExpSplit, context, receiver,
string, maybe_limit));
} }
class RegExpStringIteratorAssembler : public RegExpBuiltinsAssembler { class RegExpStringIteratorAssembler : public RegExpBuiltinsAssembler {
......
...@@ -187,9 +187,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler { ...@@ -187,9 +187,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
TNode<String> const string, TNode<String> const string,
const bool is_fastpath); const bool is_fastpath);
void RegExpPrototypeSplitBody(TNode<Context> context, TNode<JSRegExp> regexp, TNode<JSArray> RegExpPrototypeSplitBody(TNode<Context> context,
TNode<String> const string, TNode<JSRegExp> regexp,
TNode<Smi> const limit); TNode<String> const string,
TNode<Smi> const limit);
}; };
class RegExpMatchAllAssembler : public RegExpBuiltinsAssembler { class RegExpMatchAllAssembler : public RegExpBuiltinsAssembler {
......
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include 'src/builtins/builtins-regexp-gen.h'
namespace runtime {
extern transitioning runtime
RegExpSplit(implicit context: Context)(JSReceiver, String, Object): JSAny;
} // namespace runtime
namespace regexp {
const kMaxValueSmi: constexpr int31
generates 'Smi::kMaxValue';
extern transitioning macro RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
implicit context: Context)(JSRegExp, String, Smi): JSArray;
// Helper that skips a few initial checks.
transitioning builtin
RegExpSplit(implicit context: Context)(
regexp: FastJSRegExp, string: String, limit: JSAny): JSAny {
let sanitizedLimit: Smi;
// We need to be extra-strict and require the given limit to be either
// undefined or a positive smi. We can't call ToUint32(maybe_limit) since
// that might move us onto the slow path, resulting in ordering spec
// violations (see https://crbug.com/801171).
if (limit == Undefined) {
// TODO(jgruber): In this case, we can probably avoid generation of limit
// checks in Generate_RegExpPrototypeSplitBody.
sanitizedLimit = SmiConstant(kMaxValueSmi);
} else if (!TaggedIsPositiveSmi(limit)) {
return runtime::RegExpSplit(regexp, string, limit);
} else {
sanitizedLimit = UnsafeCast<Smi>(limit);
}
// Due to specific shortcuts we take on the fast path (specifically, we
// don't allocate a new regexp instance as specced), we need to ensure that
// the given regexp is non-sticky to avoid invalid results. See
// crbug.com/v8/6706.
if (FastFlagGetter(regexp, kSticky)) {
return runtime::RegExpSplit(regexp, string, sanitizedLimit);
}
// We're good to go on the fast path, which is inlined here.
return RegExpPrototypeSplitBody(regexp, string, sanitizedLimit);
}
// ES#sec-regexp.prototype-@@split
// RegExp.prototype [ @@split ] ( string, limit )
transitioning javascript builtin RegExpPrototypeSplit(
js-implicit context: Context, receiver: JSAny)(...arguments): JSAny {
ThrowIfNotJSReceiver(
receiver, kIncompatibleMethodReceiver, 'RegExp.prototype.@@split');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(context, arguments[0]);
const limit = arguments[1];
// Strict: Reads the flags property.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
const fastRegExp = Cast<FastJSRegExp>(receiver)
otherwise return runtime::RegExpSplit(receiver, string, limit);
return RegExpSplit(fastRegExp, string, limit);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment