[torque] Port String.prototype.match/search
- No noticeable changes in runtime performance according to js-perf-test/RegExp/{base_search.js, base_match.js} benchmarks - Slight builtin code sizes reduction: BEFORE ====== TFJ Builtin, StringPrototypeSearch, 1592 TFJ Builtin, StringPrototypeMatch, 1592 AFTER ===== TFJ Builtin, StringPrototypeSearch, 1432 TFJ Builtin, StringPrototypeMatch, 1432 Bug: v8:8996 Change-Id: Ifeadac1c924a36003a41be3b805438522f8188be Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2670247 Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Nico Hartmann <nicohartmann@chromium.org> Commit-Queue: Peter Wong <peter.wm.wong@gmail.com> Cr-Commit-Position: refs/heads/master@{#72592}
This commit is contained in:
parent
5cc7bfd04c
commit
3985d6a461
1
BUILD.gn
1
BUILD.gn
@ -1327,6 +1327,7 @@ torque_files = [
|
||||
"src/builtins/string-includes.tq",
|
||||
"src/builtins/string-indexof.tq",
|
||||
"src/builtins/string-iterator.tq",
|
||||
"src/builtins/string-match-search.tq",
|
||||
"src/builtins/string-pad.tq",
|
||||
"src/builtins/string-repeat.tq",
|
||||
"src/builtins/string-replaceall.tq",
|
||||
|
@ -465,6 +465,7 @@ extern macro NameStringConstant(): String;
|
||||
extern macro NullConstant(): Null;
|
||||
extern macro NumberStringConstant(): String;
|
||||
extern macro ReturnStringConstant(): String;
|
||||
extern macro SearchSymbolConstant(): Symbol;
|
||||
extern macro StringStringConstant(): String;
|
||||
extern macro TheHoleConstant(): TheHole;
|
||||
extern macro ToPrimitiveSymbolConstant(): PublicSymbol;
|
||||
|
@ -751,16 +751,12 @@ namespace internal {
|
||||
TFJ(StringFromCharCode, kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 #sec-string.prototype.lastindexof */ \
|
||||
CPP(StringPrototypeLastIndexOf) \
|
||||
/* ES6 #sec-string.prototype.match */ \
|
||||
TFJ(StringPrototypeMatch, 1, kReceiver, kRegexp) \
|
||||
/* ES #sec-string.prototype.matchAll */ \
|
||||
TFJ(StringPrototypeMatchAll, 1, kReceiver, kRegexp) \
|
||||
/* ES6 #sec-string.prototype.localecompare */ \
|
||||
CPP(StringPrototypeLocaleCompare) \
|
||||
/* ES6 #sec-string.prototype.replace */ \
|
||||
TFJ(StringPrototypeReplace, 2, kReceiver, kSearch, kReplace) \
|
||||
/* ES6 #sec-string.prototype.search */ \
|
||||
TFJ(StringPrototypeSearch, 1, kReceiver, kRegexp) \
|
||||
/* ES6 #sec-string.prototype.split */ \
|
||||
TFJ(StringPrototypeSplit, kDontAdaptArgumentsSentinel) \
|
||||
/* ES6 #sec-string.raw */ \
|
||||
|
@ -858,6 +858,29 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp(
|
||||
prototype_check_assembler.CheckAndBranch(prototype, if_isunmodified,
|
||||
if_ismodified);
|
||||
}
|
||||
void RegExpBuiltinsAssembler::BranchIfFastRegExpForSearch(
|
||||
TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
|
||||
Label* if_ismodified) {
|
||||
BranchIfFastRegExp(
|
||||
context, object, LoadMap(object),
|
||||
PrototypeCheckAssembler::kCheckPrototypePropertyConstness,
|
||||
DescriptorIndexNameValue{JSRegExp::kSymbolSearchFunctionDescriptorIndex,
|
||||
RootIndex::ksearch_symbol,
|
||||
Context::REGEXP_SEARCH_FUNCTION_INDEX},
|
||||
if_isunmodified, if_ismodified);
|
||||
}
|
||||
|
||||
void RegExpBuiltinsAssembler::BranchIfFastRegExpForMatch(
|
||||
TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
|
||||
Label* if_ismodified) {
|
||||
BranchIfFastRegExp(
|
||||
context, object, LoadMap(object),
|
||||
PrototypeCheckAssembler::kCheckPrototypePropertyConstness,
|
||||
DescriptorIndexNameValue{JSRegExp::kSymbolMatchFunctionDescriptorIndex,
|
||||
RootIndex::kmatch_symbol,
|
||||
Context::REGEXP_MATCH_FUNCTION_INDEX},
|
||||
if_isunmodified, if_ismodified);
|
||||
}
|
||||
|
||||
void RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict(
|
||||
TNode<Context> context, TNode<HeapObject> object, Label* if_isunmodified,
|
||||
|
@ -98,6 +98,14 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
base::Optional<DescriptorIndexNameValue> additional_property_to_check,
|
||||
Label* if_isunmodified, Label* if_ismodified);
|
||||
|
||||
void BranchIfFastRegExpForSearch(TNode<Context> context,
|
||||
TNode<HeapObject> object,
|
||||
Label* if_isunmodified,
|
||||
Label* if_ismodified);
|
||||
void BranchIfFastRegExpForMatch(TNode<Context> context,
|
||||
TNode<HeapObject> object,
|
||||
Label* if_isunmodified, Label* if_ismodified);
|
||||
|
||||
// Strict: Does not tolerate any changes to the prototype map.
|
||||
// Permissive: Allows changes to the prototype map except for the exec
|
||||
// property.
|
||||
|
@ -1160,87 +1160,6 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
||||
}
|
||||
}
|
||||
|
||||
class StringMatchSearchAssembler : public StringBuiltinsAssembler {
|
||||
public:
|
||||
explicit StringMatchSearchAssembler(compiler::CodeAssemblerState* state)
|
||||
: StringBuiltinsAssembler(state) {}
|
||||
|
||||
protected:
|
||||
enum Variant { kMatch, kSearch };
|
||||
|
||||
void Generate(Variant variant, const char* method_name,
|
||||
TNode<Object> receiver, TNode<Object> maybe_regexp,
|
||||
TNode<Context> context) {
|
||||
Label call_regexp_match_search(this);
|
||||
|
||||
Builtins::Name builtin;
|
||||
Handle<Symbol> symbol;
|
||||
DescriptorIndexNameValue property_to_check;
|
||||
if (variant == kMatch) {
|
||||
builtin = Builtins::kRegExpMatchFast;
|
||||
symbol = isolate()->factory()->match_symbol();
|
||||
property_to_check = DescriptorIndexNameValue{
|
||||
JSRegExp::kSymbolMatchFunctionDescriptorIndex,
|
||||
RootIndex::kmatch_symbol, Context::REGEXP_MATCH_FUNCTION_INDEX};
|
||||
} else {
|
||||
builtin = Builtins::kRegExpSearchFast;
|
||||
symbol = isolate()->factory()->search_symbol();
|
||||
property_to_check = DescriptorIndexNameValue{
|
||||
JSRegExp::kSymbolSearchFunctionDescriptorIndex,
|
||||
RootIndex::ksearch_symbol, Context::REGEXP_SEARCH_FUNCTION_INDEX};
|
||||
}
|
||||
|
||||
RequireObjectCoercible(context, receiver, method_name);
|
||||
|
||||
MaybeCallFunctionAtSymbol(
|
||||
context, maybe_regexp, receiver, symbol, property_to_check,
|
||||
[=] { Return(CallBuiltin(builtin, context, maybe_regexp, receiver)); },
|
||||
[=](TNode<Object> fn) {
|
||||
Return(Call(context, fn, maybe_regexp, receiver));
|
||||
});
|
||||
|
||||
// maybe_regexp is not a RegExp nor has [@@match / @@search] property.
|
||||
{
|
||||
RegExpBuiltinsAssembler regexp_asm(state());
|
||||
|
||||
TNode<String> receiver_string = ToString_Inline(context, receiver);
|
||||
TNode<NativeContext> native_context = LoadNativeContext(context);
|
||||
TNode<HeapObject> regexp_function = CAST(
|
||||
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
|
||||
TNode<Map> initial_map = CAST(LoadObjectField(
|
||||
regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
TNode<Object> regexp = regexp_asm.RegExpCreate(
|
||||
context, initial_map, maybe_regexp, EmptyStringConstant());
|
||||
|
||||
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
|
||||
// permissive.
|
||||
Label fast_path(this), slow_path(this);
|
||||
regexp_asm.BranchIfFastRegExp(
|
||||
context, CAST(regexp), initial_map,
|
||||
PrototypeCheckAssembler::kCheckPrototypePropertyConstness,
|
||||
property_to_check, &fast_path, &slow_path);
|
||||
|
||||
BIND(&fast_path);
|
||||
Return(CallBuiltin(builtin, context, regexp, receiver_string));
|
||||
|
||||
BIND(&slow_path);
|
||||
{
|
||||
TNode<Object> maybe_func = GetProperty(context, regexp, symbol);
|
||||
Return(Call(context, maybe_func, regexp, receiver_string));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ES6 #sec-string.prototype.match
|
||||
TF_BUILTIN(StringPrototypeMatch, StringMatchSearchAssembler) {
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto maybe_regexp = Parameter<Object>(Descriptor::kRegexp);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
|
||||
Generate(kMatch, "String.prototype.match", receiver, maybe_regexp, context);
|
||||
}
|
||||
|
||||
// ES #sec-string.prototype.matchAll
|
||||
TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
|
||||
char const* method_name = "String.prototype.matchAll";
|
||||
@ -1340,14 +1259,6 @@ TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) {
|
||||
Return(Call(context, match_all_func, rx, s));
|
||||
}
|
||||
|
||||
// ES6 #sec-string.prototype.search
|
||||
TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
|
||||
auto receiver = Parameter<Object>(Descriptor::kReceiver);
|
||||
auto maybe_regexp = Parameter<Object>(Descriptor::kRegexp);
|
||||
auto context = Parameter<Context>(Descriptor::kContext);
|
||||
Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context);
|
||||
}
|
||||
|
||||
TNode<JSArray> StringBuiltinsAssembler::StringToArray(
|
||||
TNode<NativeContext> context, TNode<String> subject_string,
|
||||
TNode<Smi> subject_length, TNode<Number> limit_number) {
|
||||
|
@ -6,6 +6,20 @@
|
||||
|
||||
namespace regexp {
|
||||
|
||||
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExpForMatch(
|
||||
implicit context: Context)(HeapObject): never labels IsFast,
|
||||
IsSlow;
|
||||
macro IsFastRegExpForMatch(implicit context: Context)(o: HeapObject): bool {
|
||||
BranchIfFastRegExpForMatch(o) otherwise return true, return false;
|
||||
}
|
||||
|
||||
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExpForSearch(
|
||||
implicit context: Context)(HeapObject): never labels IsFast,
|
||||
IsSlow;
|
||||
macro IsFastRegExpForSearch(implicit context: Context)(o: HeapObject): bool {
|
||||
BranchIfFastRegExpForSearch(o) otherwise return true, return false;
|
||||
}
|
||||
|
||||
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Strict(
|
||||
implicit context: Context)(HeapObject): never labels IsFast,
|
||||
IsSlow;
|
||||
|
86
src/builtins/string-match-search.tq
Normal file
86
src/builtins/string-match-search.tq
Normal file
@ -0,0 +1,86 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
namespace string {
|
||||
|
||||
struct StringMatchFunctor {
|
||||
macro FnSymbol(): Symbol {
|
||||
return MatchSymbolConstant();
|
||||
}
|
||||
macro CanCallFast(implicit context: Context)(maybeRegExp: HeapObject): bool {
|
||||
return regexp::IsFastRegExpForMatch(maybeRegExp);
|
||||
}
|
||||
transitioning macro CallFast(implicit context: Context)(
|
||||
regexp: FastJSRegExp, string: String): JSAny {
|
||||
return regexp::RegExpMatchFast(regexp, string);
|
||||
}
|
||||
}
|
||||
|
||||
struct StringSearchFunctor {
|
||||
macro FnSymbol(): Symbol {
|
||||
return SearchSymbolConstant();
|
||||
}
|
||||
macro CanCallFast(implicit context: Context)(maybeRegExp: HeapObject): bool {
|
||||
return regexp::IsFastRegExpForSearch(maybeRegExp);
|
||||
}
|
||||
transitioning macro CallFast(implicit context: Context)(
|
||||
regexp: FastJSRegExp, string: String): JSAny {
|
||||
return regexp::RegExpSearchFast(regexp, string);
|
||||
}
|
||||
}
|
||||
|
||||
transitioning macro StringMatchSearch<F: type>(
|
||||
implicit context: NativeContext, receiver: JSAny)(
|
||||
regexp: JSAny, functor: F, methodName: constexpr string): JSAny {
|
||||
// 1. Let O be ? RequireObjectCoercible(this value).
|
||||
RequireObjectCoercible(receiver, methodName);
|
||||
|
||||
try {
|
||||
// 3. Let string be ? ToString(O).
|
||||
const string = Cast<String>(receiver) otherwise Slow;
|
||||
const heapRegexp = Cast<HeapObject>(regexp) otherwise Slow;
|
||||
if (!functor.CanCallFast(heapRegexp)) goto Slow;
|
||||
|
||||
return functor.CallFast(UnsafeCast<FastJSRegExp>(heapRegexp), string);
|
||||
} label Slow deferred {
|
||||
// 2. If regexp is neither undefined nor null, then
|
||||
if (regexp != Undefined && regexp != Null) {
|
||||
try {
|
||||
// a. Let fn be ? GetMethod(regexp, @@match/@@search).
|
||||
// b. If fn is not undefined, then
|
||||
const fn = GetMethod(regexp, functor.FnSymbol())
|
||||
otherwise FnSymbolIsNullOrUndefined;
|
||||
// i. Return ? Call(fn, regexp, « O »).
|
||||
return Call(context, fn, regexp, receiver);
|
||||
} label FnSymbolIsNullOrUndefined {}
|
||||
}
|
||||
|
||||
// 3. Let string be ? ToString(O).
|
||||
const string = ToString_Inline(receiver);
|
||||
|
||||
// 4. Let rx be ? RegExpCreate(regexp, undefined).
|
||||
const rx = regexp::RegExpCreate(context, regexp, kEmptyString);
|
||||
|
||||
// 5. Return ? Invoke(rx, @@match/@@search, « string »).
|
||||
const fn = GetProperty(rx, functor.FnSymbol());
|
||||
return Call(context, fn, rx, string);
|
||||
}
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-string.prototype.match
|
||||
transitioning javascript builtin
|
||||
StringPrototypeMatch(
|
||||
js-implicit context: NativeContext, receiver: JSAny)(regexp: JSAny): JSAny {
|
||||
return StringMatchSearch(
|
||||
regexp, StringMatchFunctor{}, 'String.prototype.match');
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-string.prototype.search
|
||||
transitioning javascript builtin
|
||||
StringPrototypeSearch(
|
||||
js-implicit context: NativeContext, receiver: JSAny)(regexp: JSAny): JSAny {
|
||||
return StringMatchSearch(
|
||||
regexp, StringSearchFunctor{}, 'String.prototype.search');
|
||||
}
|
||||
}
|
@ -189,6 +189,7 @@ enum class PrimitiveType { kBoolean, kNumber, kString, kSymbol };
|
||||
V(regexp_to_string, regexp_to_string, RegexpToString) \
|
||||
V(resolve_string, resolve_string, ResolveString) \
|
||||
V(return_string, return_string, ReturnString) \
|
||||
V(search_symbol, search_symbol, SearchSymbol) \
|
||||
V(species_symbol, species_symbol, SpeciesSymbol) \
|
||||
V(StaleRegister, stale_register, StaleRegister) \
|
||||
V(StoreHandler0Map, store_handler0_map, StoreHandler0Map) \
|
||||
|
Loading…
Reference in New Issue
Block a user