Commit 082bf306 authored by jgruber's avatar jgruber Committed by Commit bot

[stubs] Add RegExpReplace and RegExpSplit stubs

This adds helper stubs for RegExp split and replace operations, called directly
by both RegExpPrototype{Replace,Split} and StringPrototype{Replace,Split}.

BUG=

Review-Url: https://codereview.chromium.org/2668703002
Cr-Commit-Position: refs/heads/master@{#42919}
parent 87e53cb8
......@@ -422,9 +422,10 @@ Node* RegExpBuiltinsAssembler::IsInitialRegExpMap(Node* context, Node* map) {
// We use a fairly coarse granularity for this and simply check whether both
// the regexp itself is unmodified (i.e. its map has not changed) and its
// prototype is unmodified.
void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map,
Label* if_isunmodified,
Label* if_ismodified) {
void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* const context,
Node* const map,
Label* const if_isunmodified,
Label* const if_ismodified) {
Node* const native_context = LoadNativeContext(context);
Node* const regexp_fun =
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
......@@ -446,6 +447,25 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(Node* context, Node* map,
Branch(proto_has_initialmap, if_isunmodified, if_ismodified);
}
Node* RegExpBuiltinsAssembler::IsFastRegExpMap(Node* const context,
Node* const map) {
Label yup(this), nope(this), out(this);
Variable var_result(this, MachineRepresentation::kWord32);
BranchIfFastRegExp(context, map, &yup, &nope);
Bind(&yup);
var_result.Bind(Int32Constant(1));
Goto(&out);
Bind(&nope);
var_result.Bind(Int32Constant(0));
Goto(&out);
Bind(&out);
return var_result.value();
}
void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* context, Node* map,
Label* if_isunmodified,
Label* if_ismodified) {
......@@ -2029,6 +2049,51 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
}
}
// Helper that skips a few initial checks.
TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
typedef RegExpSplitDescriptor Descriptor;
Node* const regexp = Parameter(Descriptor::kReceiver);
Node* const string = Parameter(Descriptor::kString);
Node* const maybe_limit = Parameter(Descriptor::kLimit);
Node* const context = Parameter(Descriptor::kContext);
CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
CSA_ASSERT(this, IsString(string));
// TODO(jgruber): Even if map checks send us to the fast path, we still need
// to verify the constructor property and jump to the slow path if it has
// been changed.
// Convert {maybe_limit} to a uint32, capping at the maximal smi value.
Variable var_limit(this, MachineRepresentation::kTagged);
Label if_limitissmimax(this), limit_done(this);
GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
{
Node* const limit = ToUint32(context, maybe_limit);
GotoUnless(TaggedIsSmi(limit), &if_limitissmimax);
var_limit.Bind(limit);
Goto(&limit_done);
}
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(&limit_done);
}
Bind(&limit_done);
{
Node* const limit = var_limit.value();
RegExpPrototypeSplitBody(context, regexp, string, limit);
}
}
// ES#sec-regexp.prototype-@@split
// RegExp.prototype [ @@split ] ( string, limit )
TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
......@@ -2046,51 +2111,16 @@ TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
// Convert {maybe_string} to a String.
Node* const string = ToString(context, maybe_string);
Label fast_path(this), slow_path(this);
BranchIfFastRegExp(context, map, &fast_path, &slow_path);
Bind(&fast_path);
{
// TODO(jgruber): Even if map checks send us to the fast path, we still need
// to verify the constructor property and jump to the slow path if it has
// been changed.
// Convert {maybe_limit} to a uint32, capping at the maximal smi value.
Variable var_limit(this, MachineRepresentation::kTagged);
Label if_limitissmimax(this), limit_done(this);
GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
{
Node* const limit = ToUint32(context, maybe_limit);
GotoUnless(TaggedIsSmi(limit), &if_limitissmimax);
var_limit.Bind(limit);
Goto(&limit_done);
}
Bind(&if_limitissmimax);
{
// TODO(jgruber): In this case, we can probably generation of limit checks
// in Generate_RegExpPrototypeSplitBody.
Node* const smi_max = SmiConstant(Smi::kMaxValue);
var_limit.Bind(smi_max);
Goto(&limit_done);
}
Label stub(this), runtime(this, Label::kDeferred);
BranchIfFastRegExp(context, map, &stub, &runtime);
Bind(&limit_done);
{
Node* const limit = var_limit.value();
RegExpPrototypeSplitBody(context, receiver, string, limit);
}
}
Bind(&stub);
Callable split_callable = CodeFactory::RegExpSplit(isolate());
Return(CallStub(split_callable, context, receiver, string, maybe_limit));
Bind(&slow_path);
{
Node* const result = CallRuntime(Runtime::kRegExpSplit, context, receiver,
string, maybe_limit);
Return(result);
}
Bind(&runtime);
Return(CallRuntime(Runtime::kRegExpSplit, context, receiver, string,
maybe_limit));
}
Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
......@@ -2406,50 +2436,37 @@ Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
return var_result.value();
}
// ES#sec-regexp.prototype-@@replace
// RegExp.prototype [ @@replace ] ( string, replaceValue )
TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
Node* const maybe_receiver = Parameter(0);
Node* const maybe_string = Parameter(1);
Node* const replace_value = Parameter(2);
Node* const context = Parameter(5);
// Ensure {maybe_receiver} is a JSReceiver.
Node* const map = ThrowIfNotJSReceiver(
context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
"RegExp.prototype.@@replace");
Node* const receiver = maybe_receiver;
// Helper that skips a few initial checks.
TF_BUILTIN(RegExpReplace, RegExpBuiltinsAssembler) {
typedef RegExpReplaceDescriptor Descriptor;
// Convert {maybe_string} to a String.
Callable tostring_callable = CodeFactory::ToString(isolate());
Node* const string = CallStub(tostring_callable, context, maybe_string);
Node* const regexp = Parameter(Descriptor::kReceiver);
Node* const string = Parameter(Descriptor::kString);
Node* const replace_value = Parameter(Descriptor::kReplaceValue);
Node* const context = Parameter(Descriptor::kContext);
// Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
Label checkreplacecallable(this), runtime(this, Label::kDeferred),
fastpath(this);
BranchIfFastRegExp(context, map, &checkreplacecallable, &runtime);
CSA_ASSERT(this, IsFastRegExpMap(context, LoadMap(regexp)));
CSA_ASSERT(this, IsString(string));
Bind(&checkreplacecallable);
Node* const regexp = receiver;
Label checkreplacestring(this), if_iscallable(this),
runtime(this, Label::kDeferred);
// 2. Is {replace_value} callable?
Label checkreplacestring(this), if_iscallable(this);
GotoIf(TaggedIsSmi(replace_value), &checkreplacestring);
Node* const replace_value_map = LoadMap(replace_value);
Branch(IsCallableMap(replace_value_map), &if_iscallable, &checkreplacestring);
Branch(IsCallableMap(LoadMap(replace_value)), &if_iscallable,
&checkreplacestring);
// 3. Does ToString({replace_value}) contain '$'?
Bind(&checkreplacestring);
{
Callable tostring_callable = CodeFactory::ToString(isolate());
Node* const replace_string =
CallStub(tostring_callable, context, replace_value);
Node* const dollar_char = Int32Constant('$');
Node* const smi_minusone = SmiConstant(Smi::FromInt(-1));
GotoUnless(SmiEqual(StringIndexOfChar(context, replace_string, dollar_char,
SmiConstant(0)),
smi_minusone),
SmiConstant(-1)),
&runtime);
Return(
......@@ -2459,35 +2476,56 @@ TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
// {regexp} is unmodified and {replace_value} is callable.
Bind(&if_iscallable);
{
Node* const replace_callable = replace_value;
Node* const replace_fn = replace_value;
// Check if the {regexp} is global.
Label if_isglobal(this), if_isnotglobal(this);
Node* const is_global = FastFlagGetter(regexp, JSRegExp::kGlobal);
Branch(is_global, &if_isglobal, &if_isnotglobal);
Bind(&if_isglobal);
{
Node* const result = ReplaceGlobalCallableFastPath(
context, regexp, string, replace_callable);
Return(result);
}
Return(ReplaceGlobalCallableFastPath(context, regexp, string, replace_fn));
Bind(&if_isnotglobal);
{
Node* const result =
CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
context, string, regexp, replace_callable);
Return(result);
}
Return(CallRuntime(Runtime::kStringReplaceNonGlobalRegExpWithFunction,
context, string, regexp, replace_fn));
}
Bind(&runtime);
{
Node* const result = CallRuntime(Runtime::kRegExpReplace, context, receiver,
string, replace_value);
Return(result);
}
Return(CallRuntime(Runtime::kRegExpReplace, context, regexp, string,
replace_value));
}
// ES#sec-regexp.prototype-@@replace
// RegExp.prototype [ @@replace ] ( string, replaceValue )
TF_BUILTIN(RegExpPrototypeReplace, RegExpBuiltinsAssembler) {
Node* const maybe_receiver = Parameter(0);
Node* const maybe_string = Parameter(1);
Node* const replace_value = Parameter(2);
Node* const context = Parameter(5);
// Ensure {maybe_receiver} is a JSReceiver.
Node* const map = ThrowIfNotJSReceiver(
context, maybe_receiver, MessageTemplate::kIncompatibleMethodReceiver,
"RegExp.prototype.@@replace");
Node* const receiver = maybe_receiver;
// Convert {maybe_string} to a String.
Callable tostring_callable = CodeFactory::ToString(isolate());
Node* const string = CallStub(tostring_callable, context, maybe_string);
// Fast-path checks: 1. Is the {receiver} an unmodified JSRegExp instance?
Label stub(this), runtime(this, Label::kDeferred);
BranchIfFastRegExp(context, map, &stub, &runtime);
Bind(&stub);
Callable replace_callable = CodeFactory::RegExpReplace(isolate());
Return(CallStub(replace_callable, context, receiver, string, replace_value));
Bind(&runtime);
Return(CallRuntime(Runtime::kRegExpReplace, context, receiver, string,
replace_value));
}
// Simple string matching functionality for internal use which does not modify
......
......@@ -19,8 +19,9 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
explicit RegExpBuiltinsAssembler(CodeAssemblerState* state)
: CodeStubAssembler(state) {}
void BranchIfFastRegExp(Node* context, Node* map, Label* if_isunmodified,
Label* if_ismodified);
void BranchIfFastRegExp(Node* const context, Node* const map,
Label* const if_isunmodified,
Label* const if_ismodified);
protected:
Node* FastLoadLastIndex(Node* regexp);
......@@ -48,6 +49,9 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
MessageTemplate::Template msg_template,
char const* method_name);
// Analogous to BranchIfFastRegExp, for use in asserts.
Node* IsFastRegExpMap(Node* const context, Node* const map);
Node* IsInitialRegExpMap(Node* context, Node* map);
void BranchIfFastRegExpResult(Node* context, Node* map,
Label* if_isunmodified, Label* if_ismodified);
......
......@@ -1115,7 +1115,7 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
}
// Take the fast path for RegExps.
if (regexp_call != nullptr) {
{
Label stub_call(this), slow_lookup(this);
RegExpBuiltinsAssembler regexp_asm(state());
......@@ -1160,10 +1160,18 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
RequireObjectCoercible(context, receiver, "String.prototype.replace");
// Redirect to replacer method if {search[@@replace]} is not undefined.
// TODO(jgruber): Call RegExp.p.replace stub for fast path.
MaybeCallFunctionAtSymbol(
context, search, isolate()->factory()->replace_symbol(), nullptr,
context, search, isolate()->factory()->replace_symbol(),
[=]() {
Callable tostring_callable = CodeFactory::ToString(isolate());
Node* const subject_string =
CallStub(tostring_callable, context, receiver);
Callable replace_callable = CodeFactory::RegExpReplace(isolate());
return CallStub(replace_callable, context, search, subject_string,
replace);
},
[=](Node* fn) {
Callable call_callable = CodeFactory::Call(isolate());
return CallJS(call_callable, context, fn, search, receiver, replace);
......@@ -1317,10 +1325,18 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
RequireObjectCoercible(context, receiver, "String.prototype.split");
// Redirect to splitter method if {separator[@@split]} is not undefined.
// TODO(jgruber): Call RegExp.p.split stub for fast path.
MaybeCallFunctionAtSymbol(
context, separator, isolate()->factory()->split_symbol(), nullptr,
context, separator, isolate()->factory()->split_symbol(),
[=]() {
Callable tostring_callable = CodeFactory::ToString(isolate());
Node* const subject_string =
CallStub(tostring_callable, context, receiver);
Callable split_callable = CodeFactory::RegExpSplit(isolate());
return CallStub(split_callable, context, separator, subject_string,
limit);
},
[=](Node* fn) {
Callable call_callable = CodeFactory::Call(isolate());
return CallJS(call_callable, context, fn, separator, receiver, limit);
......
......@@ -707,16 +707,20 @@ class Isolate;
TFJ(RegExpPrototypeIgnoreCaseGetter, 0) \
TFJ(RegExpPrototypeMatch, 1) \
TFJ(RegExpPrototypeMultilineGetter, 0) \
TFJ(RegExpPrototypeReplace, 2) \
TFJ(RegExpPrototypeSearch, 1) \
TFJ(RegExpPrototypeSourceGetter, 0) \
TFJ(RegExpPrototypeSplit, 2) \
TFJ(RegExpPrototypeStickyGetter, 0) \
TFJ(RegExpPrototypeTest, 1) \
CPP(RegExpPrototypeToString) \
TFJ(RegExpPrototypeUnicodeGetter, 0) \
CPP(RegExpRightContextGetter) \
\
TFS(RegExpReplace, BUILTIN, kNoExtraICState, RegExpReplace) \
TFJ(RegExpPrototypeReplace, 2) \
\
TFS(RegExpSplit, BUILTIN, kNoExtraICState, RegExpSplit) \
TFJ(RegExpPrototypeSplit, 2) \
\
/* SharedArrayBuffer */ \
CPP(SharedArrayBufferPrototypeGetByteLength) \
TFJ(AtomicsLoad, 2) \
......
......@@ -251,6 +251,8 @@ TFS_BUILTIN(ForInFilter)
TFS_BUILTIN(GetSuperConstructor)
TFS_BUILTIN(KeyedLoadIC_Megamorphic)
TFS_BUILTIN(PromiseHandleReject)
TFS_BUILTIN(RegExpReplace)
TFS_BUILTIN(RegExpSplit)
TFS_BUILTIN(StringCharAt)
TFS_BUILTIN(StringCharCodeAt)
TFS_BUILTIN(StringEqual)
......
......@@ -132,6 +132,9 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable SubString(Isolate* isolate);
static Callable StringIndexOf(Isolate* isolate);
static Callable RegExpReplace(Isolate* isolate);
static Callable RegExpSplit(Isolate* isolate);
static Callable ClassOf(Isolate* isolate);
static Callable Typeof(Isolate* isolate);
static Callable GetSuperConstructor(Isolate* isolate);
......
......@@ -52,6 +52,8 @@ class PlatformInterfaceDescriptor;
V(ConstructStub) \
V(ConstructTrampoline) \
V(RegExpExec) \
V(RegExpReplace) \
V(RegExpSplit) \
V(CopyFastSmiOrObjectElements) \
V(TransitionElementsKind) \
V(AllocateHeapNumber) \
......@@ -619,6 +621,20 @@ class RegExpExecDescriptor : public CallInterfaceDescriptor {
CallInterfaceDescriptor)
};
class RegExpReplaceDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kReceiver, kString, kReplaceValue)
DECLARE_DEFAULT_DESCRIPTOR(RegExpReplaceDescriptor, CallInterfaceDescriptor,
kParameterCount)
};
class RegExpSplitDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kReceiver, kString, kLimit)
DECLARE_DEFAULT_DESCRIPTOR(RegExpSplitDescriptor, CallInterfaceDescriptor,
kParameterCount)
};
class CopyFastSmiOrObjectElementsDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kObject)
......
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