Commit 65d2c4b4 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] uniform flattening and string access in Torque

Port String::Flatten to Torque (using a fast C call for the
non-allocating part) and provide fast and easy access to sequential
string data in Torque: GetStringData() flattens if necessary and
computes slices that allow direct access.

Applications: String.prototype.replaceAll, String.prototype.endsWith,
  and String.prototype.beginsWith now use GetStringData() and direct
  slice access instead of the slow StringCharCodeAt and they no
  longer bail out to the runtime for flattening.

Drive-by changes:
  - Expose String instance type bits as bitfields and enums in Torque.
  - Fix method lookup in Torque to include superclass methods.
  - Use char8 and char16 types in more places.
  - Allow fast C calls with void return type.
  - Add Torque macros to create subslices.
  - Add no-GC scopes to runtime functions loading external string data.


Bug: v8:7793
Change-Id: I763b9b24212770307c9b2fe9f070f21f65d68d58
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2565515
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71611}
parent fb37749a
...@@ -101,7 +101,7 @@ type uint16 extends uint31 ...@@ -101,7 +101,7 @@ type uint16 extends uint31
type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t'; type int8 extends int16 generates 'TNode<Int8T>' constexpr 'int8_t';
type uint8 extends uint16 type uint8 extends uint16
generates 'TNode<Uint8T>' constexpr 'uint8_t'; generates 'TNode<Uint8T>' constexpr 'uint8_t';
type char8 extends int8 constexpr 'char'; type char8 extends uint8 constexpr 'char';
type char16 extends uint16 constexpr 'char16_t'; type char16 extends uint16 constexpr 'char16_t';
type int64 generates 'TNode<Int64T>' constexpr 'int64_t'; type int64 generates 'TNode<Int64T>' constexpr 'int64_t';
type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t'; type intptr generates 'TNode<IntPtrT>' constexpr 'intptr_t';
...@@ -626,7 +626,6 @@ extern macro ToObject_Inline(Context, JSAny): JSReceiver; ...@@ -626,7 +626,6 @@ extern macro ToObject_Inline(Context, JSAny): JSReceiver;
extern macro IsUndefined(Object): bool; extern macro IsUndefined(Object): bool;
extern macro IsNullOrUndefined(Object): bool; extern macro IsNullOrUndefined(Object): bool;
extern macro IsString(HeapObject): bool; extern macro IsString(HeapObject): bool;
extern macro IsSeqOneByteString(HeapObject): bool;
extern transitioning builtin NonPrimitiveToPrimitive_String( extern transitioning builtin NonPrimitiveToPrimitive_String(
Context, JSAny): JSPrimitive; Context, JSAny): JSPrimitive;
extern transitioning builtin NonPrimitiveToPrimitive_Default( extern transitioning builtin NonPrimitiveToPrimitive_Default(
...@@ -655,9 +654,9 @@ extern macro LoadBufferIntptr(RawPtr, constexpr int32): intptr; ...@@ -655,9 +654,9 @@ extern macro LoadBufferIntptr(RawPtr, constexpr int32): intptr;
extern runtime StringEqual(Context, String, String): Oddball; extern runtime StringEqual(Context, String, String): Oddball;
extern builtin StringLessThan(Context, String, String): Boolean; extern builtin StringLessThan(Context, String, String): Boolean;
extern macro StringCharCodeAt(String, uintptr): int32; extern macro StringCharCodeAt(String, uintptr): char16;
extern runtime StringCompareSequence(Context, String, String, Number): Boolean; extern macro StringFromSingleCharCode(char8): String;
extern macro StringFromSingleCharCode(int32): String; extern macro StringFromSingleCharCode(char16): String;
extern macro NumberToString(Number): String; extern macro NumberToString(Number): String;
extern macro StringToNumber(String): Number; extern macro StringToNumber(String): Number;
......
...@@ -40,12 +40,6 @@ TNode<RawPtrT> StringBuiltinsAssembler::DirectStringData( ...@@ -40,12 +40,6 @@ TNode<RawPtrT> StringBuiltinsAssembler::DirectStringData(
BIND(&if_external); BIND(&if_external);
{ {
// This is only valid for ExternalStrings where the resource data
// pointer is cached (i.e. no uncached external strings).
CSA_ASSERT(this, Word32NotEqual(
Word32And(string_instance_type,
Int32Constant(kUncachedExternalStringMask)),
Int32Constant(kUncachedExternalStringTag)));
var_data = LoadExternalStringResourceDataPtr(CAST(string)); var_data = LoadExternalStringResourceDataPtr(CAST(string));
Goto(&if_join); Goto(&if_join);
} }
......
...@@ -139,7 +139,7 @@ transitioning javascript builtin StringPrototypeCharAt( ...@@ -139,7 +139,7 @@ transitioning javascript builtin StringPrototypeCharAt(
GenerateStringAt(receiver, position, 'String.prototype.charAt') GenerateStringAt(receiver, position, 'String.prototype.charAt')
otherwise IfInBounds, IfOutOfBounds; otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) { } label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index); const code: char16 = StringCharCodeAt(string, index);
return StringFromSingleCharCode(code); return StringFromSingleCharCode(code);
} label IfOutOfBounds { } label IfOutOfBounds {
return kEmptyString; return kEmptyString;
...@@ -154,7 +154,7 @@ transitioning javascript builtin StringPrototypeCharCodeAt( ...@@ -154,7 +154,7 @@ transitioning javascript builtin StringPrototypeCharCodeAt(
GenerateStringAt(receiver, position, 'String.prototype.charCodeAt') GenerateStringAt(receiver, position, 'String.prototype.charCodeAt')
otherwise IfInBounds, IfOutOfBounds; otherwise IfInBounds, IfOutOfBounds;
} label IfInBounds(string: String, index: uintptr, _length: uintptr) { } label IfInBounds(string: String, index: uintptr, _length: uintptr) {
const code: int32 = StringCharCodeAt(string, index); const code: uint32 = StringCharCodeAt(string, index);
return Convert<Smi>(code); return Convert<Smi>(code);
} label IfOutOfBounds { } label IfOutOfBounds {
return kNaN; return kNaN;
...@@ -250,7 +250,7 @@ transitioning builtin StringAddConvertRight(implicit context: Context)( ...@@ -250,7 +250,7 @@ transitioning builtin StringAddConvertRight(implicit context: Context)(
builtin StringCharAt(implicit context: Context)( builtin StringCharAt(implicit context: Context)(
receiver: String, position: uintptr): String { receiver: String, position: uintptr): String {
// Load the character code at the {position} from the {receiver}. // Load the character code at the {position} from the {receiver}.
const code: int32 = StringCharCodeAt(receiver, position); const code: char16 = StringCharCodeAt(receiver, position);
// And return the single character string with only that {code} // And return the single character string with only that {code}
return StringFromSingleCharCode(code); return StringFromSingleCharCode(code);
} }
......
...@@ -404,7 +404,7 @@ Cast<PrivateSymbol>(o: HeapObject): PrivateSymbol labels CastError { ...@@ -404,7 +404,7 @@ Cast<PrivateSymbol>(o: HeapObject): PrivateSymbol labels CastError {
return Cast<PrivateSymbol>(s) otherwise CastError; return Cast<PrivateSymbol>(s) otherwise CastError;
} }
Cast<DirectString>(o: HeapObject): DirectString Cast<DirectString>(o: String): DirectString
labels CastError { labels CastError {
return TaggedToDirectString(o) otherwise CastError; return TaggedToDirectString(o) otherwise CastError;
} }
...@@ -538,11 +538,112 @@ Cast<FastJSArrayForReadWithNoCustomIteration>(implicit context: Context)( ...@@ -538,11 +538,112 @@ Cast<FastJSArrayForReadWithNoCustomIteration>(implicit context: Context)(
return %RawDownCast<FastJSArrayForReadWithNoCustomIteration>(a); return %RawDownCast<FastJSArrayForReadWithNoCustomIteration>(a);
} }
macro Cast<T: type>(o: String): T labels CastError;
Cast<SeqOneByteString>(o: HeapObject): SeqOneByteString labels CastError { Cast<SeqOneByteString>(o: HeapObject): SeqOneByteString labels CastError {
if (!IsSeqOneByteString(o)) goto CastError; return Cast<SeqOneByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<SeqOneByteString>(o: String): SeqOneByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation == StringRepresentationTag::kSeqStringTag &
instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<SeqOneByteString>(o); return %RawDownCast<SeqOneByteString>(o);
} }
Cast<SeqTwoByteString>(o: HeapObject): SeqTwoByteString labels CastError {
return Cast<SeqTwoByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<SeqTwoByteString>(o: String): SeqTwoByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation == StringRepresentationTag::kSeqStringTag &
!instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<SeqTwoByteString>(o);
}
Cast<ThinString>(o: HeapObject): ThinString labels CastError {
return Cast<ThinString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ThinString>(o: String): ThinString labels CastError {
const instanceType = o.StringInstanceType();
if (instanceType.representation != StringRepresentationTag::kThinStringTag) {
goto CastError;
}
return %RawDownCast<ThinString>(o);
}
Cast<ConsString>(o: HeapObject): ConsString labels CastError {
return Cast<ConsString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ConsString>(o: String): ConsString labels CastError {
const instanceType = o.StringInstanceType();
if (instanceType.representation != StringRepresentationTag::kConsStringTag) {
goto CastError;
}
return %RawDownCast<ConsString>(o);
}
Cast<SlicedString>(o: HeapObject): SlicedString labels CastError {
return Cast<SlicedString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<SlicedString>(o: String): SlicedString labels CastError {
const instanceType = o.StringInstanceType();
if (instanceType.representation !=
StringRepresentationTag::kSlicedStringTag) {
goto CastError;
}
return %RawDownCast<SlicedString>(o);
}
Cast<ExternalOneByteString>(o: HeapObject):
ExternalOneByteString labels CastError {
return Cast<ExternalOneByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ExternalOneByteString>(o: String): ExternalOneByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation ==
StringRepresentationTag::kExternalStringTag &
instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<ExternalOneByteString>(o);
}
Cast<ExternalTwoByteString>(o: HeapObject):
ExternalTwoByteString labels CastError {
return Cast<ExternalTwoByteString>(Cast<String>(o) otherwise CastError)
otherwise CastError;
}
Cast<ExternalTwoByteString>(o: String): ExternalTwoByteString labels CastError {
const instanceType = o.StringInstanceType();
// Using & instead of && enables Turbofan to merge the two checks into one.
if (!(instanceType.representation ==
StringRepresentationTag::kExternalStringTag &
!instanceType.is_one_byte)) {
goto CastError;
}
return %RawDownCast<ExternalTwoByteString>(o);
}
Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null Cast<JSReceiver|Null>(o: HeapObject): JSReceiver|Null
labels CastError { labels CastError {
typeswitch (o) { typeswitch (o) {
......
...@@ -58,10 +58,7 @@ FromConstexpr<int8, constexpr int31>(i: constexpr int31): int8 { ...@@ -58,10 +58,7 @@ FromConstexpr<int8, constexpr int31>(i: constexpr int31): int8 {
return %RawDownCast<int8>(i); return %RawDownCast<int8>(i);
} }
FromConstexpr<char8, constexpr int31>(i: constexpr int31): char8 { FromConstexpr<char8, constexpr int31>(i: constexpr int31): char8 {
const i: int32 = i; return %RawDownCast<char8>(FromConstexpr<uint8>(i));
static_assert(i <= 255);
static_assert(0 <= i);
return %RawDownCast<char8>(i);
} }
FromConstexpr<Number, constexpr Smi>(s: constexpr Smi): Number { FromConstexpr<Number, constexpr Smi>(s: constexpr Smi): Number {
return SmiConstant(s); return SmiConstant(s);
......
...@@ -63,8 +63,9 @@ transitioning macro ThisNumberValue(implicit context: Context)( ...@@ -63,8 +63,9 @@ transitioning macro ThisNumberValue(implicit context: Context)(
macro ToCharCode(input: int32): char8 { macro ToCharCode(input: int32): char8 {
assert(0 <= input && input < 36); assert(0 <= input && input < 36);
return input < 10 ? %RawDownCast<char8>(input + kAsciiZero) : return input < 10 ?
%RawDownCast<char8>(input - 10 + kAsciiLowerCaseA); %RawDownCast<char8>(Unsigned(input + kAsciiZero)) :
%RawDownCast<char8>(Unsigned(input - 10 + kAsciiLowerCaseA));
} }
@export @export
...@@ -92,7 +93,7 @@ macro NumberToStringSmi(x: int32, radix: int32): String labels Slow { ...@@ -92,7 +93,7 @@ macro NumberToStringSmi(x: int32, radix: int32): String labels Slow {
length = length + 1; length = length + 1;
} }
assert(length > 0); assert(length > 0);
const strSeq: SeqOneByteString = AllocateSeqOneByteString(Unsigned(length)); const strSeq = AllocateNonEmptySeqOneByteString(Unsigned(length));
let cursor: intptr = Convert<intptr>(length) - 1; let cursor: intptr = Convert<intptr>(length) - 1;
while (n > 0) { while (n > 0) {
const digit: int32 = n % radix; const digit: int32 = n % radix;
......
...@@ -3,25 +3,47 @@ ...@@ -3,25 +3,47 @@
// found in the LICENSE file. // found in the LICENSE file.
namespace string { namespace string {
macro TryFastStringCompareSequence(
string: String, searchStr: String, start: uintptr,
searchLength: uintptr): Boolean labels Slow {
const directString = Cast<DirectString>(string) otherwise Slow;
const directSearchStr = Cast<DirectString>(searchStr) otherwise Slow;
let searchIndex: uintptr = 0; // TODO(tebbi): This could be replaced with a fast C-call to
let stringIndex: uintptr = start; // CompareCharsUnsigned.
macro IsSubstringAt<A: type, B: type>(
string: ConstSlice<A>, searchStr: ConstSlice<B>, start: intptr): bool {
const subslice =
Subslice(string, start, searchStr.length) otherwise return false;
let stringIterator = subslice.Iterator();
let searchIterator = searchStr.Iterator();
while (searchIndex < searchLength) { while (true) {
if (StringCharCodeAt(directSearchStr, searchIndex) != const searchChar = searchIterator.Next() otherwise return true;
StringCharCodeAt(directString, stringIndex)) { const stringChar = stringIterator.Next() otherwise unreachable;
return False; if (searchChar != stringChar) {
return false;
} }
}
VerifiedUnreachable();
}
searchIndex++; macro IsSubstringAt(string: String, searchStr: String, start: intptr): bool {
stringIndex++; // TODO(tebbi): Avoid repeating this verbose pattern.
try {
GetStringData(string) otherwise FirstOneByte, FirstTwoByte;
} label FirstOneByte(string: ConstSlice<char8>) {
try {
GetStringData(searchStr) otherwise SecondOneByte, SecondTwoByte;
} label SecondOneByte(searchStr: ConstSlice<char8>) {
return IsSubstringAt(string, searchStr, start);
} label SecondTwoByte(searchStr: ConstSlice<char16>) {
return IsSubstringAt(string, searchStr, start);
}
} label FirstTwoByte(string: ConstSlice<char16>) {
try {
GetStringData(searchStr) otherwise SecondOneByte, SecondTwoByte;
} label SecondOneByte(searchStr: ConstSlice<char8>) {
return IsSubstringAt(string, searchStr, start);
} label SecondTwoByte(searchStr: ConstSlice<char16>) {
return IsSubstringAt(string, searchStr, start);
}
} }
return True;
} }
// https://tc39.github.io/ecma262/#sec-string.prototype.endswith // https://tc39.github.io/ecma262/#sec-string.prototype.endswith
...@@ -58,23 +80,15 @@ transitioning javascript builtin StringPrototypeEndsWith( ...@@ -58,23 +80,15 @@ transitioning javascript builtin StringPrototypeEndsWith(
const searchLength: uintptr = searchStr.length_uintptr; const searchLength: uintptr = searchStr.length_uintptr;
// 10. Let start be end - searchLength. // 10. Let start be end - searchLength.
const start: uintptr = end - searchLength; const start = Signed(end - searchLength);
// 11. If start is less than 0, return false. // 11. If start is less than 0, return false.
if (Signed(start) < 0) return False; if (start < 0) return False;
// 12. If the sequence of code units of S starting at start of length // 12. If the sequence of code units of S starting at start of length
// searchLength is the same as the full code unit sequence of searchStr, // searchLength is the same as the full code unit sequence of searchStr,
// return true. // return true.
// 13. Otherwise, return false. // 13. Otherwise, return false.
try { return Convert<Boolean>(IsSubstringAt(string, searchStr, start));
// Fast Path: If both strings are direct and relevant indices are Smis.
return TryFastStringCompareSequence(string, searchStr, start, searchLength)
otherwise Slow;
} label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringCompareSequence(
context, string, searchStr, Convert<Number>(start));
}
} }
} }
...@@ -13,25 +13,13 @@ extern macro StringBuiltinsAssembler::GetSubstitution( ...@@ -13,25 +13,13 @@ extern macro StringBuiltinsAssembler::GetSubstitution(
extern builtin extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi; StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
macro TryFastAbstractStringIndexOf(implicit context: Context)( // TODO(tebbi): This could be replaced with a fast C-call to StringSearchRaw.
string: String, searchString: String, fromIndex: Smi): Smi labels Slow { macro AbstractStringIndexOf<A: type, B: type>(
const stringLen = string.length_uintptr; string: ConstSlice<A>, searchString: ConstSlice<B>, fromIndex: Smi): Smi {
const searchLen = searchString.length_uintptr; for (let i: intptr = SmiUntag(fromIndex);
const directString = Cast<DirectString>(string) otherwise Slow; i <= string.length - searchString.length; i++) {
const directSearchStr = Cast<DirectString>(searchString) otherwise Slow; if (IsSubstringAt(string, searchString, i)) {
const fromIndexUint = Unsigned(SmiUntag(fromIndex)); return SmiTag(i);
for (let i: uintptr = fromIndexUint; i < stringLen; i++) {
let j = i;
let k: uintptr = 0;
while (j < stringLen && k < searchLen &&
StringCharCodeAt(directString, j) ==
StringCharCodeAt(directSearchStr, k)) {
j++;
k++;
}
if (k == searchLen) {
return SmiTag(Signed(i));
} }
} }
return -1; return -1;
...@@ -54,18 +42,23 @@ macro AbstractStringIndexOf(implicit context: Context)( ...@@ -54,18 +42,23 @@ macro AbstractStringIndexOf(implicit context: Context)(
} }
try { try {
return TryFastAbstractStringIndexOf(string, searchString, fromIndex) GetStringData(string) otherwise FirstOneByte, FirstTwoByte;
otherwise Slow; } label FirstOneByte(stringData: ConstSlice<char8>) {
} label Slow { try {
for (let i: intptr = SmiUntag(fromIndex); GetStringData(searchString) otherwise SecondOneByte, SecondTwoByte;
i + searchStringLength <= stringLength; i++) { } label SecondOneByte(searchStringData: ConstSlice<char8>) {
if (StringCompareSequence( return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
context, string, searchString, Convert<Number>(SmiTag(i))) == } label SecondTwoByte(searchStringData: ConstSlice<char16>) {
True) { return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
return SmiTag(i); }
} } label FirstTwoByte(stringData: ConstSlice<char16>) {
try {
GetStringData(searchString) otherwise SecondOneByte, SecondTwoByte;
} label SecondOneByte(searchStringData: ConstSlice<char8>) {
return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
} label SecondTwoByte(searchStringData: ConstSlice<char16>) {
return AbstractStringIndexOf(stringData, searchStringData, fromIndex);
} }
return -1;
} }
} }
......
...@@ -47,14 +47,6 @@ transitioning javascript builtin StringPrototypeStartsWith( ...@@ -47,14 +47,6 @@ transitioning javascript builtin StringPrototypeStartsWith(
// searchLength is the same as the full code unit sequence of searchStr, // searchLength is the same as the full code unit sequence of searchStr,
// return true. // return true.
// 13. Otherwise, return false. // 13. Otherwise, return false.
try { return Convert<Boolean>(IsSubstringAt(string, searchStr, Signed(start)));
// Fast Path: If both strings are direct and relevant indices are Smis.
return TryFastStringCompareSequence(string, searchStr, start, searchLength)
otherwise Slow;
} label Slow {
// Slow Path: If either of the string is indirect, bail into runtime.
return StringCompareSequence(
context, string, searchStr, Convert<Number>(start));
}
} }
} }
...@@ -13,41 +13,41 @@ extern enum TrimMode extends uint31 constexpr 'String::TrimMode' { ...@@ -13,41 +13,41 @@ extern enum TrimMode extends uint31 constexpr 'String::TrimMode' {
} }
@export @export
macro IsWhiteSpaceOrLineTerminator(charCode: int32): bool { macro IsWhiteSpaceOrLineTerminator(charCode: char16): bool {
// 0x0020 - SPACE (Intentionally out of order to fast path a commmon case) // 0x0020 - SPACE (Intentionally out of order to fast path a commmon case)
if (charCode == Int32Constant(0x0020)) { if (charCode == 0x0020) {
return true; return true;
} }
// 0x0009 - HORIZONTAL TAB // 0x0009 - HORIZONTAL TAB
if (charCode < Int32Constant(0x0009)) { if (charCode < 0x0009) {
return false; return false;
} }
// 0x000A - LINE FEED OR NEW LINE // 0x000A - LINE FEED OR NEW LINE
// 0x000B - VERTICAL TAB // 0x000B - VERTICAL TAB
// 0x000C - FORMFEED // 0x000C - FORMFEED
// 0x000D - HORIZONTAL TAB // 0x000D - HORIZONTAL TAB
if (charCode <= Int32Constant(0x000D)) { if (charCode <= 0x000D) {
return true; return true;
} }
// Common Non-whitespace characters // Common Non-whitespace characters
if (charCode < Int32Constant(0x00A0)) { if (charCode < 0x00A0) {
return false; return false;
} }
// 0x00A0 - NO-BREAK SPACE // 0x00A0 - NO-BREAK SPACE
if (charCode == Int32Constant(0x00A0)) { if (charCode == 0x00A0) {
return true; return true;
} }
// 0x1680 - Ogham Space Mark // 0x1680 - Ogham Space Mark
if (charCode == Int32Constant(0x1680)) { if (charCode == 0x1680) {
return true; return true;
} }
// 0x2000 - EN QUAD // 0x2000 - EN QUAD
if (charCode < Int32Constant(0x2000)) { if (charCode < 0x2000) {
return false; return false;
} }
// 0x2001 - EM QUAD // 0x2001 - EM QUAD
...@@ -60,32 +60,32 @@ macro IsWhiteSpaceOrLineTerminator(charCode: int32): bool { ...@@ -60,32 +60,32 @@ macro IsWhiteSpaceOrLineTerminator(charCode: int32): bool {
// 0x2008 - PUNCTUATION SPACE // 0x2008 - PUNCTUATION SPACE
// 0x2009 - THIN SPACE // 0x2009 - THIN SPACE
// 0x200A - HAIR SPACE // 0x200A - HAIR SPACE
if (charCode <= Int32Constant(0x200A)) { if (charCode <= 0x200A) {
return true; return true;
} }
// 0x2028 - LINE SEPARATOR // 0x2028 - LINE SEPARATOR
if (charCode == Int32Constant(0x2028)) { if (charCode == 0x2028) {
return true; return true;
} }
// 0x2029 - PARAGRAPH SEPARATOR // 0x2029 - PARAGRAPH SEPARATOR
if (charCode == Int32Constant(0x2029)) { if (charCode == 0x2029) {
return true; return true;
} }
// 0x202F - NARROW NO-BREAK SPACE // 0x202F - NARROW NO-BREAK SPACE
if (charCode == Int32Constant(0x202F)) { if (charCode == 0x202F) {
return true; return true;
} }
// 0x205F - MEDIUM MATHEMATICAL SPACE // 0x205F - MEDIUM MATHEMATICAL SPACE
if (charCode == Int32Constant(0x205F)) { if (charCode == 0x205F) {
return true; return true;
} }
// 0xFEFF - BYTE ORDER MARK // 0xFEFF - BYTE ORDER MARK
if (charCode == Int32Constant(0xFEFF)) { if (charCode == 0xFEFF) {
return true; return true;
} }
// 0x3000 - IDEOGRAPHIC SPACE // 0x3000 - IDEOGRAPHIC SPACE
if (charCode == Int32Constant(0x3000)) { if (charCode == 0x3000) {
return true; return true;
} }
......
...@@ -7,6 +7,23 @@ ...@@ -7,6 +7,23 @@
type MutableSlice<T: type> extends torque_internal::Slice<T, &T>; type MutableSlice<T: type> extends torque_internal::Slice<T, &T>;
type ConstSlice<T: type> extends torque_internal::Slice<T, const &T>; type ConstSlice<T: type> extends torque_internal::Slice<T, const &T>;
macro Subslice<T: type>(slice: ConstSlice<T>, start: intptr, length: intptr):
ConstSlice<T>labels OutOfBounds {
if (Unsigned(length) > Unsigned(slice.length)) goto OutOfBounds;
if (Unsigned(start) > Unsigned(slice.length - length)) goto OutOfBounds;
const offset = slice.offset + torque_internal::TimesSizeOf<T>(start);
return torque_internal::unsafe::NewConstSlice<T>(
slice.object, offset, length);
}
macro Subslice<T: type>(slice: MutableSlice<T>, start: intptr, length: intptr):
MutableSlice<T>labels OutOfBounds {
if (Unsigned(length) > Unsigned(slice.length)) goto OutOfBounds;
if (Unsigned(start) > Unsigned(slice.length - length)) goto OutOfBounds;
const offset = slice.offset + torque_internal::TimesSizeOf<T>(start);
return torque_internal::unsafe::NewMutableSlice<T>(
slice.object, offset, length);
}
namespace torque_internal { namespace torque_internal {
// Unsafe is a marker that we require to be passed when calling internal APIs // Unsafe is a marker that we require to be passed when calling internal APIs
// that might lead to unsoundness when used incorrectly. Unsafe markers should // that might lead to unsoundness when used incorrectly. Unsafe markers should
...@@ -17,6 +34,10 @@ struct Unsafe {} ...@@ -17,6 +34,10 @@ struct Unsafe {}
// of the pointer, not of the instance. // of the pointer, not of the instance.
intrinsic %SizeOf<T: type>(): constexpr int31; intrinsic %SizeOf<T: type>(): constexpr int31;
macro TimesSizeOf<T: type>(i: intptr): intptr {
return i * %SizeOf<T>();
}
struct Reference<T: type> { struct Reference<T: type> {
const object: HeapObject|TaggedZeroPattern; const object: HeapObject|TaggedZeroPattern;
const offset: intptr; const offset: intptr;
...@@ -43,13 +64,17 @@ macro ReferenceCast<T: type, U: type>(ref:&U):&T { ...@@ -43,13 +64,17 @@ macro ReferenceCast<T: type, U: type>(ref:&U):&T {
UnsafeCast<T>(*ref); UnsafeCast<T>(*ref);
return ref; return ref;
} }
extern macro GCUnsafeReferenceToRawPtr(
HeapObject | TaggedZeroPattern, intptr): RawPtr;
} // namespace unsafe } // namespace unsafe
struct Slice<T: type, Reference: type> { struct Slice<T: type, Reference: type> {
macro TryAtIndex(index: intptr): Reference labels OutOfBounds { macro TryAtIndex(index: intptr): Reference labels OutOfBounds {
if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) { if (Convert<uintptr>(index) < Convert<uintptr>(this.length)) {
return unsafe::NewReference<T>( return unsafe::NewReference<T>(
this.object, this.offset + index * %SizeOf<T>()); this.object, this.offset + TimesSizeOf<T>(index));
} else { } else {
goto OutOfBounds; goto OutOfBounds;
} }
...@@ -74,7 +99,7 @@ struct Slice<T: type, Reference: type> { ...@@ -74,7 +99,7 @@ struct Slice<T: type, Reference: type> {
} }
macro Iterator(): SliceIterator<T, Reference> { macro Iterator(): SliceIterator<T, Reference> {
const end = this.offset + this.length * %SizeOf<T>(); const end = this.offset + TimesSizeOf<T>(this.length);
return SliceIterator<T, Reference>{ return SliceIterator<T, Reference>{
object: this.object, object: this.object,
start: this.offset, start: this.offset,
...@@ -87,8 +112,8 @@ struct Slice<T: type, Reference: type> { ...@@ -87,8 +112,8 @@ struct Slice<T: type, Reference: type> {
check( check(
Convert<uintptr>(endIndex) <= Convert<uintptr>(this.length) && Convert<uintptr>(endIndex) <= Convert<uintptr>(this.length) &&
Convert<uintptr>(startIndex) <= Convert<uintptr>(endIndex)); Convert<uintptr>(startIndex) <= Convert<uintptr>(endIndex));
const start = this.offset + startIndex * %SizeOf<T>(); const start = this.offset + TimesSizeOf<T>(startIndex);
const end = this.offset + endIndex * %SizeOf<T>(); const end = this.offset + TimesSizeOf<T>(endIndex);
return SliceIterator<T, Reference>{ return SliceIterator<T, Reference>{
object: this.object, object: this.object,
start, start,
...@@ -97,6 +122,12 @@ struct Slice<T: type, Reference: type> { ...@@ -97,6 +122,12 @@ struct Slice<T: type, Reference: type> {
}; };
} }
// WARNING: This can return a raw pointer into the heap, which is not GC-safe.
macro GCUnsafeStartPointer(): RawPtr<T> {
return %RawDownCast<RawPtr<T>>(
unsafe::GCUnsafeReferenceToRawPtr(this.object, this.offset));
}
const object: HeapObject|TaggedZeroPattern; const object: HeapObject|TaggedZeroPattern;
const offset: intptr; const offset: intptr;
const length: intptr; const length: intptr;
...@@ -106,7 +137,8 @@ struct Slice<T: type, Reference: type> { ...@@ -106,7 +137,8 @@ struct Slice<T: type, Reference: type> {
namespace unsafe { namespace unsafe {
macro NewMutableSlice<T: type>( macro NewMutableSlice<T: type>(
object: HeapObject, offset: intptr, length: intptr): MutableSlice<T> { object: HeapObject|TaggedZeroPattern, offset: intptr,
length: intptr): MutableSlice<T> {
return %RawDownCast<MutableSlice<T>>(Slice<T, &T>{ return %RawDownCast<MutableSlice<T>>(Slice<T, &T>{
object: object, object: object,
offset: offset, offset: offset,
...@@ -116,7 +148,8 @@ macro NewMutableSlice<T: type>( ...@@ -116,7 +148,8 @@ macro NewMutableSlice<T: type>(
} }
macro NewConstSlice<T: type>( macro NewConstSlice<T: type>(
object: HeapObject, offset: intptr, length: intptr): ConstSlice<T> { object: HeapObject|TaggedZeroPattern, offset: intptr,
length: intptr): ConstSlice<T> {
return %RawDownCast<ConstSlice<T>>(Slice<T, const &T>{ return %RawDownCast<ConstSlice<T>>(Slice<T, const &T>{
object: object, object: object,
offset: offset, offset: offset,
......
...@@ -6548,11 +6548,11 @@ TNode<BoolT> CodeStubAssembler::FixedArraySizeDoesntFitInNewSpace( ...@@ -6548,11 +6548,11 @@ TNode<BoolT> CodeStubAssembler::FixedArraySizeDoesntFitInNewSpace(
element_count, IntPtrOrSmiConstant<TIndex>(max_newspace_elements)); element_count, IntPtrOrSmiConstant<TIndex>(max_newspace_elements));
} }
TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string, TNode<Uint16T> CodeStubAssembler::StringCharCodeAt(TNode<String> string,
TNode<UintPtrT> index) { TNode<UintPtrT> index) {
CSA_ASSERT(this, UintPtrLessThan(index, LoadStringLengthAsWord(string))); CSA_ASSERT(this, UintPtrLessThan(index, LoadStringLengthAsWord(string)));
TVARIABLE(Int32T, var_result); TVARIABLE(Uint16T, var_result);
Label return_result(this), if_runtime(this, Label::kDeferred), Label return_result(this), if_runtime(this, Label::kDeferred),
if_stringistwobyte(this), if_stringisonebyte(this); if_stringistwobyte(this), if_stringisonebyte(this);
...@@ -6570,14 +6570,13 @@ TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string, ...@@ -6570,14 +6570,13 @@ TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string,
BIND(&if_stringisonebyte); BIND(&if_stringisonebyte);
{ {
var_result = UncheckedCast<Int32T>(Load<Uint8T>(string_data, offset)); var_result = Load<Uint8T>(string_data, offset);
Goto(&return_result); Goto(&return_result);
} }
BIND(&if_stringistwobyte); BIND(&if_stringistwobyte);
{ {
var_result = UncheckedCast<Int32T>( var_result = Load<Uint16T>(string_data, WordShl(offset, IntPtrConstant(1)));
Load<Uint16T>(string_data, WordShl(offset, IntPtrConstant(1))));
Goto(&return_result); Goto(&return_result);
} }
...@@ -6586,7 +6585,7 @@ TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string, ...@@ -6586,7 +6585,7 @@ TNode<Int32T> CodeStubAssembler::StringCharCodeAt(TNode<String> string,
TNode<Object> result = TNode<Object> result =
CallRuntime(Runtime::kStringCharCodeAt, NoContextConstant(), string, CallRuntime(Runtime::kStringCharCodeAt, NoContextConstant(), string,
ChangeUintPtrToTagged(index)); ChangeUintPtrToTagged(index));
var_result = SmiToInt32(CAST(result)); var_result = UncheckedCast<Uint16T>(SmiToInt32(CAST(result)));
Goto(&return_result); Goto(&return_result);
} }
...@@ -7612,6 +7611,50 @@ void CodeStubAssembler::TryToName(SloppyTNode<Object> key, Label* if_keyisindex, ...@@ -7612,6 +7611,50 @@ void CodeStubAssembler::TryToName(SloppyTNode<Object> key, Label* if_keyisindex,
} }
} }
void CodeStubAssembler::StringWriteToFlatOneByte(TNode<String> source,
TNode<RawPtrT> sink,
TNode<Int32T> from,
TNode<Int32T> to) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::string_write_to_flat_one_byte());
CallCFunction(function, base::nullopt,
std::make_pair(MachineType::AnyTagged(), source),
std::make_pair(MachineType::Pointer(), sink),
std::make_pair(MachineType::Int32(), from),
std::make_pair(MachineType::Int32(), to));
}
void CodeStubAssembler::StringWriteToFlatTwoByte(TNode<String> source,
TNode<RawPtrT> sink,
TNode<Int32T> from,
TNode<Int32T> to) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::string_write_to_flat_two_byte());
CallCFunction(function, base::nullopt,
std::make_pair(MachineType::AnyTagged(), source),
std::make_pair(MachineType::Pointer(), sink),
std::make_pair(MachineType::Int32(), from),
std::make_pair(MachineType::Int32(), to));
}
TNode<RawPtr<Uint8T>> CodeStubAssembler::ExternalOneByteStringGetChars(
TNode<ExternalOneByteString> string) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::external_one_byte_string_get_chars());
return UncheckedCast<RawPtr<Uint8T>>(
CallCFunction(function, MachineType::Pointer(),
std::make_pair(MachineType::AnyTagged(), string)));
}
TNode<RawPtr<Uint16T>> CodeStubAssembler::ExternalTwoByteStringGetChars(
TNode<ExternalTwoByteString> string) {
TNode<ExternalReference> function =
ExternalConstant(ExternalReference::external_two_byte_string_get_chars());
return UncheckedCast<RawPtr<Uint16T>>(
CallCFunction(function, MachineType::Pointer(),
std::make_pair(MachineType::AnyTagged(), string)));
}
void CodeStubAssembler::TryInternalizeString( void CodeStubAssembler::TryInternalizeString(
TNode<String> string, Label* if_index, TVariable<IntPtrT>* var_index, TNode<String> string, Label* if_index, TVariable<IntPtrT>* var_index,
Label* if_internalized, TVariable<Name>* var_internalized, Label* if_internalized, TVariable<Name>* var_internalized,
......
...@@ -1034,6 +1034,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -1034,6 +1034,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<RawPtrT> LoadExternalStringResourceDataPtr( TNode<RawPtrT> LoadExternalStringResourceDataPtr(
TNode<ExternalString> object) { TNode<ExternalString> object) {
// This is only valid for ExternalStrings where the resource data
// pointer is cached (i.e. no uncached external strings).
CSA_ASSERT(this, Word32NotEqual(
Word32And(LoadInstanceType(object),
Int32Constant(kUncachedExternalStringMask)),
Int32Constant(kUncachedExternalStringTag)));
return LoadExternalPointerFromObject(object, return LoadExternalPointerFromObject(object,
ExternalString::kResourceDataOffset, ExternalString::kResourceDataOffset,
kExternalStringResourceDataTag); kExternalStringResourceDataTag);
...@@ -1190,6 +1196,13 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -1190,6 +1196,13 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
value, StoreToObjectWriteBarrier::kNone); value, StoreToObjectWriteBarrier::kNone);
} }
TNode<RawPtrT> GCUnsafeReferenceToRawPtr(TNode<Object> object,
TNode<IntPtrT> offset) {
return ReinterpretCast<RawPtrT>(
IntPtrAdd(BitcastTaggedToWord(object),
IntPtrSub(offset, IntPtrConstant(kHeapObjectTag))));
}
// Load the floating point value of a HeapNumber. // Load the floating point value of a HeapNumber.
TNode<Float64T> LoadHeapNumberValue(TNode<HeapObject> object); TNode<Float64T> LoadHeapNumberValue(TNode<HeapObject> object);
// Load the Map of an HeapObject. // Load the Map of an HeapObject.
...@@ -2520,7 +2533,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -2520,7 +2533,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// String helpers. // String helpers.
// Load a character from a String (might flatten a ConsString). // Load a character from a String (might flatten a ConsString).
TNode<Int32T> StringCharCodeAt(TNode<String> string, TNode<UintPtrT> index); TNode<Uint16T> StringCharCodeAt(TNode<String> string, TNode<UintPtrT> index);
// Return the single character string with only {code}. // Return the single character string with only {code}.
TNode<String> StringFromSingleCharCode(TNode<Int32T> code); TNode<String> StringFromSingleCharCode(TNode<Int32T> code);
...@@ -2766,6 +2779,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -2766,6 +2779,18 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TVariable<Name>* var_unique, Label* if_bailout, TVariable<Name>* var_unique, Label* if_bailout,
Label* if_notinternalized = nullptr); Label* if_notinternalized = nullptr);
// Call non-allocating runtime String::WriteToFlat using fast C-calls.
void StringWriteToFlatOneByte(TNode<String> source, TNode<RawPtrT> sink,
TNode<Int32T> from, TNode<Int32T> to);
void StringWriteToFlatTwoByte(TNode<String> source, TNode<RawPtrT> sink,
TNode<Int32T> from, TNode<Int32T> to);
// Calls External{One,Two}ByteString::GetChars with a fast C-call.
TNode<RawPtr<Uint8T>> ExternalOneByteStringGetChars(
TNode<ExternalOneByteString> string);
TNode<RawPtr<Uint16T>> ExternalTwoByteStringGetChars(
TNode<ExternalTwoByteString> string);
// Performs a hash computation and string table lookup for the given string, // Performs a hash computation and string table lookup for the given string,
// and jumps to: // and jumps to:
// - |if_index| if the string is an array index like "123"; |var_index| // - |if_index| if the string is an array index like "123"; |var_index|
......
...@@ -660,6 +660,37 @@ ExternalReference ExternalReference::search_string_raw_two_two() { ...@@ -660,6 +660,37 @@ ExternalReference ExternalReference::search_string_raw_two_two() {
return search_string_raw<const uc16, const uc16>(); return search_string_raw<const uc16, const uc16>();
} }
namespace {
void StringWriteToFlatOneByte(Address source, uint8_t* sink, int32_t from,
int32_t to) {
return String::WriteToFlat<uint8_t>(String::cast(Object(source)), sink, from,
to);
}
void StringWriteToFlatTwoByte(Address source, uint16_t* sink, int32_t from,
int32_t to) {
return String::WriteToFlat<uint16_t>(String::cast(Object(source)), sink, from,
to);
}
const uint8_t* ExternalOneByteStringGetChars(Address string) {
return ExternalOneByteString::cast(Object(string)).GetChars();
}
const uint16_t* ExternalTwoByteStringGetChars(Address string) {
return ExternalTwoByteString::cast(Object(string)).GetChars();
}
} // namespace
FUNCTION_REFERENCE(string_write_to_flat_one_byte, StringWriteToFlatOneByte)
FUNCTION_REFERENCE(string_write_to_flat_two_byte, StringWriteToFlatTwoByte)
FUNCTION_REFERENCE(external_one_byte_string_get_chars,
ExternalOneByteStringGetChars)
FUNCTION_REFERENCE(external_two_byte_string_get_chars,
ExternalTwoByteStringGetChars)
FUNCTION_REFERENCE(orderedhashmap_gethash_raw, OrderedHashMap::GetHash) FUNCTION_REFERENCE(orderedhashmap_gethash_raw, OrderedHashMap::GetHash)
Address GetOrCreateHash(Isolate* isolate, Address raw_key) { Address GetOrCreateHash(Isolate* isolate, Address raw_key) {
......
...@@ -178,6 +178,10 @@ class StatsCounter; ...@@ -178,6 +178,10 @@ class StatsCounter;
V(search_string_raw_one_two, "search_string_raw_one_two") \ V(search_string_raw_one_two, "search_string_raw_one_two") \
V(search_string_raw_two_one, "search_string_raw_two_one") \ V(search_string_raw_two_one, "search_string_raw_two_one") \
V(search_string_raw_two_two, "search_string_raw_two_two") \ V(search_string_raw_two_two, "search_string_raw_two_two") \
V(string_write_to_flat_one_byte, "string_write_to_flat_one_byte") \
V(string_write_to_flat_two_byte, "string_write_to_flat_two_byte") \
V(external_one_byte_string_get_chars, "external_one_byte_string_get_chars") \
V(external_two_byte_string_get_chars, "external_two_byte_string_get_chars") \
V(smi_lexicographic_compare_function, "smi_lexicographic_compare_function") \ V(smi_lexicographic_compare_function, "smi_lexicographic_compare_function") \
V(string_to_array_index_function, "String::ToArrayIndex") \ V(string_to_array_index_function, "String::ToArrayIndex") \
V(try_string_to_index_or_lookup_existing, \ V(try_string_to_index_or_lookup_existing, \
......
...@@ -1177,7 +1177,7 @@ Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature, ...@@ -1177,7 +1177,7 @@ Node* CodeAssembler::CallCFunctionN(Signature<MachineType>* signature,
} }
Node* CodeAssembler::CallCFunction( Node* CodeAssembler::CallCFunction(
Node* function, MachineType return_type, Node* function, base::Optional<MachineType> return_type,
std::initializer_list<CodeAssembler::CFunctionArg> args) { std::initializer_list<CodeAssembler::CFunctionArg> args) {
return raw_assembler()->CallCFunction(function, return_type, args); return raw_assembler()->CallCFunction(function, return_type, args);
} }
......
...@@ -1174,7 +1174,8 @@ class V8_EXPORT_PRIVATE CodeAssembler { ...@@ -1174,7 +1174,8 @@ class V8_EXPORT_PRIVATE CodeAssembler {
// Call to a C function. // Call to a C function.
template <class... CArgs> template <class... CArgs>
Node* CallCFunction(Node* function, MachineType return_type, CArgs... cargs) { Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
CArgs... cargs) {
static_assert(v8::internal::conjunction< static_assert(v8::internal::conjunction<
std::is_convertible<CArgs, CFunctionArg>...>::value, std::is_convertible<CArgs, CFunctionArg>...>::value,
"invalid argument types"); "invalid argument types");
...@@ -1234,7 +1235,7 @@ class V8_EXPORT_PRIVATE CodeAssembler { ...@@ -1234,7 +1235,7 @@ class V8_EXPORT_PRIVATE CodeAssembler {
private: private:
void HandleException(Node* result); void HandleException(Node* result);
Node* CallCFunction(Node* function, MachineType return_type, Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
std::initializer_list<CFunctionArg> args); std::initializer_list<CFunctionArg> args);
Node* CallCFunctionWithoutFunctionDescriptor( Node* CallCFunctionWithoutFunctionDescriptor(
......
...@@ -731,14 +731,18 @@ namespace { ...@@ -731,14 +731,18 @@ namespace {
enum FunctionDescriptorMode { kHasFunctionDescriptor, kNoFunctionDescriptor }; enum FunctionDescriptorMode { kHasFunctionDescriptor, kNoFunctionDescriptor };
Node* CallCFunctionImpl( Node* CallCFunctionImpl(
RawMachineAssembler* rasm, Node* function, MachineType return_type, RawMachineAssembler* rasm, Node* function,
base::Optional<MachineType> return_type,
std::initializer_list<RawMachineAssembler::CFunctionArg> args, std::initializer_list<RawMachineAssembler::CFunctionArg> args,
bool caller_saved_regs, SaveFPRegsMode mode, bool caller_saved_regs, SaveFPRegsMode mode,
FunctionDescriptorMode no_function_descriptor) { FunctionDescriptorMode no_function_descriptor) {
static constexpr std::size_t kNumCArgs = 10; static constexpr std::size_t kNumCArgs = 10;
MachineSignature::Builder builder(rasm->zone(), 1, args.size()); MachineSignature::Builder builder(rasm->zone(), return_type ? 1 : 0,
builder.AddReturn(return_type); args.size());
if (return_type) {
builder.AddReturn(*return_type);
}
for (const auto& arg : args) builder.AddParam(arg.first); for (const auto& arg : args) builder.AddParam(arg.first);
bool caller_saved_fp_regs = caller_saved_regs && (mode == kSaveFPRegs); bool caller_saved_fp_regs = caller_saved_regs && (mode == kSaveFPRegs);
...@@ -763,7 +767,7 @@ Node* CallCFunctionImpl( ...@@ -763,7 +767,7 @@ Node* CallCFunctionImpl(
} // namespace } // namespace
Node* RawMachineAssembler::CallCFunction( Node* RawMachineAssembler::CallCFunction(
Node* function, MachineType return_type, Node* function, base::Optional<MachineType> return_type,
std::initializer_list<RawMachineAssembler::CFunctionArg> args) { std::initializer_list<RawMachineAssembler::CFunctionArg> args) {
return CallCFunctionImpl(this, function, return_type, args, false, return CallCFunctionImpl(this, function, return_type, args, false,
kDontSaveFPRegs, kHasFunctionDescriptor); kDontSaveFPRegs, kHasFunctionDescriptor);
......
...@@ -927,14 +927,15 @@ class V8_EXPORT_PRIVATE RawMachineAssembler { ...@@ -927,14 +927,15 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
// Call to a C function. // Call to a C function.
template <class... CArgs> template <class... CArgs>
Node* CallCFunction(Node* function, MachineType return_type, CArgs... cargs) { Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
CArgs... cargs) {
static_assert(v8::internal::conjunction< static_assert(v8::internal::conjunction<
std::is_convertible<CArgs, CFunctionArg>...>::value, std::is_convertible<CArgs, CFunctionArg>...>::value,
"invalid argument types"); "invalid argument types");
return CallCFunction(function, return_type, {cargs...}); return CallCFunction(function, return_type, {cargs...});
} }
Node* CallCFunction(Node* function, MachineType return_type, Node* CallCFunction(Node* function, base::Optional<MachineType> return_type,
std::initializer_list<CFunctionArg> args); std::initializer_list<CFunctionArg> args);
// Call to a C function without a function discriptor on AIX. // Call to a C function without a function discriptor on AIX.
......
...@@ -787,6 +787,7 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) { ...@@ -787,6 +787,7 @@ DebugInfo::SideEffectState BuiltinGetSideEffectState(Builtins::Name id) {
case Builtins::kStringPrototypeSlice: case Builtins::kStringPrototypeSlice:
case Builtins::kStringPrototypeSmall: case Builtins::kStringPrototypeSmall:
case Builtins::kStringPrototypeStartsWith: case Builtins::kStringPrototypeStartsWith:
case Builtins::kStringSlowFlatten:
case Builtins::kStringPrototypeStrike: case Builtins::kStringPrototypeStrike:
case Builtins::kStringPrototypeSub: case Builtins::kStringPrototypeSub:
case Builtins::kStringPrototypeSubstr: case Builtins::kStringPrototypeSubstr:
......
...@@ -89,6 +89,7 @@ macro IsSimpleObjectMap(map: Map): bool { ...@@ -89,6 +89,7 @@ macro IsSimpleObjectMap(map: Map): bool {
return false; return false;
} }
const bitField = map.bit_field; const bitField = map.bit_field;
// Using & instead of && enables Turbofan to merge the two checks into one.
return !bitField.has_named_interceptor & !bitField.is_access_check_needed; return !bitField.has_named_interceptor & !bitField.is_access_check_needed;
} }
......
...@@ -845,6 +845,7 @@ DEF_GETTER(ExternalOneByteString, resource, ...@@ -845,6 +845,7 @@ DEF_GETTER(ExternalOneByteString, resource,
void ExternalOneByteString::update_data_cache(Isolate* isolate) { void ExternalOneByteString::update_data_cache(Isolate* isolate) {
if (is_uncached()) return; if (is_uncached()) return;
DisallowGarbageCollection no_gc;
WriteExternalPointerField(kResourceDataOffset, isolate, WriteExternalPointerField(kResourceDataOffset, isolate,
reinterpret_cast<Address>(resource()->data()), reinterpret_cast<Address>(resource()->data()),
kExternalStringResourceDataTag); kExternalStringResourceDataTag);
...@@ -868,6 +869,7 @@ void ExternalOneByteString::set_resource( ...@@ -868,6 +869,7 @@ void ExternalOneByteString::set_resource(
} }
const uint8_t* ExternalOneByteString::GetChars() { const uint8_t* ExternalOneByteString::GetChars() {
DisallowGarbageCollection no_gc;
return reinterpret_cast<const uint8_t*>(resource()->data()); return reinterpret_cast<const uint8_t*>(resource()->data());
} }
...@@ -883,6 +885,7 @@ DEF_GETTER(ExternalTwoByteString, resource, ...@@ -883,6 +885,7 @@ DEF_GETTER(ExternalTwoByteString, resource,
void ExternalTwoByteString::update_data_cache(Isolate* isolate) { void ExternalTwoByteString::update_data_cache(Isolate* isolate) {
if (is_uncached()) return; if (is_uncached()) return;
DisallowGarbageCollection no_gc;
WriteExternalPointerField(kResourceDataOffset, isolate, WriteExternalPointerField(kResourceDataOffset, isolate,
reinterpret_cast<Address>(resource()->data()), reinterpret_cast<Address>(resource()->data()),
kExternalStringResourceDataTag); kExternalStringResourceDataTag);
...@@ -905,7 +908,10 @@ void ExternalTwoByteString::set_resource( ...@@ -905,7 +908,10 @@ void ExternalTwoByteString::set_resource(
if (resource != nullptr) update_data_cache(isolate); if (resource != nullptr) update_data_cache(isolate);
} }
const uint16_t* ExternalTwoByteString::GetChars() { return resource()->data(); } const uint16_t* ExternalTwoByteString::GetChars() {
DisallowGarbageCollection no_gc;
return resource()->data();
}
uint16_t ExternalTwoByteString::Get(int index) { uint16_t ExternalTwoByteString::Get(int index) {
DCHECK(index >= 0 && index < length()); DCHECK(index >= 0 && index < length());
......
...@@ -1703,5 +1703,25 @@ template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void String::WriteToFlat( ...@@ -1703,5 +1703,25 @@ template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void String::WriteToFlat(
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void String::WriteToFlat( template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void String::WriteToFlat(
String source, uint8_t* sink, int from, int to); String source, uint8_t* sink, int from, int to);
namespace {
// Check that the constants defined in src/objects/instance-type.h coincides
// with the Torque-definition of string instance types in src/objects/string.tq.
DEFINE_TORQUE_GENERATED_STRING_INSTANCE_TYPE()
STATIC_ASSERT(kStringRepresentationMask == RepresentationBits::kMask);
STATIC_ASSERT(kStringEncodingMask == IsOneByteBit::kMask);
STATIC_ASSERT(kTwoByteStringTag == IsOneByteBit::encode(false));
STATIC_ASSERT(kOneByteStringTag == IsOneByteBit::encode(true));
STATIC_ASSERT(kUncachedExternalStringMask == IsUncachedBit::kMask);
STATIC_ASSERT(kUncachedExternalStringTag == IsUncachedBit::encode(true));
STATIC_ASSERT(kIsNotInternalizedMask == IsNotInternalizedBit::kMask);
STATIC_ASSERT(kNotInternalizedTag == IsNotInternalizedBit::encode(true));
STATIC_ASSERT(kInternalizedTag == IsNotInternalizedBit::encode(false));
} // namespace
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
...@@ -6,13 +6,42 @@ ...@@ -6,13 +6,42 @@
@generateCppClass @generateCppClass
@reserveBitsInInstanceType(6) @reserveBitsInInstanceType(6)
extern class String extends Name { extern class String extends Name {
macro StringInstanceType(): StringInstanceType {
return %RawDownCast<StringInstanceType>(
Convert<uint16>(this.map.instance_type));
}
const length: int32; const length: int32;
} }
extern enum StringRepresentationTag extends uint32 {
kSeqStringTag,
kConsStringTag,
kExternalStringTag,
kSlicedStringTag,
kThinStringTag
}
bitfield struct StringInstanceType extends uint16 {
representation: StringRepresentationTag: 3 bit;
is_one_byte: bool: 1 bit;
is_uncached: bool: 1 bit;
is_not_internalized: bool: 1 bit;
}
@generateCppClass @generateCppClass
@generateBodyDescriptor @generateBodyDescriptor
@doNotGenerateCast @doNotGenerateCast
extern class ConsString extends String { extern class ConsString extends String {
// Corresponds to String::IsFlat() in the C++ runtime.
macro IsFlat(): bool {
return this.second.length == 0;
}
macro IsOneByteRepresentation(): bool {
return this.StringInstanceType().is_one_byte;
}
first: String; first: String;
second: String; second: String;
} }
...@@ -21,6 +50,7 @@ extern class ConsString extends String { ...@@ -21,6 +50,7 @@ extern class ConsString extends String {
@doNotGenerateCast @doNotGenerateCast
extern class ExternalString extends String { extern class ExternalString extends String {
resource: ExternalPointer; resource: ExternalPointer;
// WARNING: This field is missing for uncached external strings.
resource_data: ExternalPointer; resource_data: ExternalPointer;
} }
...@@ -28,13 +58,36 @@ extern operator '.resource_ptr' macro LoadExternalStringResourcePtr( ...@@ -28,13 +58,36 @@ extern operator '.resource_ptr' macro LoadExternalStringResourcePtr(
ExternalString): RawPtr; ExternalString): RawPtr;
extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr( extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr(
ExternalString): RawPtr; ExternalString): RawPtr;
extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr(
ExternalOneByteString): RawPtr<char8>;
extern operator '.resource_data_ptr' macro LoadExternalStringResourceDataPtr(
ExternalTwoByteString): RawPtr<char16>;
extern macro ExternalOneByteStringGetChars(ExternalOneByteString):
RawPtr<char8>;
extern macro ExternalTwoByteStringGetChars(ExternalTwoByteString):
RawPtr<char16>;
@doNotGenerateCast @doNotGenerateCast
extern class ExternalOneByteString extends ExternalString { extern class ExternalOneByteString extends ExternalString {
macro GetChars(): RawPtr<char8> {
if (this.StringInstanceType().is_uncached) {
return ExternalOneByteStringGetChars(this);
} else {
return this.resource_data_ptr;
}
}
} }
@doNotGenerateCast @doNotGenerateCast
extern class ExternalTwoByteString extends ExternalString { extern class ExternalTwoByteString extends ExternalString {
macro GetChars(): RawPtr<char16> {
if (this.StringInstanceType().is_uncached) {
return ExternalTwoByteStringGetChars(this);
} else {
return this.resource_data_ptr;
}
}
} }
@generateCppClass @generateCppClass
...@@ -79,26 +132,153 @@ extern class ThinString extends String { ...@@ -79,26 +132,153 @@ extern class ThinString extends String {
// C++ runtime. See also: ToDirectStringAssembler. // C++ runtime. See also: ToDirectStringAssembler.
type DirectString extends String; type DirectString extends String;
@export macro AllocateNonEmptySeqOneByteString<Iterator: type>(
macro AllocateSeqOneByteString(length: uint32): SeqOneByteString { length: uint32, content: Iterator): SeqOneByteString {
assert(length <= kStringMaxLength); assert(length != 0 && length <= kStringMaxLength);
if (length == 0) return UnsafeCast<SeqOneByteString>(kEmptyString);
return new SeqOneByteString{ return new SeqOneByteString{
map: kOneByteStringMap, map: kOneByteStringMap,
raw_hash_field: kNameEmptyHashField, raw_hash_field: kNameEmptyHashField,
length: Signed(length), length: Signed(length),
chars: ...UninitializedIterator {} chars: ...content
}; };
} }
@export macro AllocateNonEmptySeqTwoByteString<Iterator: type>(
macro AllocateSeqTwoByteString(length: uint32): String { length: uint32, content: Iterator): SeqTwoByteString {
assert(length <= kStringMaxLength); assert(length > 0 && length <= kStringMaxLength);
if (length == 0) return kEmptyString;
return new SeqTwoByteString{ return new SeqTwoByteString{
map: kStringMap, map: kStringMap,
raw_hash_field: kNameEmptyHashField, raw_hash_field: kNameEmptyHashField,
length: Signed(length), length: Signed(length),
chars: ...UninitializedIterator {} chars: ...content
}; };
} }
macro AllocateNonEmptySeqOneByteString(length: uint32): SeqOneByteString {
return AllocateNonEmptySeqOneByteString(length, UninitializedIterator{});
}
macro AllocateNonEmptySeqTwoByteString(length: uint32): SeqTwoByteString {
return AllocateNonEmptySeqTwoByteString(length, UninitializedIterator{});
}
macro AllocateSeqOneByteString<Iterator: type>(
length: uint32, content: Iterator): SeqOneByteString|EmptyString {
if (length == 0) return kEmptyString;
return AllocateNonEmptySeqOneByteString(length, content);
}
macro AllocateSeqTwoByteString<Iterator: type>(
length: uint32, content: Iterator): SeqTwoByteString|EmptyString {
if (length == 0) return kEmptyString;
return AllocateNonEmptySeqTwoByteString(length, content);
}
@export
macro AllocateSeqOneByteString(length: uint32): SeqOneByteString|EmptyString {
return AllocateSeqOneByteString(length, UninitializedIterator{});
}
@export
macro AllocateSeqTwoByteString(length: uint32): SeqTwoByteString|EmptyString {
return AllocateSeqTwoByteString(length, UninitializedIterator{});
}
extern macro StringWriteToFlatOneByte(String, RawPtr<char8>, int32, int32);
extern macro StringWriteToFlatTwoByte(String, RawPtr<char16>, int32, int32);
// Corresponds to String::SlowFlatten in the C++ runtime.
builtin StringSlowFlatten(cons: ConsString): String {
// TurboFan can create cons strings with empty first parts.
let cons = cons;
while (cons.first.length == 0) {
// We do not want to call this function recursively. Therefore we call
// String::Flatten only in those cases where String::SlowFlatten is not
// called again.
try {
const second = Cast<ConsString>(cons.second) otherwise FoundFlatString;
if (second.IsFlat()) goto FoundFlatString;
cons = second;
} label FoundFlatString {
return Flatten(cons.second);
}
}
let flat: String;
if (cons.IsOneByteRepresentation()) {
const allocated = AllocateNonEmptySeqOneByteString(Unsigned(cons.length));
StringWriteToFlatOneByte(
cons, (&allocated.chars).GCUnsafeStartPointer(), 0, cons.length);
flat = allocated;
} else {
const allocated = UnsafeCast<SeqTwoByteString>(
AllocateNonEmptySeqTwoByteString(Unsigned(cons.length)));
StringWriteToFlatTwoByte(
cons, (&allocated.chars).GCUnsafeStartPointer(), 0, cons.length);
flat = allocated;
}
cons.first = flat;
cons.second = kEmptyString;
return flat;
}
// Corresponds to String::Flatten in the C++ runtime.
macro Flatten(string: String): String {
typeswitch (string) {
case (cons: ConsString): {
return Flatten(cons);
}
case (thin: ThinString): {
assert(!Is<ConsString>(thin.actual));
return thin.actual;
}
case (other: String): {
return other;
}
}
}
macro Flatten(cons: ConsString): String {
if (cons.IsFlat()) return cons.first;
return StringSlowFlatten(cons);
}
// Get a slice to the string data, flatten only if unavoidable for this.
macro GetStringData(string: String): never labels OneByte(ConstSlice<char8>),
TwoByte(ConstSlice<char16>) {
let string = string;
let offset: intptr = 0;
const length = Convert<intptr>(string.length);
while (true) {
typeswitch (string) {
case (s: SeqOneByteString): {
goto OneByte(Subslice(&s.chars, offset, length) otherwise unreachable);
}
case (s: SeqTwoByteString): {
goto TwoByte(Subslice(&s.chars, offset, length) otherwise unreachable);
}
case (s: ThinString): {
string = s.actual;
}
case (s: ConsString): {
string = Flatten(s);
}
case (s: SlicedString): {
offset += Convert<intptr>(s.offset);
string = s.parent;
}
case (s: ExternalOneByteString): {
const data = torque_internal::unsafe::NewOffHeapConstSlice(
s.GetChars(), Convert<intptr>(s.length));
goto OneByte(Subslice(data, offset, length) otherwise unreachable);
}
case (s: ExternalTwoByteString): {
const data = torque_internal::unsafe::NewOffHeapConstSlice(
s.GetChars(), Convert<intptr>(s.length));
goto TwoByte(Subslice(data, offset, length) otherwise unreachable);
}
case (String): {
unreachable;
}
}
}
VerifiedUnreachable();
}
...@@ -486,29 +486,6 @@ RUNTIME_FUNCTION(Runtime_StringMaxLength) { ...@@ -486,29 +486,6 @@ RUNTIME_FUNCTION(Runtime_StringMaxLength) {
return Smi::FromInt(String::kMaxLength); return Smi::FromInt(String::kMaxLength);
} }
RUNTIME_FUNCTION(Runtime_StringCompareSequence) {
HandleScope handle_scope(isolate);
DCHECK_EQ(3, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, string, 0);
CONVERT_ARG_HANDLE_CHECKED(String, search_string, 1);
CONVERT_NUMBER_CHECKED(int, start, Int32, args[2]);
// Check if start + searchLength is in bounds.
DCHECK_LE(start + search_string->length(), string->length());
FlatStringReader string_reader(isolate, String::Flatten(isolate, string));
FlatStringReader search_reader(isolate,
String::Flatten(isolate, search_string));
for (int i = 0; i < search_string->length(); i++) {
if (string_reader.Get(start + i) != search_reader.Get(i)) {
return ReadOnlyRoots(isolate).false_value();
}
}
return ReadOnlyRoots(isolate).true_value();
}
RUNTIME_FUNCTION(Runtime_StringEscapeQuotes) { RUNTIME_FUNCTION(Runtime_StringEscapeQuotes) {
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
......
...@@ -439,7 +439,6 @@ namespace internal { ...@@ -439,7 +439,6 @@ namespace internal {
F(StringLessThanOrEqual, 2, 1) \ F(StringLessThanOrEqual, 2, 1) \
F(StringMaxLength, 0, 1) \ F(StringMaxLength, 0, 1) \
F(StringReplaceOneCharWithString, 3, 1) \ F(StringReplaceOneCharWithString, 3, 1) \
F(StringCompareSequence, 3, 1) \
F(StringSubstring, 3, 1) \ F(StringSubstring, 3, 1) \
F(StringToArray, 2, 1) \ F(StringToArray, 2, 1) \
F(StringTrim, 2, 1) F(StringTrim, 2, 1)
......
...@@ -471,6 +471,11 @@ std::vector<Method*> AggregateType::Methods(const std::string& name) const { ...@@ -471,6 +471,11 @@ std::vector<Method*> AggregateType::Methods(const std::string& name) const {
std::vector<Method*> result; std::vector<Method*> result;
std::copy_if(methods_.begin(), methods_.end(), std::back_inserter(result), std::copy_if(methods_.begin(), methods_.end(), std::back_inserter(result),
[name](Macro* macro) { return macro->ReadableName() == name; }); [name](Macro* macro) { return macro->ReadableName() == name; });
if (result.empty() && parent() != nullptr) {
if (auto aggregate_parent = parent()->AggregateSupertype()) {
return (*aggregate_parent)->Methods(name);
}
}
return result; return result;
} }
......
...@@ -3202,7 +3202,8 @@ TEST(IsWhiteSpaceOrLineTerminator) { ...@@ -3202,7 +3202,8 @@ TEST(IsWhiteSpaceOrLineTerminator) {
{ // Returns true if whitespace, false otherwise. { // Returns true if whitespace, false otherwise.
CodeStubAssembler m(asm_tester.state()); CodeStubAssembler m(asm_tester.state());
Label if_true(&m), if_false(&m); Label if_true(&m), if_false(&m);
m.Branch(m.IsWhiteSpaceOrLineTerminator(m.SmiToInt32(m.Parameter<Smi>(1))), m.Branch(m.IsWhiteSpaceOrLineTerminator(
m.UncheckedCast<Uint16T>(m.SmiToInt32(m.Parameter<Smi>(1)))),
&if_true, &if_false); &if_true, &if_false);
m.BIND(&if_true); m.BIND(&if_true);
m.Return(m.TrueConstant()); m.Return(m.TrueConstant());
......
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