Commit 886c6583 authored by peterwmwong's avatar peterwmwong Committed by Commit Bot

[builtins] Port ReplaceSimpleStringFastPath and RegExpMatchInfo to Torque.

Bug: v8:8976
Change-Id: I2d5131c2a1d96e5d5e0114efac3b1b2c3497351d
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1566249Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarSimon Zünd <szuend@chromium.org>
Commit-Queue: Peter Wong <peter.wm.wong@gmail.com>
Cr-Commit-Position: refs/heads/master@{#60861}
parent 2d8f2e86
......@@ -895,6 +895,12 @@ transient type FastJSRegExp extends JSRegExp;
extern operator '.global' macro
RegExpBuiltinsAssembler::FastFlagGetterGlobal(FastJSRegExp): bool;
extern operator '.unicode' macro
RegExpBuiltinsAssembler::FastFlagGetterUnicode(FastJSRegExp): bool;
extern operator '.lastIndex' macro
RegExpBuiltinsAssembler::FastLoadLastIndex(FastJSRegExp): Smi;
extern operator '.lastIndex=' macro
RegExpBuiltinsAssembler::FastStoreLastIndex(FastJSRegExp, Smi): void;
extern class JSRegExpResult extends JSArray {
index: Object;
......@@ -908,6 +914,25 @@ extern class JSRegExpStringIterator extends JSObject {
flags: Smi;
}
const kRegExpMatchInfoFirstCaptureIndex:
constexpr int31 generates 'RegExpMatchInfo::kFirstCaptureIndex';
macro GetStartOfCaptureIndex(captureIndex: constexpr int31): constexpr int31 {
return kRegExpMatchInfoFirstCaptureIndex + (captureIndex * 2);
}
extern class RegExpMatchInfo extends FixedArray {
GetStartOfCapture(implicit context: Context)(captureIndex: constexpr int31):
Smi {
const index: constexpr int31 = GetStartOfCaptureIndex(captureIndex);
return UnsafeCast<Smi>(this.objects[index]);
}
GetEndOfCapture(implicit context: Context)(captureIndex: constexpr int31):
Smi {
const index: constexpr int31 = GetStartOfCaptureIndex(captureIndex) + 1;
return UnsafeCast<Smi>(this.objects[index]);
}
}
extern class AccessorInfo extends Struct {
name: Object;
flags: Smi;
......@@ -1154,6 +1179,10 @@ extern operator '|' macro WordOr(uintptr, uintptr): uintptr;
extern operator '+' macro Int32Add(int32, int32): int32;
extern operator '+' macro ConstexprUint32Add(
constexpr uint32, constexpr int32): constexpr uint32;
extern operator '+' macro ConstexprInt31Add(
constexpr int31, constexpr int31): constexpr int31;
extern operator '*' macro ConstexprInt31Mul(
constexpr int31, constexpr int31): constexpr int31;
extern operator '-' macro Int32Sub(int32, int32): int32;
extern operator '*' macro Int32Mul(int32, int32): int32;
extern operator '%' macro Int32Mod(int32, int32): int32;
......
......@@ -781,6 +781,14 @@ RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
return CAST(var_result.value());
}
TNode<RegExpMatchInfo>
RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResultFast(
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
TNode<String> string, Label* if_didnotmatch) {
return RegExpPrototypeExecBodyWithoutResult(context, maybe_regexp, string,
if_didnotmatch, true);
}
// ES#sec-regexp.prototype.exec
// RegExp.prototype.exec ( string )
TNode<HeapObject> RegExpBuiltinsAssembler::RegExpPrototypeExecBody(
......@@ -1823,6 +1831,8 @@ Node* RegExpBuiltinsAssembler::AdvanceStringIndex(Node* const string,
if (is_fastpath) CSA_ASSERT(this, TaggedIsPositiveSmi(index));
// Default to last_index + 1.
// TODO(pwong): Consider using TrySmiAdd for the fast path to reduce generated
// code.
Node* const index_plus_one = NumberInc(index);
VARIABLE(var_result, MachineRepresentation::kTagged, index_plus_one);
......@@ -2882,95 +2892,6 @@ Node* RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
return var_result.value();
}
Node* RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
Node* context, Node* regexp, TNode<String> string,
TNode<String> replace_string) {
// The fast path is reached only if {receiver} is an unmodified
// JSRegExp instance, {replace_value} is non-callable, and
// ToString({replace_value}) does not contain '$', i.e. we're doing a simple
// string replacement.
CSA_ASSERT(this, IsFastRegExp(context, regexp));
const bool kIsFastPath = true;
TVARIABLE(String, var_result, EmptyStringConstant());
VARIABLE(var_last_match_end, MachineRepresentation::kTagged, SmiZero());
VARIABLE(var_is_unicode, MachineRepresentation::kWord32, Int32Constant(0));
Variable* vars[] = {&var_result, &var_last_match_end};
Label out(this), loop(this, 2, vars), loop_end(this),
if_nofurthermatches(this);
// Is {regexp} global?
Node* const is_global = FastFlagGetter(CAST(regexp), JSRegExp::kGlobal);
GotoIfNot(is_global, &loop);
var_is_unicode.Bind(FastFlagGetter(CAST(regexp), JSRegExp::kUnicode));
FastStoreLastIndex(regexp, SmiZero());
Goto(&loop);
BIND(&loop);
{
TNode<RegExpMatchInfo> var_match_indices =
RegExpPrototypeExecBodyWithoutResult(CAST(context), CAST(regexp),
string, &if_nofurthermatches,
kIsFastPath);
// Successful match.
{
TNode<Smi> const match_start = CAST(UnsafeLoadFixedArrayElement(
var_match_indices, RegExpMatchInfo::kFirstCaptureIndex));
TNode<Smi> const match_end = CAST(UnsafeLoadFixedArrayElement(
var_match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1));
TNode<Smi> const replace_length = LoadStringLengthAsSmi(replace_string);
// TODO(jgruber): We could skip many of the checks that using SubString
// here entails.
TNode<String> first_part =
CAST(CallBuiltin(Builtins::kSubString, context, string,
var_last_match_end.value(), match_start));
var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
var_result.value(), first_part));
GotoIf(SmiEqual(replace_length, SmiZero()), &loop_end);
var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
var_result.value(), replace_string));
Goto(&loop_end);
BIND(&loop_end);
{
var_last_match_end.Bind(match_end);
// Non-global case ends here after the first replacement.
GotoIfNot(is_global, &if_nofurthermatches);
GotoIf(SmiNotEqual(match_end, match_start), &loop);
// If match is the empty string, we have to increment lastIndex.
Node* const this_index = FastLoadLastIndex(CAST(regexp));
Node* const next_index = AdvanceStringIndex(
string, this_index, var_is_unicode.value(), kIsFastPath);
FastStoreLastIndex(regexp, next_index);
Goto(&loop);
}
}
}
BIND(&if_nofurthermatches);
{
TNode<Smi> const string_length = LoadStringLengthAsSmi(string);
TNode<String> last_part =
CAST(CallBuiltin(Builtins::kSubString, context, string,
var_last_match_end.value(), string_length));
var_result = CAST(CallBuiltin(Builtins::kStringAdd_CheckNone, context,
var_result.value(), last_part));
Goto(&out);
}
BIND(&out);
return var_result.value();
}
class RegExpStringIteratorAssembler : public RegExpBuiltinsAssembler {
public:
explicit RegExpStringIteratorAssembler(compiler::CodeAssemblerState* state)
......
......@@ -74,6 +74,10 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
TNode<RegExpMatchInfo> RegExpPrototypeExecBodyWithoutResult(
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
TNode<String> string, Label* if_didnotmatch, const bool is_fastpath);
TNode<RegExpMatchInfo> RegExpPrototypeExecBodyWithoutResultFast(
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
TNode<String> string, Label* if_didnotmatch);
TNode<HeapObject> RegExpPrototypeExecBody(TNode<Context> context,
TNode<JSReceiver> maybe_regexp,
TNode<String> string,
......@@ -108,6 +112,9 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
TNode<BoolT> FastFlagGetterGlobal(TNode<JSRegExp> regexp) {
return ReinterpretCast<BoolT>(FastFlagGetter(regexp, JSRegExp::kGlobal));
}
TNode<BoolT> FastFlagGetterUnicode(TNode<JSRegExp> regexp) {
return ReinterpretCast<BoolT>(FastFlagGetter(regexp, JSRegExp::kUnicode));
}
TNode<Int32T> SlowFlagGetter(TNode<Context> context, TNode<Object> regexp,
JSRegExp::Flag flag);
TNode<Int32T> FlagGetter(TNode<Context> context, TNode<Object> regexp,
......@@ -124,6 +131,11 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
Node* AdvanceStringIndex(Node* const string, Node* const index,
Node* const is_unicode, bool is_fastpath);
Node* AdvanceStringIndexFast(Node* const string, Node* const index,
Node* const is_unicode) {
return AdvanceStringIndex(string, index, is_unicode, true);
}
void RegExpPrototypeMatchBody(Node* const context, Node* const regexp,
TNode<String> const string,
const bool is_fastpath);
......@@ -139,9 +151,6 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
Node* ReplaceGlobalCallableFastPath(Node* context, Node* regexp, Node* string,
Node* replace_callable);
Node* ReplaceSimpleStringFastPath(Node* context, Node* regexp,
TNode<String> string,
TNode<String> replace_string);
};
class RegExpMatchAllAssembler : public RegExpBuiltinsAssembler {
......
......@@ -8,13 +8,14 @@ namespace regexp_replace {
extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
extern builtin
SubString(implicit context: Context)(String, Smi, Smi): String;
extern transitioning macro
RegExpBuiltinsAssembler::ReplaceSimpleStringFastPath(
implicit context: Context)(JSRegExp, String, String): String;
extern transitioning macro
RegExpBuiltinsAssembler::ReplaceGlobalCallableFastPath(
implicit context: Context)(JSRegExp, String, Callable): String;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Number, bool): Smi;
extern transitioning runtime
RegExpReplaceRT(Context, JSReceiver, String, Object): String;
......@@ -22,6 +23,53 @@ namespace regexp_replace {
StringReplaceNonGlobalRegExpWithFunction(implicit context: Context)(
String, JSRegExp, Callable): String;
extern macro
RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResultFast(
implicit context: Context)(JSReceiver, String):
RegExpMatchInfo labels IfDidNotMatch;
transitioning macro RegExpReplaceFastString(implicit context: Context)(
regexp: FastJSRegExp, string: String, replaceString: String): String {
// The fast path is reached only if {receiver} is an unmodified JSRegExp
// instance, {replace_value} is non-callable, and ToString({replace_value})
// does not contain '$', i.e. we're doing a simple string replacement.
let result: String = kEmptyString;
let lastMatchEnd: Smi = 0;
let unicode: bool = false;
let replaceLength: Smi = replaceString.length_smi;
const global: bool = regexp.global;
if (global) {
unicode = regexp.unicode;
regexp.lastIndex = 0;
}
while (true) {
const match: RegExpMatchInfo = RegExpPrototypeExecBodyWithoutResultFast(
regexp, string) otherwise break;
const matchStart: Smi = match.GetStartOfCapture(0);
const matchEnd: Smi = match.GetEndOfCapture(0);
// TODO(jgruber): We could skip many of the checks that using SubString
// here entails.
result = result + SubString(string, lastMatchEnd, matchStart);
lastMatchEnd = matchEnd;
if (replaceLength != 0) result = result + replaceString;
// Non-global case ends here after the first replacement.
if (!global) break;
// If match is the empty string, we have to increment lastIndex.
if (matchEnd == matchStart) {
regexp.lastIndex =
AdvanceStringIndexFast(string, regexp.lastIndex, unicode);
}
}
return result + SubString(string, lastMatchEnd, string.length_smi);
}
transitioning builtin RegExpReplace(implicit context: Context)(
regexp: FastJSRegExp, string: String, replaceValue: Object): String {
// TODO(pwong): Remove assert when all callers (StringPrototypeReplace) are
......@@ -49,7 +97,7 @@ namespace regexp_replace {
goto Runtime;
}
return ReplaceSimpleStringFastPath(fastRegexp, string, replaceString);
return RegExpReplaceFastString(fastRegexp, string, replaceString);
}
label Runtime deferred {
return RegExpReplaceRT(context, stableRegexp, string, replaceString);
......
......@@ -3306,6 +3306,16 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
bool ConstexprInt31Equal(int31_t a, int31_t b) { return a == b; }
bool ConstexprInt31GreaterThanEqual(int31_t a, int31_t b) { return a >= b; }
uint32_t ConstexprUint32Add(uint32_t a, uint32_t b) { return a + b; }
int31_t ConstexprInt31Add(int31_t a, int31_t b) {
int32_t val;
CHECK(!base::bits::SignedAddOverflow32(a, b, &val));
return val;
}
int31_t ConstexprInt31Mul(int31_t a, int31_t b) {
int32_t val;
CHECK(!base::bits::SignedMulOverflow32(a, b, &val));
return val;
}
void PerformStackCheck(TNode<Context> context);
......
......@@ -57,17 +57,6 @@ class V8_EXPORT_PRIVATE RegExpMatchInfo : NON_EXPORTED_BASE(public FixedArray) {
static const int kFirstCaptureIndex = 3;
static const int kLastMatchOverhead = kFirstCaptureIndex;
// Layout description.
#define REG_EXP_MATCH_INFO_FIELDS(V) \
V(kNumberOfCapturesOffset, kTaggedSize) \
V(kLastSubjectOffset, kTaggedSize) \
V(kLastInputOffset, kTaggedSize) \
V(kFirstCaptureOffset, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(FixedArray::kHeaderSize,
REG_EXP_MATCH_INFO_FIELDS)
#undef REG_EXP_MATCH_INFO_FIELDS
// Every match info is guaranteed to have enough space to store two captures.
static const int kInitialCaptureIndices = 2;
......
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