From 3985d6a4614e42e3eb6d605feda6037500aa4b01 Mon Sep 17 00:00:00 2001 From: Peter Wong Date: Wed, 3 Feb 2021 10:43:10 -0600 Subject: [PATCH] [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 Reviewed-by: Nico Hartmann Commit-Queue: Peter Wong Cr-Commit-Position: refs/heads/master@{#72592} --- BUILD.gn | 1 + src/builtins/base.tq | 1 + src/builtins/builtins-definitions.h | 4 -- src/builtins/builtins-regexp-gen.cc | 23 ++++++++ src/builtins/builtins-regexp-gen.h | 8 +++ src/builtins/builtins-string-gen.cc | 89 ----------------------------- src/builtins/regexp.tq | 14 +++++ src/builtins/string-match-search.tq | 86 ++++++++++++++++++++++++++++ src/codegen/code-stub-assembler.h | 1 + 9 files changed, 134 insertions(+), 93 deletions(-) create mode 100644 src/builtins/string-match-search.tq diff --git a/BUILD.gn b/BUILD.gn index ebdb0c711e..32f0802296 100644 --- a/BUILD.gn +++ b/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", diff --git a/src/builtins/base.tq b/src/builtins/base.tq index 65bd7fe94d..a4d6663771 100644 --- a/src/builtins/base.tq +++ b/src/builtins/base.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; diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index aac4358b3c..6b8b6b3937 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -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 */ \ diff --git a/src/builtins/builtins-regexp-gen.cc b/src/builtins/builtins-regexp-gen.cc index de088ad2ac..97212afc6c 100644 --- a/src/builtins/builtins-regexp-gen.cc +++ b/src/builtins/builtins-regexp-gen.cc @@ -858,6 +858,29 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp( prototype_check_assembler.CheckAndBranch(prototype, if_isunmodified, if_ismodified); } +void RegExpBuiltinsAssembler::BranchIfFastRegExpForSearch( + TNode context, TNode 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, TNode 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, TNode object, Label* if_isunmodified, diff --git a/src/builtins/builtins-regexp-gen.h b/src/builtins/builtins-regexp-gen.h index 2f3d9a2051..0538b77165 100644 --- a/src/builtins/builtins-regexp-gen.h +++ b/src/builtins/builtins-regexp-gen.h @@ -98,6 +98,14 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler { base::Optional additional_property_to_check, Label* if_isunmodified, Label* if_ismodified); + void BranchIfFastRegExpForSearch(TNode context, + TNode object, + Label* if_isunmodified, + Label* if_ismodified); + void BranchIfFastRegExpForMatch(TNode context, + TNode 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. diff --git a/src/builtins/builtins-string-gen.cc b/src/builtins/builtins-string-gen.cc index 4c70a82a61..d46bbacadb 100644 --- a/src/builtins/builtins-string-gen.cc +++ b/src/builtins/builtins-string-gen.cc @@ -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 receiver, TNode maybe_regexp, - TNode context) { - Label call_regexp_match_search(this); - - Builtins::Name builtin; - Handle 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 fn) { - Return(Call(context, fn, maybe_regexp, receiver)); - }); - - // maybe_regexp is not a RegExp nor has [@@match / @@search] property. - { - RegExpBuiltinsAssembler regexp_asm(state()); - - TNode receiver_string = ToString_Inline(context, receiver); - TNode native_context = LoadNativeContext(context); - TNode regexp_function = CAST( - LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); - TNode initial_map = CAST(LoadObjectField( - regexp_function, JSFunction::kPrototypeOrInitialMapOffset)); - TNode 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 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(Descriptor::kReceiver); - auto maybe_regexp = Parameter(Descriptor::kRegexp); - auto context = Parameter(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(Descriptor::kReceiver); - auto maybe_regexp = Parameter(Descriptor::kRegexp); - auto context = Parameter(Descriptor::kContext); - Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context); -} - TNode StringBuiltinsAssembler::StringToArray( TNode context, TNode subject_string, TNode subject_length, TNode limit_number) { diff --git a/src/builtins/regexp.tq b/src/builtins/regexp.tq index 9fb1215e2e..29fad26736 100644 --- a/src/builtins/regexp.tq +++ b/src/builtins/regexp.tq @@ -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; diff --git a/src/builtins/string-match-search.tq b/src/builtins/string-match-search.tq new file mode 100644 index 0000000000..18dcf1b812 --- /dev/null +++ b/src/builtins/string-match-search.tq @@ -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( + 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(receiver) otherwise Slow; + const heapRegexp = Cast(regexp) otherwise Slow; + if (!functor.CanCallFast(heapRegexp)) goto Slow; + + return functor.CallFast(UnsafeCast(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'); +} +} diff --git a/src/codegen/code-stub-assembler.h b/src/codegen/code-stub-assembler.h index c22e2a6cc0..e01864a692 100644 --- a/src/codegen/code-stub-assembler.h +++ b/src/codegen/code-stub-assembler.h @@ -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) \