From 5a2c53f9482e19d150303cd36d7ba04418e6cade Mon Sep 17 00:00:00 2001 From: Peter Wong Date: Mon, 1 Feb 2021 01:34:16 -0600 Subject: [PATCH] [torque] Port String.prototype.includes/indexOf and StringIndexOf - Removed no longer used StringBuiltinAssembler methods (DispatchOnStringEncodings, PointerToStringDataAtIndex) - Removed no longer used Runtime functions (StringIncludes, StringIndexOf, StringIndexOfUnchecked). - Overall builtin code size is reduced (652 bytes on Mac x64.release build), builtin size breakdown: BEFORE ====== TFS Builtin, StringIndexOf, 1092 TFJ Builtin, StringPrototypeIncludes, 1784 TFJ Builtin, StringPrototypeIndexOf, 1536 Total = 4412 AFTER ===== TFC Builtin, StringIndexOf, 2036 (+944) TFJ Builtin, StringPrototypeIncludes, 1072 (-712) TFJ Builtin, StringPrototypeIndexOf, 652 (-884) Total = 3760 (-652) Bug: v8:8996 Change-Id: I9a88c095e2097f7d570e58e744d6692dc524ddf4 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2660995 Commit-Queue: Peter Wong Reviewed-by: Jakob Gruber Cr-Commit-Position: refs/heads/master@{#72456} --- BUILD.gn | 2 + src/builtins/builtins-definitions.h | 5 - src/builtins/builtins-string-gen.cc | 367 ++++------------------------ src/builtins/builtins-string-gen.h | 49 ++-- src/builtins/builtins-string.tq | 4 +- src/builtins/regexp-match.tq | 4 +- src/builtins/string-includes.tq | 49 ++++ src/builtins/string-indexof.tq | 39 +++ src/builtins/string-replaceall.tq | 46 ---- src/debug/debug-evaluate.cc | 3 - src/objects/string.tq | 86 +++++++ src/runtime/runtime-strings.cc | 66 ----- src/runtime/runtime.h | 3 - tools/v8heapconst.py | 42 ++-- 14 files changed, 273 insertions(+), 492 deletions(-) create mode 100644 src/builtins/string-includes.tq create mode 100644 src/builtins/string-indexof.tq diff --git a/BUILD.gn b/BUILD.gn index 9e7efb3da1..d0c7210b66 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -1315,6 +1315,8 @@ torque_files = [ "src/builtins/string-at.tq", "src/builtins/string-endswith.tq", "src/builtins/string-html.tq", + "src/builtins/string-includes.tq", + "src/builtins/string-indexof.tq", "src/builtins/string-iterator.tq", "src/builtins/string-pad.tq", "src/builtins/string-repeat.tq", diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index ecc2a6fa05..f735791171 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -110,7 +110,6 @@ namespace internal { TFC(StringEqual, Compare) \ TFC(StringGreaterThan, Compare) \ TFC(StringGreaterThanOrEqual, Compare) \ - TFS(StringIndexOf, kReceiver, kSearchString, kPosition) \ TFC(StringLessThan, Compare) \ TFC(StringLessThanOrEqual, Compare) \ TFC(StringSubstring, StringSubstring) \ @@ -750,10 +749,6 @@ namespace internal { CPP(StringFromCodePoint) \ /* ES6 #sec-string.fromcharcode */ \ TFJ(StringFromCharCode, kDontAdaptArgumentsSentinel) \ - /* ES6 #sec-string.prototype.includes */ \ - TFJ(StringPrototypeIncludes, kDontAdaptArgumentsSentinel) \ - /* ES6 #sec-string.prototype.indexof */ \ - TFJ(StringPrototypeIndexOf, kDontAdaptArgumentsSentinel) \ /* ES6 #sec-string.prototype.lastindexof */ \ CPP(StringPrototypeLastIndexOf) \ /* ES6 #sec-string.prototype.match */ \ diff --git a/src/builtins/builtins-string-gen.cc b/src/builtins/builtins-string-gen.cc index 0b08f8a743..4c70a82a61 100644 --- a/src/builtins/builtins-string-gen.cc +++ b/src/builtins/builtins-string-gen.cc @@ -48,46 +48,6 @@ TNode StringBuiltinsAssembler::DirectStringData( return var_data.value(); } -void StringBuiltinsAssembler::DispatchOnStringEncodings( - TNode const lhs_instance_type, - TNode const rhs_instance_type, Label* if_one_one, - Label* if_one_two, Label* if_two_one, Label* if_two_two) { - STATIC_ASSERT(kStringEncodingMask == 0x8); - STATIC_ASSERT(kTwoByteStringTag == 0x0); - STATIC_ASSERT(kOneByteStringTag == 0x8); - - // First combine the encodings. - - const TNode encoding_mask = Int32Constant(kStringEncodingMask); - const TNode lhs_encoding = - Word32And(lhs_instance_type, encoding_mask); - const TNode rhs_encoding = - Word32And(rhs_instance_type, encoding_mask); - - const TNode combined_encodings = - Word32Or(lhs_encoding, Word32Shr(rhs_encoding, 1)); - - // Then dispatch on the combined encoding. - - Label unreachable(this, Label::kDeferred); - - int32_t values[] = { - kOneByteStringTag | (kOneByteStringTag >> 1), - kOneByteStringTag | (kTwoByteStringTag >> 1), - kTwoByteStringTag | (kOneByteStringTag >> 1), - kTwoByteStringTag | (kTwoByteStringTag >> 1), - }; - Label* labels[] = { - if_one_one, if_one_two, if_two_one, if_two_two, - }; - - STATIC_ASSERT(arraysize(values) == arraysize(labels)); - Switch(combined_encodings, &unreachable, values, labels, arraysize(values)); - - BIND(&unreachable); - Unreachable(); -} - template TNode StringBuiltinsAssembler::CallSearchStringRaw( const TNode subject_ptr, const TNode subject_length, @@ -111,15 +71,57 @@ TNode StringBuiltinsAssembler::CallSearchStringRaw( return result; } - -TNode StringBuiltinsAssembler::PointerToStringDataAtIndex( - TNode string_data, TNode index, - String::Encoding encoding) { - const ElementsKind kind = (encoding == String::ONE_BYTE_ENCODING) - ? UINT8_ELEMENTS - : UINT16_ELEMENTS; - TNode offset_in_bytes = ElementOffsetFromIndex(index, kind); - return RawPtrAdd(string_data, offset_in_bytes); +TNode StringBuiltinsAssembler::SearchOneByteStringInTwoByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position) { + return CallSearchStringRaw( + subject_ptr, subject_length, search_ptr, search_length, start_position); +} +TNode StringBuiltinsAssembler::SearchOneByteStringInOneByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position) { + return CallSearchStringRaw( + subject_ptr, subject_length, search_ptr, search_length, start_position); +} +TNode StringBuiltinsAssembler::SearchTwoByteStringInTwoByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position) { + return CallSearchStringRaw( + subject_ptr, subject_length, search_ptr, search_length, start_position); +} +TNode StringBuiltinsAssembler::SearchTwoByteStringInOneByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position) { + return CallSearchStringRaw( + subject_ptr, subject_length, search_ptr, search_length, start_position); +} +TNode StringBuiltinsAssembler::SearchOneByteInOneByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode start_position) { + const TNode subject_start_ptr = + RawPtrAdd(subject_ptr, start_position); + const TNode search_byte = + ChangeInt32ToIntPtr(Load(search_ptr)); + const TNode search_length = + Unsigned(IntPtrSub(subject_length, start_position)); + const TNode memchr = + ExternalConstant(ExternalReference::libc_memchr_function()); + const TNode result_address = UncheckedCast( + CallCFunction(memchr, MachineType::Pointer(), + std::make_pair(MachineType::Pointer(), subject_start_ptr), + std::make_pair(MachineType::IntPtr(), search_byte), + std::make_pair(MachineType::UintPtr(), search_length))); + return Select( + WordEqual(result_address, IntPtrConstant(0)), + [=] { return IntPtrConstant(-1); }, + [=] { + return IntPtrAdd(RawPtrSub(result_address, subject_start_ptr), + start_position); + }); } void StringBuiltinsAssembler::GenerateStringEqual(TNode left, @@ -887,273 +889,6 @@ TF_BUILTIN(StringFromCharCode, StringBuiltinsAssembler) { } } -void StringBuiltinsAssembler::StringIndexOf( - const TNode subject_string, const TNode search_string, - const TNode position, - const std::function)>& f_return) { - const TNode int_zero = IntPtrConstant(0); - const TNode search_length = LoadStringLengthAsWord(search_string); - const TNode subject_length = LoadStringLengthAsWord(subject_string); - const TNode start_position = IntPtrMax(SmiUntag(position), int_zero); - - Label zero_length_needle(this), return_minus_1(this); - { - GotoIf(IntPtrEqual(int_zero, search_length), &zero_length_needle); - - // Check that the needle fits in the start position. - GotoIfNot(IntPtrLessThanOrEqual(search_length, - IntPtrSub(subject_length, start_position)), - &return_minus_1); - } - - // If the string pointers are identical, we can just return 0. Note that this - // implies {start_position} == 0 since we've passed the check above. - Label return_zero(this); - GotoIf(TaggedEqual(subject_string, search_string), &return_zero); - - // Try to unpack subject and search strings. Bail to runtime if either needs - // to be flattened. - ToDirectStringAssembler subject_to_direct(state(), subject_string); - ToDirectStringAssembler search_to_direct(state(), search_string); - - Label call_runtime_unchecked(this, Label::kDeferred); - - subject_to_direct.TryToDirect(&call_runtime_unchecked); - search_to_direct.TryToDirect(&call_runtime_unchecked); - - // Load pointers to string data. - const TNode subject_ptr = - subject_to_direct.PointerToData(&call_runtime_unchecked); - const TNode search_ptr = - search_to_direct.PointerToData(&call_runtime_unchecked); - - const TNode subject_offset = subject_to_direct.offset(); - const TNode search_offset = search_to_direct.offset(); - - // Like String::IndexOf, the actual matching is done by the optimized - // SearchString method in string-search.h. Dispatch based on string instance - // types, then call straight into C++ for matching. - - CSA_ASSERT(this, IntPtrGreaterThan(search_length, int_zero)); - CSA_ASSERT(this, IntPtrGreaterThanOrEqual(start_position, int_zero)); - CSA_ASSERT(this, IntPtrGreaterThanOrEqual(subject_length, start_position)); - CSA_ASSERT(this, - IntPtrLessThanOrEqual(search_length, - IntPtrSub(subject_length, start_position))); - - Label one_one(this), one_two(this), two_one(this), two_two(this); - DispatchOnStringEncodings(subject_to_direct.instance_type(), - search_to_direct.instance_type(), &one_one, - &one_two, &two_one, &two_two); - - using onebyte_t = const uint8_t; - using twobyte_t = const uc16; - - BIND(&one_one); - { - const TNode adjusted_subject_ptr = PointerToStringDataAtIndex( - subject_ptr, subject_offset, String::ONE_BYTE_ENCODING); - const TNode adjusted_search_ptr = PointerToStringDataAtIndex( - search_ptr, search_offset, String::ONE_BYTE_ENCODING); - - Label direct_memchr_call(this), generic_fast_path(this); - Branch(IntPtrEqual(search_length, IntPtrConstant(1)), &direct_memchr_call, - &generic_fast_path); - - // An additional fast path that calls directly into memchr for 1-length - // search strings. - BIND(&direct_memchr_call); - { - const TNode string_addr = - RawPtrAdd(adjusted_subject_ptr, start_position); - const TNode search_length = - IntPtrSub(subject_length, start_position); - const TNode search_byte = - ChangeInt32ToIntPtr(Load(adjusted_search_ptr)); - - const TNode memchr = - ExternalConstant(ExternalReference::libc_memchr_function()); - const TNode result_address = UncheckedCast( - CallCFunction(memchr, MachineType::Pointer(), - std::make_pair(MachineType::Pointer(), string_addr), - std::make_pair(MachineType::IntPtr(), search_byte), - std::make_pair(MachineType::UintPtr(), search_length))); - GotoIf(WordEqual(result_address, int_zero), &return_minus_1); - const TNode result_index = - IntPtrAdd(RawPtrSub(result_address, string_addr), start_position); - f_return(SmiTag(result_index)); - } - - BIND(&generic_fast_path); - { - const TNode result = CallSearchStringRaw( - adjusted_subject_ptr, subject_length, adjusted_search_ptr, - search_length, start_position); - f_return(SmiTag(result)); - } - } - - BIND(&one_two); - { - const TNode adjusted_subject_ptr = PointerToStringDataAtIndex( - subject_ptr, subject_offset, String::ONE_BYTE_ENCODING); - const TNode adjusted_search_ptr = PointerToStringDataAtIndex( - search_ptr, search_offset, String::TWO_BYTE_ENCODING); - - const TNode result = CallSearchStringRaw( - adjusted_subject_ptr, subject_length, adjusted_search_ptr, - search_length, start_position); - f_return(SmiTag(result)); - } - - BIND(&two_one); - { - const TNode adjusted_subject_ptr = PointerToStringDataAtIndex( - subject_ptr, subject_offset, String::TWO_BYTE_ENCODING); - const TNode adjusted_search_ptr = PointerToStringDataAtIndex( - search_ptr, search_offset, String::ONE_BYTE_ENCODING); - - const TNode result = CallSearchStringRaw( - adjusted_subject_ptr, subject_length, adjusted_search_ptr, - search_length, start_position); - f_return(SmiTag(result)); - } - - BIND(&two_two); - { - const TNode adjusted_subject_ptr = PointerToStringDataAtIndex( - subject_ptr, subject_offset, String::TWO_BYTE_ENCODING); - const TNode adjusted_search_ptr = PointerToStringDataAtIndex( - search_ptr, search_offset, String::TWO_BYTE_ENCODING); - - const TNode result = CallSearchStringRaw( - adjusted_subject_ptr, subject_length, adjusted_search_ptr, - search_length, start_position); - f_return(SmiTag(result)); - } - - BIND(&return_minus_1); - f_return(SmiConstant(-1)); - - BIND(&return_zero); - f_return(SmiConstant(0)); - - BIND(&zero_length_needle); - { - Comment("0-length search_string"); - f_return(SmiTag(IntPtrMin(subject_length, start_position))); - } - - BIND(&call_runtime_unchecked); - { - // Simplified version of the runtime call where the types of the arguments - // are already known due to type checks in this stub. - Comment("Call Runtime Unchecked"); - TNode result = - CAST(CallRuntime(Runtime::kStringIndexOfUnchecked, NoContextConstant(), - subject_string, search_string, position)); - f_return(result); - } -} - -// ES6 String.prototype.indexOf(searchString [, position]) -// #sec-string.prototype.indexof -// Unchecked helper for builtins lowering. -TF_BUILTIN(StringIndexOf, StringBuiltinsAssembler) { - auto receiver = Parameter(Descriptor::kReceiver); - auto search_string = Parameter(Descriptor::kSearchString); - auto position = Parameter(Descriptor::kPosition); - StringIndexOf(receiver, search_string, position, - [this](TNode result) { this->Return(result); }); -} - -// ES6 String.prototype.includes(searchString [, position]) -// #sec-string.prototype.includes -TF_BUILTIN(StringPrototypeIncludes, StringIncludesIndexOfAssembler) { - TNode argc = ChangeInt32ToIntPtr( - UncheckedParameter(Descriptor::kJSActualArgumentsCount)); - auto context = Parameter(Descriptor::kContext); - Generate(kIncludes, argc, context); -} - -// ES6 String.prototype.indexOf(searchString [, position]) -// #sec-string.prototype.indexof -TF_BUILTIN(StringPrototypeIndexOf, StringIncludesIndexOfAssembler) { - TNode argc = ChangeInt32ToIntPtr( - UncheckedParameter(Descriptor::kJSActualArgumentsCount)); - auto context = Parameter(Descriptor::kContext); - Generate(kIndexOf, argc, context); -} - -void StringIncludesIndexOfAssembler::Generate(SearchVariant variant, - TNode argc, - TNode context) { - CodeStubArguments arguments(this, argc); - const TNode receiver = arguments.GetReceiver(); - - TVARIABLE(Object, var_search_string); - TVARIABLE(Object, var_position); - Label argc_1(this), argc_2(this), call_runtime(this, Label::kDeferred), - fast_path(this); - - GotoIf(IntPtrEqual(arguments.GetLength(), IntPtrConstant(1)), &argc_1); - GotoIf(IntPtrGreaterThan(arguments.GetLength(), IntPtrConstant(1)), &argc_2); - { - Comment("0 Argument case"); - CSA_ASSERT(this, IntPtrEqual(arguments.GetLength(), IntPtrConstant(0))); - TNode undefined = UndefinedConstant(); - var_search_string = undefined; - var_position = undefined; - Goto(&call_runtime); - } - BIND(&argc_1); - { - Comment("1 Argument case"); - var_search_string = arguments.AtIndex(0); - var_position = SmiConstant(0); - Goto(&fast_path); - } - BIND(&argc_2); - { - Comment("2 Argument case"); - var_search_string = arguments.AtIndex(0); - var_position = arguments.AtIndex(1); - GotoIfNot(TaggedIsSmi(var_position.value()), &call_runtime); - Goto(&fast_path); - } - BIND(&fast_path); - { - Comment("Fast Path"); - const TNode search = var_search_string.value(); - const TNode position = CAST(var_position.value()); - GotoIf(TaggedIsSmi(receiver), &call_runtime); - GotoIf(TaggedIsSmi(search), &call_runtime); - GotoIfNot(IsString(CAST(receiver)), &call_runtime); - GotoIfNot(IsString(CAST(search)), &call_runtime); - - StringIndexOf(CAST(receiver), CAST(search), position, - [&](TNode result) { - if (variant == kIndexOf) { - arguments.PopAndReturn(result); - } else { - arguments.PopAndReturn(SelectBooleanConstant( - SmiGreaterThanOrEqual(result, SmiConstant(0)))); - } - }); - } - BIND(&call_runtime); - { - Comment("Call Runtime"); - Runtime::FunctionId runtime = variant == kIndexOf - ? Runtime::kStringIndexOf - : Runtime::kStringIncludes; - const TNode result = - CallRuntime(runtime, context, receiver, var_search_string.value(), - var_position.value()); - arguments.PopAndReturn(result); - } -} - void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( const TNode context, const TNode object, const TNode maybe_string, Handle symbol, diff --git a/src/builtins/builtins-string-gen.h b/src/builtins/builtins-string-gen.h index 5e3ee93f17..bd1390dc24 100644 --- a/src/builtins/builtins-string-gen.h +++ b/src/builtins/builtins-string-gen.h @@ -61,6 +61,29 @@ class StringBuiltinsAssembler : public CodeStubAssembler { String::Encoding from_encoding, String::Encoding to_encoding); + // Torque wrapper methods for CallSearchStringRaw for each combination of + // search and subject character widths (char8/char16). This is a workaround + // for Torque's current lack of support for extern macros with generics. + TNode SearchOneByteStringInTwoByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position); + TNode SearchOneByteStringInOneByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position); + TNode SearchTwoByteStringInTwoByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position); + TNode SearchTwoByteStringInOneByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode search_length, + const TNode start_position); + TNode SearchOneByteInOneByteString( + const TNode subject_ptr, const TNode subject_length, + const TNode search_ptr, const TNode start_position); + protected: void StringEqual_Loop(TNode lhs, TNode lhs_instance_type, MachineType lhs_type, TNode rhs, @@ -70,11 +93,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler { TNode DirectStringData(TNode string, TNode string_instance_type); - void DispatchOnStringEncodings(const TNode lhs_instance_type, - const TNode rhs_instance_type, - Label* if_one_one, Label* if_one_two, - Label* if_two_one, Label* if_two_two); - template TNode CallSearchStringRaw(const TNode subject_ptr, const TNode subject_length, @@ -82,10 +100,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler { const TNode search_length, const TNode start_position); - TNode PointerToStringDataAtIndex(TNode string_data, - TNode index, - String::Encoding encoding); - void GenerateStringEqual(TNode left, TNode right); void GenerateStringRelationalComparison(TNode left, TNode right, Operation op); @@ -93,11 +107,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler { using StringAtAccessor = std::function( TNode receiver, TNode length, TNode index)>; - void StringIndexOf(const TNode subject_string, - const TNode search_string, - const TNode position, - const std::function)>& f_return); - const TNode IndexOfDollarChar(const TNode context, const TNode string); @@ -172,18 +181,6 @@ class StringBuiltinsAssembler : public CodeStubAssembler { TNode character_count); }; -class StringIncludesIndexOfAssembler : public StringBuiltinsAssembler { - public: - explicit StringIncludesIndexOfAssembler(compiler::CodeAssemblerState* state) - : StringBuiltinsAssembler(state) {} - - protected: - enum SearchVariant { kIncludes, kIndexOf }; - - void Generate(SearchVariant variant, TNode argc, - TNode context); -}; - } // namespace internal } // namespace v8 diff --git a/src/builtins/builtins-string.tq b/src/builtins/builtins-string.tq index b7f8a671c2..4111155fd2 100644 --- a/src/builtins/builtins-string.tq +++ b/src/builtins/builtins-string.tq @@ -116,9 +116,7 @@ IfInBounds(String, uintptr, uintptr), IfOutOfBounds { const index: uintptr = Unsigned(Convert(indexSmi)); // Max string length fits Smi range, so we can do an unsigned bounds // check. - const kMaxStringLengthFitsSmi: constexpr bool = - kStringMaxLengthUintptr < kSmiMaxValue; - static_assert(kMaxStringLengthFitsSmi); + StaticAssertStringLengthFitsSmi(); if (index >= length) goto IfOutOfBounds; goto IfInBounds(string, index, length); } diff --git a/src/builtins/regexp-match.tq b/src/builtins/regexp-match.tq index 48fd8a42bf..5fca09893c 100644 --- a/src/builtins/regexp-match.tq +++ b/src/builtins/regexp-match.tq @@ -108,9 +108,7 @@ transitioning macro RegExpPrototypeMatchBody(implicit context: Context)( // On the fast path, we can be certain that lastIndex can never be // incremented to overflow the Smi range since the maximal string // length is less than the maximal Smi value. - const kMaxStringLengthFitsSmi: constexpr bool = - kStringMaxLengthUintptr < kSmiMaxValue; - static_assert(kMaxStringLengthFitsSmi); + StaticAssertStringLengthFitsSmi(); assert(TaggedIsPositiveSmi(newLastIndex)); } diff --git a/src/builtins/string-includes.tq b/src/builtins/string-includes.tq new file mode 100644 index 0000000000..7da7aa7e6d --- /dev/null +++ b/src/builtins/string-includes.tq @@ -0,0 +1,49 @@ +// 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. + +#include 'src/builtins/builtins-string-gen.h' + +namespace string { + +// https://tc39.es/ecma262/#sec-string.prototype.includes +transitioning javascript builtin +StringPrototypeIncludes(js-implicit context: NativeContext, receiver: JSAny)( + ...arguments): Boolean { + const methodName: constexpr string = 'String.prototype.includes'; + const searchString: JSAny = arguments[0]; + const position: JSAny = arguments[1]; + + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const s = ToThisString(receiver, methodName); + + // 3. Let isRegExp be ? IsRegExp(searchString). + // 4. If isRegExp is true, throw a TypeError exception. + if (regexp::IsRegExp(searchString)) { + ThrowTypeError(MessageTemplate::kFirstArgumentNotRegExp, methodName); + } + + // 5. Let searchStr be ? ToString(searchString). + const searchStr = ToString_Inline(searchString); + + // 6. Let pos be ? ToIntegerOrInfinity(position). + // 7. Assert: If position is undefined, then pos is 0. + let start: Smi = 0; + if (position != Undefined) { + // 8. Let len be the length of S. + const len = s.length_uintptr; + + // 9. Let start be the result of clamping pos between 0 and len. + StaticAssertStringLengthFitsSmi(); + start = Convert(Signed(ClampToIndexRange(position, len))); + } + + // 10. Let index be ! StringIndexOf(S, searchStr, start). + const index = StringIndexOf(s, searchStr, start); + + // 11. If index is not -1, return true. + // 12. Return false. + return index != -1 ? True : False; +} +} diff --git a/src/builtins/string-indexof.tq b/src/builtins/string-indexof.tq new file mode 100644 index 0000000000..f649d1d15e --- /dev/null +++ b/src/builtins/string-indexof.tq @@ -0,0 +1,39 @@ +// 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. + +#include 'src/builtins/builtins-string-gen.h' + +namespace string { + +// https://tc39.es/ecma262/#sec-string.prototype.indexof +transitioning javascript builtin +StringPrototypeIndexOf( + js-implicit context: NativeContext, receiver: JSAny)(...arguments): Smi { + const methodName: constexpr string = 'String.prototype.indexOf'; + const searchString: JSAny = arguments[0]; + const position: JSAny = arguments[1]; + + // 1. Let O be ? RequireObjectCoercible(this value). + // 2. Let S be ? ToString(O). + const s = ToThisString(receiver, methodName); + + // 3. Let searchStr be ? ToString(searchString). + const searchStr = ToString_Inline(searchString); + + // 4. Let pos be ? ToIntegerOrInfinity(position). + // 5. Assert: If position is undefined, then pos is 0. + let start: Smi = 0; + if (position != Undefined) { + // 6. Let len be the length of S. + const len = s.length_uintptr; + + // 7. Let start be the result of clamping pos between 0 and len. + StaticAssertStringLengthFitsSmi(); + start = Convert(Signed(ClampToIndexRange(position, len))); + } + + // 8. Let index be ! StringIndexOf(S, searchStr, start). + return StringIndexOf(s, searchStr, start); +} +} diff --git a/src/builtins/string-replaceall.tq b/src/builtins/string-replaceall.tq index 8dc7ad7ed6..cd670208ad 100644 --- a/src/builtins/string-replaceall.tq +++ b/src/builtins/string-replaceall.tq @@ -9,52 +9,6 @@ extern macro ReplaceSymbolConstant(): Symbol; extern macro StringBuiltinsAssembler::GetSubstitution( implicit context: Context)(String, Smi, Smi, String): String; -extern builtin -StringIndexOf(implicit context: Context)(String, String, Smi): Smi; - -// TODO(turbofan): This could be replaced with a fast C-call to StringSearchRaw. -macro AbstractStringIndexOf( - string: ConstSlice, searchString: ConstSlice, fromIndex: Smi): Smi { - for (let i: intptr = SmiUntag(fromIndex); - i <= string.length - searchString.length; i++) { - if (IsSubstringAt(string, searchString, i)) { - return SmiTag(i); - } - } - return -1; -} - -struct AbstractStringIndexOfFunctor { - fromIndex: Smi; -} -// Ideally, this would be a method of AbstractStringIndexOfFunctor, but -// currently methods don't support templates. -macro Call( - self: AbstractStringIndexOfFunctor, string: ConstSlice, - searchStr: ConstSlice): Smi { - return AbstractStringIndexOf(string, searchStr, self.fromIndex); -} - -macro AbstractStringIndexOf(implicit context: Context)( - string: String, searchString: String, fromIndex: Smi): Smi { - // Special case the empty string. - const searchStringLength = searchString.length_intptr; - const stringLength = string.length_intptr; - if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) { - return fromIndex; - } - - // Don't bother to search if the searchString would go past the end - // of the string. This is actually necessary because of runtime - // checks. - if (SmiUntag(fromIndex) + searchStringLength > stringLength) { - return -1; - } - - return TwoStringsToSlices( - string, searchString, AbstractStringIndexOfFunctor{fromIndex: fromIndex}); -} - transitioning macro ThrowIfNotGlobal(implicit context: Context)(searchValue: JSAny): void { let shouldThrow: bool; diff --git a/src/debug/debug-evaluate.cc b/src/debug/debug-evaluate.cc index b8d377ce84..f2413a73e6 100644 --- a/src/debug/debug-evaluate.cc +++ b/src/debug/debug-evaluate.cc @@ -308,8 +308,6 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { V(ThrowReferenceError) \ V(ThrowSymbolIteratorInvalid) \ /* Strings */ \ - V(StringIncludes) \ - V(StringIndexOf) \ V(StringReplaceOneCharWithString) \ V(StringSubstring) \ V(StringToNumber) \ @@ -351,7 +349,6 @@ bool IntrinsicHasNoSideEffect(Runtime::FunctionId id) { V(StringAdd) \ V(StringCharCodeAt) \ V(StringEqual) \ - V(StringIndexOfUnchecked) \ V(StringParseFloat) \ V(StringParseInt) \ V(SymbolDescriptiveString) \ diff --git a/src/objects/string.tq b/src/objects/string.tq index 542dddc3b9..ad845760ae 100644 --- a/src/objects/string.tq +++ b/src/objects/string.tq @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include 'src/builtins/builtins-string-gen.h' + @abstract @generateCppClass @reserveBitsInInstanceType(6) @@ -306,3 +308,87 @@ macro TwoStringsToSlices( } } } + +macro StaticAssertStringLengthFitsSmi(): void { + const kMaxStringLengthFitsSmi: constexpr bool = + kStringMaxLengthUintptr < kSmiMaxValue; + static_assert(kMaxStringLengthFitsSmi); +} + +extern macro StringBuiltinsAssembler::SearchOneByteStringInTwoByteString( + RawPtr, intptr, RawPtr, intptr, intptr): intptr; +extern macro StringBuiltinsAssembler::SearchOneByteStringInOneByteString( + RawPtr, intptr, RawPtr, intptr, intptr): intptr; +extern macro StringBuiltinsAssembler::SearchTwoByteStringInTwoByteString( + RawPtr, intptr, RawPtr, intptr, intptr): intptr; +extern macro StringBuiltinsAssembler::SearchTwoByteStringInOneByteString( + RawPtr, intptr, RawPtr, intptr, intptr): intptr; +extern macro StringBuiltinsAssembler::SearchOneByteInOneByteString( + RawPtr, intptr, RawPtr, intptr): intptr; + +macro AbstractStringIndexOf( + subject: RawPtr, subjectLen: intptr, search: RawPtr, + searchLen: intptr, fromIndex: intptr): intptr { + return SearchOneByteStringInTwoByteString( + subject, subjectLen, search, searchLen, fromIndex); +} +macro AbstractStringIndexOf( + subject: RawPtr, subjectLen: intptr, search: RawPtr, + searchLen: intptr, fromIndex: intptr): intptr { + if (searchLen == 1) { + return SearchOneByteInOneByteString(subject, subjectLen, search, fromIndex); + } + return SearchOneByteStringInOneByteString( + subject, subjectLen, search, searchLen, fromIndex); +} +macro AbstractStringIndexOf( + subject: RawPtr, subjectLen: intptr, search: RawPtr, + searchLen: intptr, fromIndex: intptr): intptr { + return SearchTwoByteStringInTwoByteString( + subject, subjectLen, search, searchLen, fromIndex); +} +macro AbstractStringIndexOf( + subject: RawPtr, subjectLen: intptr, search: RawPtr, + searchLen: intptr, fromIndex: intptr): intptr { + return SearchTwoByteStringInOneByteString( + subject, subjectLen, search, searchLen, fromIndex); +} + +struct AbstractStringIndexOfFunctor { + fromIndex: Smi; +} +// Ideally, this would be a method of AbstractStringIndexOfFunctor, but +// currently methods don't support templates. +macro Call( + self: AbstractStringIndexOfFunctor, string: ConstSlice, + searchStr: ConstSlice): Smi { + return Convert(AbstractStringIndexOf( + string.GCUnsafeStartPointer(), string.length, + searchStr.GCUnsafeStartPointer(), searchStr.length, + Convert(self.fromIndex))); +} + +macro AbstractStringIndexOf(implicit context: Context)( + string: String, searchString: String, fromIndex: Smi): Smi { + // Special case the empty string. + const searchStringLength = searchString.length_intptr; + const stringLength = string.length_intptr; + if (searchStringLength == 0 && SmiUntag(fromIndex) <= stringLength) { + return fromIndex; + } + + // Don't bother to search if the searchString would go past the end + // of the string. This is actually necessary because of runtime + // checks. + if (SmiUntag(fromIndex) + searchStringLength > stringLength) { + return -1; + } + + return TwoStringsToSlices( + string, searchString, AbstractStringIndexOfFunctor{fromIndex: fromIndex}); +} + +builtin StringIndexOf(implicit context: Context)( + s: String, searchString: String, start: Smi): Smi { + return AbstractStringIndexOf(s, searchString, SmiMax(start, 0)); +} diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc index 2e761c4bfc..0acf6334d4 100644 --- a/src/runtime/runtime-strings.cc +++ b/src/runtime/runtime-strings.cc @@ -139,72 +139,6 @@ RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) { return isolate->StackOverflow(); } -// ES6 #sec-string.prototype.includes -// String.prototype.includes(searchString [, position]) -RUNTIME_FUNCTION(Runtime_StringIncludes) { - HandleScope scope(isolate); - DCHECK_EQ(3, args.length()); - - Handle receiver = args.at(0); - if (receiver->IsNullOrUndefined(isolate)) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined, - isolate->factory()->NewStringFromAsciiChecked( - "String.prototype.includes"))); - } - Handle receiver_string; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, receiver_string, - Object::ToString(isolate, receiver)); - - // Check if the search string is a regExp and fail if it is. - Handle search = args.at(1); - Maybe is_reg_exp = RegExpUtils::IsRegExp(isolate, search); - if (is_reg_exp.IsNothing()) { - DCHECK(isolate->has_pending_exception()); - return ReadOnlyRoots(isolate).exception(); - } - if (is_reg_exp.FromJust()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kFirstArgumentNotRegExp, - isolate->factory()->NewStringFromStaticChars( - "String.prototype.includes"))); - } - Handle search_string; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, search_string, - Object::ToString(isolate, args.at(1))); - Handle position; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, position, - Object::ToInteger(isolate, args.at(2))); - - uint32_t index = receiver_string->ToValidIndex(*position); - int index_in_str = - String::IndexOf(isolate, receiver_string, search_string, index); - return *isolate->factory()->ToBoolean(index_in_str != -1); -} - -// ES6 #sec-string.prototype.indexof -// String.prototype.indexOf(searchString [, position]) -RUNTIME_FUNCTION(Runtime_StringIndexOf) { - HandleScope scope(isolate); - DCHECK_EQ(3, args.length()); - return String::IndexOf(isolate, args.at(0), args.at(1), args.at(2)); -} - -// ES6 #sec-string.prototype.indexof -// String.prototype.indexOf(searchString, position) -// Fast version that assumes that does not perform conversions of the incoming -// arguments. -RUNTIME_FUNCTION(Runtime_StringIndexOfUnchecked) { - HandleScope scope(isolate); - DCHECK_EQ(3, args.length()); - Handle receiver_string = args.at(0); - Handle search_string = args.at(1); - int index = std::min(std::max(args.smi_at(2), 0), receiver_string->length()); - - return Smi::FromInt(String::IndexOf(isolate, receiver_string, search_string, - static_cast(index))); -} - RUNTIME_FUNCTION(Runtime_StringLastIndexOf) { HandleScope handle_scope(isolate); return String::LastIndexOf(isolate, args.at(0), args.at(1), diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index af55e88ed0..51a8ff302f 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -431,9 +431,6 @@ namespace internal { F(StringEscapeQuotes, 1, 1) \ F(StringGreaterThan, 2, 1) \ F(StringGreaterThanOrEqual, 2, 1) \ - F(StringIncludes, 3, 1) \ - F(StringIndexOf, 3, 1) \ - F(StringIndexOfUnchecked, 3, 1) \ F(StringLastIndexOf, 2, 1) \ F(StringLessThan, 2, 1) \ F(StringLessThanOrEqual, 2, 1) \ diff --git a/tools/v8heapconst.py b/tools/v8heapconst.py index cfed872b2f..136e039005 100644 --- a/tools/v8heapconst.py +++ b/tools/v8heapconst.py @@ -465,27 +465,27 @@ KNOWN_OBJECTS = { ("old_space", 0x029a1): "StringSplitCache", ("old_space", 0x02da9): "RegExpMultipleCache", ("old_space", 0x031b1): "BuiltinsConstantsTable", - ("old_space", 0x035a9): "AsyncFunctionAwaitRejectSharedFun", - ("old_space", 0x035cd): "AsyncFunctionAwaitResolveSharedFun", - ("old_space", 0x035f1): "AsyncGeneratorAwaitRejectSharedFun", - ("old_space", 0x03615): "AsyncGeneratorAwaitResolveSharedFun", - ("old_space", 0x03639): "AsyncGeneratorYieldResolveSharedFun", - ("old_space", 0x0365d): "AsyncGeneratorReturnResolveSharedFun", - ("old_space", 0x03681): "AsyncGeneratorReturnClosedRejectSharedFun", - ("old_space", 0x036a5): "AsyncGeneratorReturnClosedResolveSharedFun", - ("old_space", 0x036c9): "AsyncIteratorValueUnwrapSharedFun", - ("old_space", 0x036ed): "PromiseAllResolveElementSharedFun", - ("old_space", 0x03711): "PromiseAllSettledResolveElementSharedFun", - ("old_space", 0x03735): "PromiseAllSettledRejectElementSharedFun", - ("old_space", 0x03759): "PromiseAnyRejectElementSharedFun", - ("old_space", 0x0377d): "PromiseCapabilityDefaultRejectSharedFun", - ("old_space", 0x037a1): "PromiseCapabilityDefaultResolveSharedFun", - ("old_space", 0x037c5): "PromiseCatchFinallySharedFun", - ("old_space", 0x037e9): "PromiseGetCapabilitiesExecutorSharedFun", - ("old_space", 0x0380d): "PromiseThenFinallySharedFun", - ("old_space", 0x03831): "PromiseThrowerFinallySharedFun", - ("old_space", 0x03855): "PromiseValueThunkFinallySharedFun", - ("old_space", 0x03879): "ProxyRevokeSharedFun", + ("old_space", 0x035b1): "AsyncFunctionAwaitRejectSharedFun", + ("old_space", 0x035d5): "AsyncFunctionAwaitResolveSharedFun", + ("old_space", 0x035f9): "AsyncGeneratorAwaitRejectSharedFun", + ("old_space", 0x0361d): "AsyncGeneratorAwaitResolveSharedFun", + ("old_space", 0x03641): "AsyncGeneratorYieldResolveSharedFun", + ("old_space", 0x03665): "AsyncGeneratorReturnResolveSharedFun", + ("old_space", 0x03689): "AsyncGeneratorReturnClosedRejectSharedFun", + ("old_space", 0x036ad): "AsyncGeneratorReturnClosedResolveSharedFun", + ("old_space", 0x036d1): "AsyncIteratorValueUnwrapSharedFun", + ("old_space", 0x036f5): "PromiseAllResolveElementSharedFun", + ("old_space", 0x03719): "PromiseAllSettledResolveElementSharedFun", + ("old_space", 0x0373d): "PromiseAllSettledRejectElementSharedFun", + ("old_space", 0x03761): "PromiseAnyRejectElementSharedFun", + ("old_space", 0x03785): "PromiseCapabilityDefaultRejectSharedFun", + ("old_space", 0x037a9): "PromiseCapabilityDefaultResolveSharedFun", + ("old_space", 0x037cd): "PromiseCatchFinallySharedFun", + ("old_space", 0x037f1): "PromiseGetCapabilitiesExecutorSharedFun", + ("old_space", 0x03815): "PromiseThenFinallySharedFun", + ("old_space", 0x03839): "PromiseThrowerFinallySharedFun", + ("old_space", 0x0385d): "PromiseValueThunkFinallySharedFun", + ("old_space", 0x03881): "ProxyRevokeSharedFun", } # Lower 32 bits of first page addresses for various heap spaces.