Revert of [stubs] Port SubStringStub to TurboFan (patchset #8 id:140001 of https://codereview.chromium.org/2355793003/ )
Reason for revert: Speculative revert because of stability problems Original issue's description: > [stubs] Port SubStringStub to TurboFan > > This ports the platform-specific SubStringStub to TurboFan. > > It also contains a minor bug-fix for the case when the requested substring > length equals the subject string length, but the start index is not equal to 0. > The old stub implementation returned the subject string, while the new > implementation calls into runtime, which finally results in a thrown exception. > > BUG=v8:5415 > > Committed: https://crrev.com/49be31921536716706a6790fbbf9c346b975af16 > Cr-Commit-Position: refs/heads/master@{#39653} TBR=ishell@chromium.org,bmeurer@chromium.org,jgruber@chromium.org # Not skipping CQ checks because original CL landed more than 1 days ago. BUG=v8:5415, chromium:649967 NOPRESUBMIT=true NOTRY=true Review-Url: https://codereview.chromium.org/2365413002 Cr-Commit-Position: refs/heads/master@{#39737}
This commit is contained in:
parent
b9cdb630dd
commit
10a801f12b
@ -2124,6 +2124,231 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
|
||||
// Stack frame on entry.
|
||||
// lr: return address
|
||||
// sp[0]: to
|
||||
// sp[4]: from
|
||||
// sp[8]: string
|
||||
|
||||
// This stub is called from the native-call %_SubString(...), so
|
||||
// nothing can be assumed about the arguments. It is tested that:
|
||||
// "string" is a sequential string,
|
||||
// both "from" and "to" are smis, and
|
||||
// 0 <= from <= to <= string.length.
|
||||
// If any of these assumptions fail, we call the runtime system.
|
||||
|
||||
const int kToOffset = 0 * kPointerSize;
|
||||
const int kFromOffset = 1 * kPointerSize;
|
||||
const int kStringOffset = 2 * kPointerSize;
|
||||
|
||||
__ Ldrd(r2, r3, MemOperand(sp, kToOffset));
|
||||
STATIC_ASSERT(kFromOffset == kToOffset + 4);
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
||||
|
||||
// Arithmetic shift right by one un-smi-tags. In this case we rotate right
|
||||
// instead because we bail out on non-smi values: ROR and ASR are equivalent
|
||||
// for smis but they set the flags in a way that's easier to optimize.
|
||||
__ mov(r2, Operand(r2, ROR, 1), SetCC);
|
||||
__ mov(r3, Operand(r3, ROR, 1), SetCC, cc);
|
||||
// If either to or from had the smi tag bit set, then C is set now, and N
|
||||
// has the same value: we rotated by 1, so the bottom bit is now the top bit.
|
||||
// We want to bailout to runtime here if From is negative. In that case, the
|
||||
// next instruction is not executed and we fall through to bailing out to
|
||||
// runtime.
|
||||
// Executed if both r2 and r3 are untagged integers.
|
||||
__ sub(r2, r2, Operand(r3), SetCC, cc);
|
||||
// One of the above un-smis or the above SUB could have set N==1.
|
||||
__ b(mi, &runtime); // Either "from" or "to" is not an smi, or from > to.
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ ldr(r0, MemOperand(sp, kStringOffset));
|
||||
__ JumpIfSmi(r0, &runtime);
|
||||
Condition is_string = masm->IsObjectStringType(r0, r1);
|
||||
__ b(NegateCondition(is_string), &runtime);
|
||||
|
||||
Label single_char;
|
||||
__ cmp(r2, Operand(1));
|
||||
__ b(eq, &single_char);
|
||||
|
||||
// Short-cut for the case of trivial substring.
|
||||
Label return_r0;
|
||||
// r0: original string
|
||||
// r2: result string length
|
||||
__ ldr(r4, FieldMemOperand(r0, String::kLengthOffset));
|
||||
__ cmp(r2, Operand(r4, ASR, 1));
|
||||
// Return original string.
|
||||
__ b(eq, &return_r0);
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ b(hi, &runtime);
|
||||
// Shorter than original string's length: an actual substring.
|
||||
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into r5.
|
||||
// r0: original string
|
||||
// r1: instance type
|
||||
// r2: length
|
||||
// r3: from index (untagged)
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ tst(r1, Operand(kIsIndirectStringMask));
|
||||
__ b(eq, &seq_or_external_string);
|
||||
|
||||
__ tst(r1, Operand(kSlicedNotConsMask));
|
||||
__ b(ne, &sliced_string);
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
__ ldr(r5, FieldMemOperand(r0, ConsString::kSecondOffset));
|
||||
__ CompareRoot(r5, Heap::kempty_stringRootIndex);
|
||||
__ b(ne, &runtime);
|
||||
__ ldr(r5, FieldMemOperand(r0, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ ldr(r1, FieldMemOperand(r5, HeapObject::kMapOffset));
|
||||
__ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and correct start index by offset.
|
||||
__ ldr(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
|
||||
__ ldr(r4, FieldMemOperand(r0, SlicedString::kOffsetOffset));
|
||||
__ add(r3, r3, Operand(r4, ASR, 1)); // Add offset to index.
|
||||
// Update instance type.
|
||||
__ ldr(r1, FieldMemOperand(r5, HeapObject::kMapOffset));
|
||||
__ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the expected register.
|
||||
__ mov(r5, r0);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// r5: underlying subject string
|
||||
// r1: instance type of underlying subject string
|
||||
// r2: length
|
||||
// r3: adjusted start index (untagged)
|
||||
__ cmp(r2, Operand(SlicedString::kMinLength));
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ b(lt, ©_routine);
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ tst(r1, Operand(kStringEncodingMask));
|
||||
__ b(eq, &two_byte_slice);
|
||||
__ AllocateOneByteSlicedString(r0, r2, r6, r4, &runtime);
|
||||
__ jmp(&set_slice_header);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(r0, r2, r6, r4, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ mov(r3, Operand(r3, LSL, 1));
|
||||
__ str(r5, FieldMemOperand(r0, SlicedString::kParentOffset));
|
||||
__ str(r3, FieldMemOperand(r0, SlicedString::kOffsetOffset));
|
||||
__ jmp(&return_r0);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// r5: underlying subject string
|
||||
// r1: instance type of underlying subject string
|
||||
// r2: length
|
||||
// r3: adjusted start index (untagged)
|
||||
Label two_byte_sequential, sequential_string, allocate_result;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ tst(r1, Operand(kExternalStringTag));
|
||||
__ b(eq, &sequential_string);
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ tst(r1, Operand(kShortExternalStringTag));
|
||||
__ b(ne, &runtime);
|
||||
__ ldr(r5, FieldMemOperand(r5, ExternalString::kResourceDataOffset));
|
||||
// r5 already points to the first character of underlying string.
|
||||
__ jmp(&allocate_result);
|
||||
|
||||
__ bind(&sequential_string);
|
||||
// Locate first character of underlying subject string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ add(r5, r5, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&allocate_result);
|
||||
// Sequential acii string. Allocate the result.
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ tst(r1, Operand(kStringEncodingMask));
|
||||
__ b(eq, &two_byte_sequential);
|
||||
|
||||
// Allocate and copy the resulting one-byte string.
|
||||
__ AllocateOneByteString(r0, r2, r4, r6, r1, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ add(r5, r5, r3);
|
||||
// Locate first character of result.
|
||||
__ add(r1, r0, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// r0: result string
|
||||
// r1: first character of result string
|
||||
// r2: result string length
|
||||
// r5: first character of substring to copy
|
||||
STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, r1, r5, r2, r3, String::ONE_BYTE_ENCODING);
|
||||
__ jmp(&return_r0);
|
||||
|
||||
// Allocate and copy the resulting two-byte string.
|
||||
__ bind(&two_byte_sequential);
|
||||
__ AllocateTwoByteString(r0, r2, r4, r6, r1, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
__ add(r5, r5, Operand(r3, LSL, 1));
|
||||
// Locate first character of result.
|
||||
__ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// r0: result string.
|
||||
// r1: first character of result.
|
||||
// r2: result length.
|
||||
// r5: first character of substring to copy.
|
||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, r1, r5, r2, r3, String::TWO_BYTE_ENCODING);
|
||||
|
||||
__ bind(&return_r0);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1, r3, r4);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// r0: original string
|
||||
// r1: instance type
|
||||
// r2: length
|
||||
// r3: from index (untagged)
|
||||
__ SmiTag(r3, r3);
|
||||
StringCharAtGenerator generator(r0, r3, r2, r0, &runtime, &runtime, &runtime,
|
||||
RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes one argument in r0.
|
||||
Label is_number;
|
||||
|
@ -2673,6 +2673,257 @@ void CompareICStub::GenerateMiss(MacroAssembler* masm) {
|
||||
__ Jump(stub_entry);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
ASM_LOCATION("SubStringStub::Generate");
|
||||
Label runtime;
|
||||
|
||||
// Stack frame on entry.
|
||||
// lr: return address
|
||||
// jssp[0]: substring "to" offset
|
||||
// jssp[8]: substring "from" offset
|
||||
// jssp[16]: pointer to string object
|
||||
|
||||
// This stub is called from the native-call %_SubString(...), so
|
||||
// nothing can be assumed about the arguments. It is tested that:
|
||||
// "string" is a sequential string,
|
||||
// both "from" and "to" are smis, and
|
||||
// 0 <= from <= to <= string.length (in debug mode.)
|
||||
// If any of these assumptions fail, we call the runtime system.
|
||||
|
||||
static const int kToOffset = 0 * kPointerSize;
|
||||
static const int kFromOffset = 1 * kPointerSize;
|
||||
static const int kStringOffset = 2 * kPointerSize;
|
||||
|
||||
Register to = x0;
|
||||
Register from = x15;
|
||||
Register input_string = x10;
|
||||
Register input_length = x11;
|
||||
Register input_type = x12;
|
||||
Register result_string = x0;
|
||||
Register result_length = x1;
|
||||
Register temp = x3;
|
||||
|
||||
__ Peek(to, kToOffset);
|
||||
__ Peek(from, kFromOffset);
|
||||
|
||||
// Check that both from and to are smis. If not, jump to runtime.
|
||||
__ JumpIfEitherNotSmi(from, to, &runtime);
|
||||
__ SmiUntag(from);
|
||||
__ SmiUntag(to);
|
||||
|
||||
// Calculate difference between from and to. If to < from, branch to runtime.
|
||||
__ Subs(result_length, to, from);
|
||||
__ B(mi, &runtime);
|
||||
|
||||
// Check from is positive.
|
||||
__ Tbnz(from, kWSignBit, &runtime);
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ Peek(input_string, kStringOffset);
|
||||
__ JumpIfSmi(input_string, &runtime);
|
||||
__ IsObjectJSStringType(input_string, input_type, &runtime);
|
||||
|
||||
Label single_char;
|
||||
__ Cmp(result_length, 1);
|
||||
__ B(eq, &single_char);
|
||||
|
||||
// Short-cut for the case of trivial substring.
|
||||
Label return_x0;
|
||||
__ Ldrsw(input_length,
|
||||
UntagSmiFieldMemOperand(input_string, String::kLengthOffset));
|
||||
|
||||
__ Cmp(result_length, input_length);
|
||||
__ CmovX(x0, input_string, eq);
|
||||
// Return original string.
|
||||
__ B(eq, &return_x0);
|
||||
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ B(hi, &runtime);
|
||||
|
||||
// Shorter than original string's length: an actual substring.
|
||||
|
||||
// x0 to substring end character offset
|
||||
// x1 result_length length of substring result
|
||||
// x10 input_string pointer to input string object
|
||||
// x10 unpacked_string pointer to unpacked string object
|
||||
// x11 input_length length of input string
|
||||
// x12 input_type instance type of input string
|
||||
// x15 from substring start character offset
|
||||
|
||||
// Deal with different string types: update the index if necessary and put
|
||||
// the underlying string into register unpacked_string.
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
Label update_instance_type;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
|
||||
// Test for string types, and branch/fall through to appropriate unpacking
|
||||
// code.
|
||||
__ Tst(input_type, kIsIndirectStringMask);
|
||||
__ B(eq, &seq_or_external_string);
|
||||
__ Tst(input_type, kSlicedNotConsMask);
|
||||
__ B(ne, &sliced_string);
|
||||
|
||||
Register unpacked_string = input_string;
|
||||
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
__ Ldr(temp, FieldMemOperand(input_string, ConsString::kSecondOffset));
|
||||
__ JumpIfNotRoot(temp, Heap::kempty_stringRootIndex, &runtime);
|
||||
__ Ldr(unpacked_string,
|
||||
FieldMemOperand(input_string, ConsString::kFirstOffset));
|
||||
__ B(&update_instance_type);
|
||||
|
||||
__ Bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and correct start index by offset.
|
||||
__ Ldrsw(temp,
|
||||
UntagSmiFieldMemOperand(input_string, SlicedString::kOffsetOffset));
|
||||
__ Add(from, from, temp);
|
||||
__ Ldr(unpacked_string,
|
||||
FieldMemOperand(input_string, SlicedString::kParentOffset));
|
||||
|
||||
__ Bind(&update_instance_type);
|
||||
__ Ldr(temp, FieldMemOperand(unpacked_string, HeapObject::kMapOffset));
|
||||
__ Ldrb(input_type, FieldMemOperand(temp, Map::kInstanceTypeOffset));
|
||||
// Now control must go to &underlying_unpacked. Since the no code is generated
|
||||
// before then we fall through instead of generating a useless branch.
|
||||
|
||||
__ Bind(&seq_or_external_string);
|
||||
// Sequential or external string. Registers unpacked_string and input_string
|
||||
// alias, so there's nothing to do here.
|
||||
// Note that if code is added here, the above code must be updated.
|
||||
|
||||
// x0 result_string pointer to result string object (uninit)
|
||||
// x1 result_length length of substring result
|
||||
// x10 unpacked_string pointer to unpacked string object
|
||||
// x11 input_length length of input string
|
||||
// x12 input_type instance type of input string
|
||||
// x15 from substring start character offset
|
||||
__ Bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
__ Cmp(result_length, SlicedString::kMinLength);
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ B(lt, ©_routine);
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyway due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ Tbz(input_type, MaskToBit(kStringEncodingMask), &two_byte_slice);
|
||||
__ AllocateOneByteSlicedString(result_string, result_length, x3, x4,
|
||||
&runtime);
|
||||
__ B(&set_slice_header);
|
||||
|
||||
__ Bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(result_string, result_length, x3, x4,
|
||||
&runtime);
|
||||
|
||||
__ Bind(&set_slice_header);
|
||||
__ SmiTag(from);
|
||||
__ Str(from, FieldMemOperand(result_string, SlicedString::kOffsetOffset));
|
||||
__ Str(unpacked_string,
|
||||
FieldMemOperand(result_string, SlicedString::kParentOffset));
|
||||
__ B(&return_x0);
|
||||
|
||||
__ Bind(©_routine);
|
||||
}
|
||||
|
||||
// x0 result_string pointer to result string object (uninit)
|
||||
// x1 result_length length of substring result
|
||||
// x10 unpacked_string pointer to unpacked string object
|
||||
// x11 input_length length of input string
|
||||
// x12 input_type instance type of input string
|
||||
// x13 unpacked_char0 pointer to first char of unpacked string (uninit)
|
||||
// x13 substring_char0 pointer to first char of substring (uninit)
|
||||
// x14 result_char0 pointer to first char of result (uninit)
|
||||
// x15 from substring start character offset
|
||||
Register unpacked_char0 = x13;
|
||||
Register substring_char0 = x13;
|
||||
Register result_char0 = x14;
|
||||
Label two_byte_sequential, sequential_string, allocate_result;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
|
||||
__ Tst(input_type, kExternalStringTag);
|
||||
__ B(eq, &sequential_string);
|
||||
|
||||
__ Tst(input_type, kShortExternalStringTag);
|
||||
__ B(ne, &runtime);
|
||||
__ Ldr(unpacked_char0,
|
||||
FieldMemOperand(unpacked_string, ExternalString::kResourceDataOffset));
|
||||
// unpacked_char0 points to the first character of the underlying string.
|
||||
__ B(&allocate_result);
|
||||
|
||||
__ Bind(&sequential_string);
|
||||
// Locate first character of underlying subject string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ Add(unpacked_char0, unpacked_string,
|
||||
SeqOneByteString::kHeaderSize - kHeapObjectTag);
|
||||
|
||||
__ Bind(&allocate_result);
|
||||
// Sequential one-byte string. Allocate the result.
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ Tbz(input_type, MaskToBit(kStringEncodingMask), &two_byte_sequential);
|
||||
|
||||
// Allocate and copy the resulting one-byte string.
|
||||
__ AllocateOneByteString(result_string, result_length, x3, x4, x5, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ Add(substring_char0, unpacked_char0, from);
|
||||
|
||||
// Locate first character of result.
|
||||
__ Add(result_char0, result_string,
|
||||
SeqOneByteString::kHeaderSize - kHeapObjectTag);
|
||||
|
||||
STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
__ CopyBytes(result_char0, substring_char0, result_length, x3, kCopyLong);
|
||||
__ B(&return_x0);
|
||||
|
||||
// Allocate and copy the resulting two-byte string.
|
||||
__ Bind(&two_byte_sequential);
|
||||
__ AllocateTwoByteString(result_string, result_length, x3, x4, x5, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ Add(substring_char0, unpacked_char0, Operand(from, LSL, 1));
|
||||
|
||||
// Locate first character of result.
|
||||
__ Add(result_char0, result_string,
|
||||
SeqTwoByteString::kHeaderSize - kHeapObjectTag);
|
||||
|
||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
__ Add(result_length, result_length, result_length);
|
||||
__ CopyBytes(result_char0, substring_char0, result_length, x3, kCopyLong);
|
||||
|
||||
__ Bind(&return_x0);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1, x3, x4);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
|
||||
__ Bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// x1: result_length
|
||||
// x10: input_string
|
||||
// x12: input_type
|
||||
// x15: from (untagged)
|
||||
__ SmiTag(from);
|
||||
StringCharAtGenerator generator(input_string, from, result_length, x0,
|
||||
&runtime, &runtime, &runtime,
|
||||
RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes one argument in x0.
|
||||
Label is_number;
|
||||
|
@ -337,18 +337,10 @@ Node* CodeStubAssembler::SmiSubWithOverflow(Node* a, Node* b) {
|
||||
|
||||
Node* CodeStubAssembler::SmiEqual(Node* a, Node* b) { return WordEqual(a, b); }
|
||||
|
||||
Node* CodeStubAssembler::SmiAbove(Node* a, Node* b) {
|
||||
return UintPtrGreaterThan(a, b);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::SmiAboveOrEqual(Node* a, Node* b) {
|
||||
return UintPtrGreaterThanOrEqual(a, b);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::SmiBelow(Node* a, Node* b) {
|
||||
return UintPtrLessThan(a, b);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::SmiLessThan(Node* a, Node* b) {
|
||||
return IntPtrLessThan(a, b);
|
||||
}
|
||||
@ -505,11 +497,6 @@ Node* CodeStubAssembler::WordIsPositiveSmi(Node* a) {
|
||||
IntPtrConstant(0));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::WordIsNotPositiveSmi(Node* a) {
|
||||
return WordNotEqual(WordAnd(a, IntPtrConstant(kSmiTagMask | kSmiSignMask)),
|
||||
IntPtrConstant(0));
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIfSameValueZero(Node* a, Node* b, Node* context,
|
||||
Label* if_true, Label* if_false) {
|
||||
Node* number_map = HeapNumberMapConstant();
|
||||
@ -1395,44 +1382,11 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length) {
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::AllocateSlicedOneByteString(Node* length, Node* parent,
|
||||
Node* offset) {
|
||||
Node* result = Allocate(SlicedString::kSize);
|
||||
Node* map = LoadRoot(Heap::kSlicedOneByteStringMapRootIndex);
|
||||
StoreMapNoWriteBarrier(result, map);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kLengthOffset, length,
|
||||
MachineRepresentation::kTagged);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kHashFieldOffset,
|
||||
Int32Constant(String::kEmptyHashField),
|
||||
MachineRepresentation::kWord32);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kParentOffset, parent,
|
||||
MachineRepresentation::kTagged);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kOffsetOffset, offset,
|
||||
MachineRepresentation::kTagged);
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::AllocateSlicedTwoByteString(Node* length, Node* parent,
|
||||
Node* offset) {
|
||||
Node* result = Allocate(SlicedString::kSize);
|
||||
Node* map = LoadRoot(Heap::kSlicedStringMapRootIndex);
|
||||
StoreMapNoWriteBarrier(result, map);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kLengthOffset, length,
|
||||
MachineRepresentation::kTagged);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kHashFieldOffset,
|
||||
Int32Constant(String::kEmptyHashField),
|
||||
MachineRepresentation::kWord32);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kParentOffset, parent,
|
||||
MachineRepresentation::kTagged);
|
||||
StoreObjectFieldNoWriteBarrier(result, SlicedString::kOffsetOffset, offset,
|
||||
MachineRepresentation::kTagged);
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::AllocateUninitializedJSArrayWithoutElements(
|
||||
ElementsKind kind, Node* array_map, Node* length, Node* allocation_site) {
|
||||
Comment("begin allocation of JSArray without elements");
|
||||
int base_size = JSArray::kSize;
|
||||
|
||||
if (allocation_site != nullptr) {
|
||||
base_size += AllocationMemento::kSize;
|
||||
}
|
||||
@ -1765,75 +1719,6 @@ void CodeStubAssembler::CopyFixedArrayElements(
|
||||
Comment("] CopyFixedArrayElements");
|
||||
}
|
||||
|
||||
void CodeStubAssembler::CopyStringCharacters(compiler::Node* from_string,
|
||||
compiler::Node* to_string,
|
||||
compiler::Node* from_index,
|
||||
compiler::Node* character_count,
|
||||
String::Encoding encoding) {
|
||||
Label out(this);
|
||||
|
||||
// Nothing to do for zero characters.
|
||||
|
||||
GotoIf(SmiLessThanOrEqual(character_count, SmiConstant(Smi::FromInt(0))),
|
||||
&out);
|
||||
|
||||
// Calculate offsets into the strings.
|
||||
|
||||
Node* from_offset;
|
||||
Node* limit_offset;
|
||||
Node* to_offset;
|
||||
|
||||
{
|
||||
Node* byte_count = SmiToWord32(character_count);
|
||||
Node* from_byte_index = SmiToWord32(from_index);
|
||||
if (encoding == String::ONE_BYTE_ENCODING) {
|
||||
const int offset = SeqOneByteString::kHeaderSize - kHeapObjectTag;
|
||||
from_offset = IntPtrAdd(IntPtrConstant(offset), from_byte_index);
|
||||
limit_offset = IntPtrAdd(from_offset, byte_count);
|
||||
to_offset = IntPtrConstant(offset);
|
||||
} else {
|
||||
STATIC_ASSERT(2 == sizeof(uc16));
|
||||
byte_count = WordShl(byte_count, 1);
|
||||
from_byte_index = WordShl(from_byte_index, 1);
|
||||
|
||||
const int offset = SeqTwoByteString::kHeaderSize - kHeapObjectTag;
|
||||
from_offset = IntPtrAdd(IntPtrConstant(offset), from_byte_index);
|
||||
limit_offset = IntPtrAdd(from_offset, byte_count);
|
||||
to_offset = IntPtrConstant(offset);
|
||||
}
|
||||
}
|
||||
|
||||
Variable var_from_offset(this, MachineType::PointerRepresentation());
|
||||
Variable var_to_offset(this, MachineType::PointerRepresentation());
|
||||
|
||||
var_from_offset.Bind(from_offset);
|
||||
var_to_offset.Bind(to_offset);
|
||||
|
||||
Variable* vars[] = {&var_from_offset, &var_to_offset};
|
||||
Label decrement(this, 2, vars);
|
||||
|
||||
Label loop(this, 2, vars);
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
from_offset = var_from_offset.value();
|
||||
to_offset = var_to_offset.value();
|
||||
|
||||
// TODO(jgruber): We could make this faster through larger copy unit sizes.
|
||||
Node* value = Load(MachineType::Uint8(), from_string, from_offset);
|
||||
StoreNoWriteBarrier(MachineRepresentation::kWord8, to_string, to_offset,
|
||||
value);
|
||||
|
||||
Node* new_from_offset = IntPtrAdd(from_offset, IntPtrConstant(1));
|
||||
var_from_offset.Bind(new_from_offset);
|
||||
var_to_offset.Bind(IntPtrAdd(to_offset, IntPtrConstant(1)));
|
||||
|
||||
Branch(WordNotEqual(new_from_offset, limit_offset), &loop, &out);
|
||||
}
|
||||
|
||||
Bind(&out);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadElementAndPrepareForStore(Node* array,
|
||||
Node* offset,
|
||||
ElementsKind from_kind,
|
||||
@ -2544,250 +2429,6 @@ Node* CodeStubAssembler::StringFromCharCode(Node* code) {
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::SubString(Node* context, Node* string, Node* from,
|
||||
Node* to) {
|
||||
Label end(this);
|
||||
Label runtime(this);
|
||||
|
||||
Variable var_instance_type(this, MachineRepresentation::kWord8); // Int32.
|
||||
Variable var_result(this, MachineRepresentation::kTagged); // String.
|
||||
Variable var_from(this, MachineRepresentation::kTagged); // Smi.
|
||||
Variable var_string(this, MachineRepresentation::kTagged); // String.
|
||||
|
||||
var_instance_type.Bind(Int32Constant(0));
|
||||
var_string.Bind(string);
|
||||
var_from.Bind(from);
|
||||
|
||||
// Make sure first argument is a string.
|
||||
|
||||
// Bailout if receiver is a Smi.
|
||||
GotoIf(WordIsSmi(string), &runtime);
|
||||
|
||||
// Load the instance type of the {string}.
|
||||
Node* const instance_type = LoadInstanceType(string);
|
||||
var_instance_type.Bind(instance_type);
|
||||
|
||||
// Check if {string} is a String.
|
||||
GotoIf(Int32GreaterThanOrEqual(instance_type,
|
||||
Int32Constant(FIRST_NONSTRING_TYPE)),
|
||||
&runtime);
|
||||
|
||||
// Make sure that both from and to are non-negative smis.
|
||||
|
||||
GotoIf(WordIsNotPositiveSmi(from), &runtime);
|
||||
GotoIf(WordIsNotPositiveSmi(to), &runtime);
|
||||
|
||||
Node* const substr_length = SmiSub(to, from);
|
||||
Node* const string_length = LoadStringLength(string);
|
||||
|
||||
// Begin dispatching based on substring length.
|
||||
|
||||
Label original_string_or_invalid_length(this);
|
||||
GotoIf(SmiAboveOrEqual(substr_length, string_length),
|
||||
&original_string_or_invalid_length);
|
||||
|
||||
// A real substring (substr_length < string_length).
|
||||
|
||||
Label single_char(this);
|
||||
GotoIf(SmiEqual(substr_length, SmiConstant(Smi::FromInt(1))), &single_char);
|
||||
|
||||
// TODO(jgruber): Add an additional case for substring of length == 0?
|
||||
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into var_string.
|
||||
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
Label underlying_unpacked(this);
|
||||
GotoIf(Word32Equal(
|
||||
Word32And(instance_type, Int32Constant(kIsIndirectStringMask)),
|
||||
Int32Constant(0)),
|
||||
&underlying_unpacked);
|
||||
|
||||
// The subject string is either a sliced or cons string.
|
||||
|
||||
Label sliced_string(this);
|
||||
GotoIf(Word32NotEqual(
|
||||
Word32And(instance_type, Int32Constant(kSlicedNotConsMask)),
|
||||
Int32Constant(0)),
|
||||
&sliced_string);
|
||||
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
// Flat cons strings have an empty second part.
|
||||
{
|
||||
GotoIf(WordNotEqual(LoadObjectField(string, ConsString::kSecondOffset),
|
||||
EmptyStringConstant()),
|
||||
&runtime);
|
||||
|
||||
Node* first_string_part = LoadObjectField(string, ConsString::kFirstOffset);
|
||||
var_string.Bind(first_string_part);
|
||||
var_instance_type.Bind(LoadInstanceType(first_string_part));
|
||||
|
||||
Goto(&underlying_unpacked);
|
||||
}
|
||||
|
||||
Bind(&sliced_string);
|
||||
{
|
||||
// Fetch parent and correct start index by offset.
|
||||
Node* sliced_offset = LoadObjectField(string, SlicedString::kOffsetOffset);
|
||||
var_from.Bind(SmiAdd(from, sliced_offset));
|
||||
|
||||
Node* slice_parent = LoadObjectField(string, SlicedString::kParentOffset);
|
||||
var_string.Bind(slice_parent);
|
||||
|
||||
Node* slice_parent_instance_type = LoadInstanceType(slice_parent);
|
||||
var_instance_type.Bind(slice_parent_instance_type);
|
||||
|
||||
Goto(&underlying_unpacked);
|
||||
}
|
||||
|
||||
// The subject string can only be external or sequential string of either
|
||||
// encoding at this point.
|
||||
Bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine(this);
|
||||
|
||||
// Short slice. Copy instead of slicing.
|
||||
GotoIf(SmiLessThan(substr_length,
|
||||
SmiConstant(Smi::FromInt(SlicedString::kMinLength))),
|
||||
©_routine);
|
||||
|
||||
// Allocate new sliced string.
|
||||
|
||||
Label two_byte_slice(this);
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
|
||||
Counters* counters = isolate()->counters();
|
||||
IncrementCounter(counters->sub_string_native(), 1);
|
||||
|
||||
GotoIf(Word32Equal(Word32And(var_instance_type.value(),
|
||||
Int32Constant(kStringEncodingMask)),
|
||||
Int32Constant(0)),
|
||||
&two_byte_slice);
|
||||
|
||||
var_result.Bind(AllocateSlicedOneByteString(
|
||||
substr_length, var_string.value(), var_from.value()));
|
||||
Goto(&end);
|
||||
|
||||
Bind(&two_byte_slice);
|
||||
|
||||
var_result.Bind(AllocateSlicedTwoByteString(
|
||||
substr_length, var_string.value(), var_from.value()));
|
||||
Goto(&end);
|
||||
|
||||
Bind(©_routine);
|
||||
}
|
||||
|
||||
// The subject string can only be external or sequential string of either
|
||||
// encoding at this point.
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
Label sequential_string(this);
|
||||
GotoIf(Word32Equal(Word32And(var_instance_type.value(),
|
||||
Int32Constant(kExternalStringTag)),
|
||||
Int32Constant(0)),
|
||||
&sequential_string);
|
||||
|
||||
// Handle external string.
|
||||
{
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
GotoIf(Word32NotEqual(Word32And(var_instance_type.value(),
|
||||
Int32Constant(kShortExternalStringMask)),
|
||||
Int32Constant(0)),
|
||||
&runtime);
|
||||
|
||||
// Move the pointer so that offset-wise, it looks like a sequential string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize ==
|
||||
SeqOneByteString::kHeaderSize);
|
||||
|
||||
Node* resource_data =
|
||||
LoadObjectField(var_string.value(), ExternalString::kResourceDataOffset,
|
||||
MachineType::AnyTagged());
|
||||
var_string.Bind(IntPtrSub(
|
||||
resource_data,
|
||||
Int32Constant(SeqTwoByteString::kHeaderSize - kHeapObjectTag)));
|
||||
|
||||
Goto(&sequential_string);
|
||||
}
|
||||
|
||||
Label two_byte_sequential(this);
|
||||
Bind(&sequential_string);
|
||||
{
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
GotoIf(Word32Equal(Word32And(var_instance_type.value(),
|
||||
Int32Constant(kStringEncodingMask)),
|
||||
Int32Constant(0)),
|
||||
&two_byte_sequential);
|
||||
}
|
||||
|
||||
// The subject string is a sequential one-byte string.
|
||||
{
|
||||
Node* result = AllocateSeqOneByteString(context, SmiToWord(substr_length));
|
||||
CopyStringCharacters(var_string.value(), result, var_from.value(),
|
||||
substr_length, String::ONE_BYTE_ENCODING);
|
||||
var_result.Bind(result);
|
||||
|
||||
Counters* counters = isolate()->counters();
|
||||
IncrementCounter(counters->sub_string_native(), 1);
|
||||
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
// The subject string is a sequential two-byte string.
|
||||
Bind(&two_byte_sequential);
|
||||
{
|
||||
Node* result = AllocateSeqTwoByteString(context, SmiToWord(substr_length));
|
||||
CopyStringCharacters(var_string.value(), result, var_from.value(),
|
||||
substr_length, String::TWO_BYTE_ENCODING);
|
||||
var_result.Bind(result);
|
||||
|
||||
Counters* counters = isolate()->counters();
|
||||
IncrementCounter(counters->sub_string_native(), 1);
|
||||
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
// Substrings of length 1 are generated through CharCodeAt and FromCharCode.
|
||||
Bind(&single_char);
|
||||
{
|
||||
Node* char_code = StringCharCodeAt(var_string.value(), var_from.value());
|
||||
var_result.Bind(StringFromCharCode(char_code));
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
Bind(&original_string_or_invalid_length);
|
||||
{
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
GotoIf(SmiAbove(substr_length, string_length), &runtime);
|
||||
|
||||
// Equal length - check if {from, to} == {0, str.length}.
|
||||
GotoIf(SmiAbove(from, SmiConstant(Smi::FromInt(0))), &runtime);
|
||||
|
||||
// Return the original string (substr_length == string_length).
|
||||
|
||||
Counters* counters = isolate()->counters();
|
||||
IncrementCounter(counters->sub_string_native(), 1);
|
||||
|
||||
var_result.Bind(string);
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
// Fall back to a runtime call.
|
||||
Bind(&runtime);
|
||||
{
|
||||
var_result.Bind(
|
||||
CallRuntime(Runtime::kSubString, context, string, from, to));
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
Bind(&end);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::StringToNumber(Node* context, Node* input) {
|
||||
Label runtime(this, Label::kDeferred);
|
||||
Label end(this);
|
||||
|
@ -111,9 +111,7 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* SmiSub(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiSubWithOverflow(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiEqual(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiAbove(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiAboveOrEqual(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiBelow(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiLessThan(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiLessThanOrEqual(compiler::Node* a, compiler::Node* b);
|
||||
compiler::Node* SmiMin(compiler::Node* a, compiler::Node* b);
|
||||
@ -136,10 +134,8 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
|
||||
// Check a value for smi-ness
|
||||
compiler::Node* WordIsSmi(compiler::Node* a);
|
||||
// Check that the value is a non-negative smi.
|
||||
// Check that the value is a positive smi.
|
||||
compiler::Node* WordIsPositiveSmi(compiler::Node* a);
|
||||
// Check that the value is a negative smi.
|
||||
compiler::Node* WordIsNotPositiveSmi(compiler::Node* a);
|
||||
|
||||
void BranchIfSmiEqual(compiler::Node* a, compiler::Node* b, Label* if_true,
|
||||
Label* if_false) {
|
||||
@ -329,18 +325,6 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* AllocateSeqTwoByteString(int length);
|
||||
compiler::Node* AllocateSeqTwoByteString(compiler::Node* context,
|
||||
compiler::Node* length);
|
||||
|
||||
// Allocate a SlicedOneByteString with the given length, parent and offset.
|
||||
// |length| and |offset| are expected to be tagged.
|
||||
compiler::Node* AllocateSlicedOneByteString(compiler::Node* length,
|
||||
compiler::Node* parent,
|
||||
compiler::Node* offset);
|
||||
// Allocate a SlicedTwoByteString with the given length, parent and offset.
|
||||
// |length| and |offset| are expected to be tagged.
|
||||
compiler::Node* AllocateSlicedTwoByteString(compiler::Node* length,
|
||||
compiler::Node* parent,
|
||||
compiler::Node* offset);
|
||||
|
||||
// Allocate a JSArray without elements and initialize the header fields.
|
||||
compiler::Node* AllocateUninitializedJSArrayWithoutElements(
|
||||
ElementsKind kind, compiler::Node* array_map, compiler::Node* length,
|
||||
@ -391,16 +375,6 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER,
|
||||
ParameterMode mode = INTEGER_PARAMETERS);
|
||||
|
||||
// Copies |character_count| elements from |from_string| to |to_string|
|
||||
// starting at the |from_index|'th character. |from_index| and
|
||||
// |character_count| must be Smis s.t.
|
||||
// 0 <= |from_index| <= |from_index| + |character_count| < from_string.length.
|
||||
void CopyStringCharacters(compiler::Node* from_string,
|
||||
compiler::Node* to_string,
|
||||
compiler::Node* from_index,
|
||||
compiler::Node* character_count,
|
||||
String::Encoding encoding);
|
||||
|
||||
// Loads an element from |array| of |from_kind| elements by given |offset|
|
||||
// (NOTE: not index!), does a hole check if |if_hole| is provided and
|
||||
// converts the value so that it becomes ready for storing to array of
|
||||
@ -472,10 +446,6 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
compiler::Node* smi_index);
|
||||
// Return the single character string with only {code}.
|
||||
compiler::Node* StringFromCharCode(compiler::Node* code);
|
||||
// Return a new string object which holds a substring containing the range
|
||||
// [from,to[ of string. |from| and |to| are expected to be tagged.
|
||||
compiler::Node* SubString(compiler::Node* context, compiler::Node* string,
|
||||
compiler::Node* from, compiler::Node* to);
|
||||
|
||||
// Type conversion helpers.
|
||||
// Convert a String to a Number.
|
||||
|
@ -2688,15 +2688,6 @@ compiler::Node* DecStub::Generate(CodeStubAssembler* assembler,
|
||||
return result_var.value();
|
||||
}
|
||||
|
||||
// ES6 section 21.1.3.19 String.prototype.substring ( start, end )
|
||||
compiler::Node* SubStringStub::Generate(CodeStubAssembler* assembler,
|
||||
compiler::Node* string,
|
||||
compiler::Node* from,
|
||||
compiler::Node* to,
|
||||
compiler::Node* context) {
|
||||
return assembler->SubString(context, string, from, to);
|
||||
}
|
||||
|
||||
// ES6 section 7.1.13 ToObject (argument)
|
||||
void ToObjectStub::GenerateAssembly(CodeStubAssembler* assembler) const {
|
||||
typedef compiler::Node Node;
|
||||
|
@ -3112,24 +3112,13 @@ class StoreBufferOverflowStub : public PlatformCodeStub {
|
||||
DEFINE_PLATFORM_CODE_STUB(StoreBufferOverflow, PlatformCodeStub);
|
||||
};
|
||||
|
||||
class SubStringStub : public TurboFanCodeStub {
|
||||
|
||||
class SubStringStub : public PlatformCodeStub {
|
||||
public:
|
||||
explicit SubStringStub(Isolate* isolate) : TurboFanCodeStub(isolate) {}
|
||||
|
||||
static compiler::Node* Generate(CodeStubAssembler* assembler,
|
||||
compiler::Node* string, compiler::Node* from,
|
||||
compiler::Node* to, compiler::Node* context);
|
||||
|
||||
void GenerateAssembly(CodeStubAssembler* assembler) const override {
|
||||
assembler->Return(Generate(assembler,
|
||||
assembler->Parameter(Descriptor::kString),
|
||||
assembler->Parameter(Descriptor::kFrom),
|
||||
assembler->Parameter(Descriptor::kTo),
|
||||
assembler->Parameter(Descriptor::kContext)));
|
||||
}
|
||||
explicit SubStringStub(Isolate* isolate) : PlatformCodeStub(isolate) {}
|
||||
|
||||
DEFINE_CALL_INTERFACE_DESCRIPTOR(SubString);
|
||||
DEFINE_CODE_STUB(SubString, TurboFanCodeStub);
|
||||
DEFINE_PLATFORM_CODE_STUB(SubString, PlatformCodeStub);
|
||||
};
|
||||
|
||||
class ToStringStub final : public PlatformCodeStub {
|
||||
|
@ -568,7 +568,6 @@ void FullCodeGenerator::EmitSubString(CallRuntime* expr) {
|
||||
VisitForStackValue(args->at(1));
|
||||
VisitForStackValue(args->at(2));
|
||||
__ CallStub(&stub);
|
||||
RestoreContext();
|
||||
OperandStackDepthDecrement(3);
|
||||
context()->Plug(result_register());
|
||||
}
|
||||
|
@ -2065,6 +2065,227 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
|
||||
// Stack frame on entry.
|
||||
// esp[0]: return address
|
||||
// esp[4]: to
|
||||
// esp[8]: from
|
||||
// esp[12]: string
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ mov(eax, Operand(esp, 3 * kPointerSize));
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
__ JumpIfSmi(eax, &runtime);
|
||||
Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
|
||||
__ j(NegateCondition(is_string), &runtime);
|
||||
|
||||
// eax: string
|
||||
// ebx: instance type
|
||||
|
||||
// Calculate length of sub string using the smi values.
|
||||
__ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
|
||||
__ JumpIfNotSmi(ecx, &runtime);
|
||||
__ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
|
||||
__ JumpIfNotSmi(edx, &runtime);
|
||||
__ sub(ecx, edx);
|
||||
__ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
|
||||
Label not_original_string;
|
||||
// Shorter than original string's length: an actual substring.
|
||||
__ j(below, ¬_original_string, Label::kNear);
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ j(above, &runtime);
|
||||
// Return original string.
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
__ bind(¬_original_string);
|
||||
|
||||
Label single_char;
|
||||
__ cmp(ecx, Immediate(Smi::FromInt(1)));
|
||||
__ j(equal, &single_char);
|
||||
|
||||
// eax: string
|
||||
// ebx: instance type
|
||||
// ecx: sub string length (smi)
|
||||
// edx: from index (smi)
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into edi.
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ test(ebx, Immediate(kIsIndirectStringMask));
|
||||
__ j(zero, &seq_or_external_string, Label::kNear);
|
||||
|
||||
Factory* factory = isolate()->factory();
|
||||
__ test(ebx, Immediate(kSlicedNotConsMask));
|
||||
__ j(not_zero, &sliced_string, Label::kNear);
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
// Flat cons strings have an empty second part.
|
||||
__ cmp(FieldOperand(eax, ConsString::kSecondOffset),
|
||||
factory->empty_string());
|
||||
__ j(not_equal, &runtime);
|
||||
__ mov(edi, FieldOperand(eax, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
|
||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked, Label::kNear);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and adjust start index by offset.
|
||||
__ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset));
|
||||
__ mov(edi, FieldOperand(eax, SlicedString::kParentOffset));
|
||||
// Update instance type.
|
||||
__ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
|
||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked, Label::kNear);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the expected register.
|
||||
__ mov(edi, eax);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// edi: underlying subject string
|
||||
// ebx: instance type of underlying subject string
|
||||
// edx: adjusted start index (smi)
|
||||
// ecx: length (smi)
|
||||
__ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ j(less, ©_routine);
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ test(ebx, Immediate(kStringEncodingMask));
|
||||
__ j(zero, &two_byte_slice, Label::kNear);
|
||||
__ AllocateOneByteSlicedString(eax, ebx, no_reg, &runtime);
|
||||
__ jmp(&set_slice_header, Label::kNear);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
|
||||
__ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
|
||||
Immediate(String::kEmptyHashField));
|
||||
__ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
|
||||
__ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// edi: underlying subject string
|
||||
// ebx: instance type of underlying subject string
|
||||
// edx: adjusted start index (smi)
|
||||
// ecx: length (smi)
|
||||
// The subject string can only be external or sequential string of either
|
||||
// encoding at this point.
|
||||
Label two_byte_sequential, runtime_drop_two, sequential_string;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ test_b(ebx, Immediate(kExternalStringTag));
|
||||
__ j(zero, &sequential_string);
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ test_b(ebx, Immediate(kShortExternalStringMask));
|
||||
__ j(not_zero, &runtime);
|
||||
__ mov(edi, FieldOperand(edi, ExternalString::kResourceDataOffset));
|
||||
// Move the pointer so that offset-wise, it looks like a sequential string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&sequential_string);
|
||||
// Stash away (adjusted) index and (underlying) string.
|
||||
__ push(edx);
|
||||
__ push(edi);
|
||||
__ SmiUntag(ecx);
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ test_b(ebx, Immediate(kStringEncodingMask));
|
||||
__ j(zero, &two_byte_sequential);
|
||||
|
||||
// Sequential one byte string. Allocate the result.
|
||||
__ AllocateOneByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
|
||||
|
||||
// eax: result string
|
||||
// ecx: result string length
|
||||
// Locate first character of result.
|
||||
__ mov(edi, eax);
|
||||
__ add(edi, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
// Load string argument and locate character of sub string start.
|
||||
__ pop(edx);
|
||||
__ pop(ebx);
|
||||
__ SmiUntag(ebx);
|
||||
__ lea(edx, FieldOperand(edx, ebx, times_1, SeqOneByteString::kHeaderSize));
|
||||
|
||||
// eax: result string
|
||||
// ecx: result length
|
||||
// edi: first character of result
|
||||
// edx: character of sub string start
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, edi, edx, ecx, ebx, String::ONE_BYTE_ENCODING);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
__ bind(&two_byte_sequential);
|
||||
// Sequential two-byte string. Allocate the result.
|
||||
__ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
|
||||
|
||||
// eax: result string
|
||||
// ecx: result string length
|
||||
// Locate first character of result.
|
||||
__ mov(edi, eax);
|
||||
__ add(edi,
|
||||
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
// Load string argument and locate character of sub string start.
|
||||
__ pop(edx);
|
||||
__ pop(ebx);
|
||||
// As from is a smi it is 2 times the value which matches the size of a two
|
||||
// byte character.
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
||||
__ lea(edx, FieldOperand(edx, ebx, times_1, SeqTwoByteString::kHeaderSize));
|
||||
|
||||
// eax: result string
|
||||
// ecx: result length
|
||||
// edi: first character of result
|
||||
// edx: character of sub string start
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, edi, edx, ecx, ebx, String::TWO_BYTE_ENCODING);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
// Drop pushed values on the stack before tail call.
|
||||
__ bind(&runtime_drop_two);
|
||||
__ Drop(2);
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// eax: string
|
||||
// ebx: instance type
|
||||
// ecx: sub string length (smi)
|
||||
// edx: from index (smi)
|
||||
StringCharAtGenerator generator(eax, edx, ecx, eax, &runtime, &runtime,
|
||||
&runtime, RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ ret(3 * kPointerSize);
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes one argument in eax.
|
||||
Label is_number;
|
||||
|
@ -2268,6 +2268,229 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
// Stack frame on entry.
|
||||
// ra: return address
|
||||
// sp[0]: to
|
||||
// sp[4]: from
|
||||
// sp[8]: string
|
||||
|
||||
// This stub is called from the native-call %_SubString(...), so
|
||||
// nothing can be assumed about the arguments. It is tested that:
|
||||
// "string" is a sequential string,
|
||||
// both "from" and "to" are smis, and
|
||||
// 0 <= from <= to <= string.length.
|
||||
// If any of these assumptions fail, we call the runtime system.
|
||||
|
||||
const int kToOffset = 0 * kPointerSize;
|
||||
const int kFromOffset = 1 * kPointerSize;
|
||||
const int kStringOffset = 2 * kPointerSize;
|
||||
|
||||
__ lw(a2, MemOperand(sp, kToOffset));
|
||||
__ lw(a3, MemOperand(sp, kFromOffset));
|
||||
STATIC_ASSERT(kFromOffset == kToOffset + 4);
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
||||
|
||||
// Utilize delay slots. SmiUntag doesn't emit a jump, everything else is
|
||||
// safe in this case.
|
||||
__ UntagAndJumpIfNotSmi(a2, a2, &runtime);
|
||||
__ UntagAndJumpIfNotSmi(a3, a3, &runtime);
|
||||
// Both a2 and a3 are untagged integers.
|
||||
|
||||
__ Branch(&runtime, lt, a3, Operand(zero_reg)); // From < 0.
|
||||
|
||||
__ Branch(&runtime, gt, a3, Operand(a2)); // Fail if from > to.
|
||||
__ Subu(a2, a2, a3);
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ lw(v0, MemOperand(sp, kStringOffset));
|
||||
__ JumpIfSmi(v0, &runtime);
|
||||
__ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
|
||||
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||
__ And(t0, a1, Operand(kIsNotStringMask));
|
||||
|
||||
__ Branch(&runtime, ne, t0, Operand(zero_reg));
|
||||
|
||||
Label single_char;
|
||||
__ Branch(&single_char, eq, a2, Operand(1));
|
||||
|
||||
// Short-cut for the case of trivial substring.
|
||||
Label return_v0;
|
||||
// v0: original string
|
||||
// a2: result string length
|
||||
__ lw(t0, FieldMemOperand(v0, String::kLengthOffset));
|
||||
__ sra(t0, t0, 1);
|
||||
// Return original string.
|
||||
__ Branch(&return_v0, eq, a2, Operand(t0));
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ Branch(&runtime, hi, a2, Operand(t0));
|
||||
// Shorter than original string's length: an actual substring.
|
||||
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into t1.
|
||||
// v0: original string
|
||||
// a1: instance type
|
||||
// a2: length
|
||||
// a3: from index (untagged)
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ And(t0, a1, Operand(kIsIndirectStringMask));
|
||||
__ Branch(USE_DELAY_SLOT, &seq_or_external_string, eq, t0, Operand(zero_reg));
|
||||
// t0 is used as a scratch register and can be overwritten in either case.
|
||||
__ And(t0, a1, Operand(kSlicedNotConsMask));
|
||||
__ Branch(&sliced_string, ne, t0, Operand(zero_reg));
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
__ lw(t1, FieldMemOperand(v0, ConsString::kSecondOffset));
|
||||
__ LoadRoot(t0, Heap::kempty_stringRootIndex);
|
||||
__ Branch(&runtime, ne, t1, Operand(t0));
|
||||
__ lw(t1, FieldMemOperand(v0, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset));
|
||||
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and correct start index by offset.
|
||||
__ lw(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
|
||||
__ lw(t0, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
||||
__ sra(t0, t0, 1); // Add offset to index.
|
||||
__ Addu(a3, a3, t0);
|
||||
// Update instance type.
|
||||
__ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset));
|
||||
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the expected register.
|
||||
__ mov(t1, v0);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// t1: underlying subject string
|
||||
// a1: instance type of underlying subject string
|
||||
// a2: length
|
||||
// a3: adjusted start index (untagged)
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ Branch(©_routine, lt, a2, Operand(SlicedString::kMinLength));
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ And(t0, a1, Operand(kStringEncodingMask));
|
||||
__ Branch(&two_byte_slice, eq, t0, Operand(zero_reg));
|
||||
__ AllocateOneByteSlicedString(v0, a2, t2, t3, &runtime);
|
||||
__ jmp(&set_slice_header);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(v0, a2, t2, t3, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ sll(a3, a3, 1);
|
||||
__ sw(t1, FieldMemOperand(v0, SlicedString::kParentOffset));
|
||||
__ sw(a3, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
||||
__ jmp(&return_v0);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// t1: underlying subject string
|
||||
// a1: instance type of underlying subject string
|
||||
// a2: length
|
||||
// a3: adjusted start index (untagged)
|
||||
Label two_byte_sequential, sequential_string, allocate_result;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ And(t0, a1, Operand(kExternalStringTag));
|
||||
__ Branch(&sequential_string, eq, t0, Operand(zero_reg));
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ And(t0, a1, Operand(kShortExternalStringTag));
|
||||
__ Branch(&runtime, ne, t0, Operand(zero_reg));
|
||||
__ lw(t1, FieldMemOperand(t1, ExternalString::kResourceDataOffset));
|
||||
// t1 already points to the first character of underlying string.
|
||||
__ jmp(&allocate_result);
|
||||
|
||||
__ bind(&sequential_string);
|
||||
// Locate first character of underlying subject string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ Addu(t1, t1, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&allocate_result);
|
||||
// Sequential acii string. Allocate the result.
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ And(t0, a1, Operand(kStringEncodingMask));
|
||||
__ Branch(&two_byte_sequential, eq, t0, Operand(zero_reg));
|
||||
|
||||
// Allocate and copy the resulting ASCII string.
|
||||
__ AllocateOneByteString(v0, a2, t0, t2, t3, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ Addu(t1, t1, a3);
|
||||
|
||||
// Locate first character of result.
|
||||
__ Addu(a1, v0, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// v0: result string
|
||||
// a1: first character of result string
|
||||
// a2: result string length
|
||||
// t1: first character of substring to copy
|
||||
STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, a1, t1, a2, a3, String::ONE_BYTE_ENCODING);
|
||||
__ jmp(&return_v0);
|
||||
|
||||
// Allocate and copy the resulting two-byte string.
|
||||
__ bind(&two_byte_sequential);
|
||||
__ AllocateTwoByteString(v0, a2, t0, t2, t3, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
__ Lsa(t1, t1, a3, 1);
|
||||
// Locate first character of result.
|
||||
__ Addu(a1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// v0: result string.
|
||||
// a1: first character of result.
|
||||
// a2: result length.
|
||||
// t1: first character of substring to copy.
|
||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, a1, t1, a2, a3, String::TWO_BYTE_ENCODING);
|
||||
|
||||
__ bind(&return_v0);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1, a3, t0);
|
||||
__ DropAndRet(3);
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// v0: original string
|
||||
// a1: instance type
|
||||
// a2: length
|
||||
// a3: from index (untagged)
|
||||
__ SmiTag(a3, a3);
|
||||
StringCharAtGenerator generator(v0, a3, a2, v0, &runtime, &runtime, &runtime,
|
||||
RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ DropAndRet(3);
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes on argument in a0.
|
||||
Label is_number;
|
||||
|
@ -2271,6 +2271,229 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
// Stack frame on entry.
|
||||
// ra: return address
|
||||
// sp[0]: to
|
||||
// sp[4]: from
|
||||
// sp[8]: string
|
||||
|
||||
// This stub is called from the native-call %_SubString(...), so
|
||||
// nothing can be assumed about the arguments. It is tested that:
|
||||
// "string" is a sequential string,
|
||||
// both "from" and "to" are smis, and
|
||||
// 0 <= from <= to <= string.length.
|
||||
// If any of these assumptions fail, we call the runtime system.
|
||||
|
||||
const int kToOffset = 0 * kPointerSize;
|
||||
const int kFromOffset = 1 * kPointerSize;
|
||||
const int kStringOffset = 2 * kPointerSize;
|
||||
|
||||
__ ld(a2, MemOperand(sp, kToOffset));
|
||||
__ ld(a3, MemOperand(sp, kFromOffset));
|
||||
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
|
||||
// Utilize delay slots. SmiUntag doesn't emit a jump, everything else is
|
||||
// safe in this case.
|
||||
__ JumpIfNotSmi(a2, &runtime);
|
||||
__ JumpIfNotSmi(a3, &runtime);
|
||||
// Both a2 and a3 are untagged integers.
|
||||
|
||||
__ SmiUntag(a2, a2);
|
||||
__ SmiUntag(a3, a3);
|
||||
__ Branch(&runtime, lt, a3, Operand(zero_reg)); // From < 0.
|
||||
|
||||
__ Branch(&runtime, gt, a3, Operand(a2)); // Fail if from > to.
|
||||
__ Dsubu(a2, a2, a3);
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ ld(v0, MemOperand(sp, kStringOffset));
|
||||
__ JumpIfSmi(v0, &runtime);
|
||||
__ ld(a1, FieldMemOperand(v0, HeapObject::kMapOffset));
|
||||
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||
__ And(a4, a1, Operand(kIsNotStringMask));
|
||||
|
||||
__ Branch(&runtime, ne, a4, Operand(zero_reg));
|
||||
|
||||
Label single_char;
|
||||
__ Branch(&single_char, eq, a2, Operand(1));
|
||||
|
||||
// Short-cut for the case of trivial substring.
|
||||
Label return_v0;
|
||||
// v0: original string
|
||||
// a2: result string length
|
||||
__ ld(a4, FieldMemOperand(v0, String::kLengthOffset));
|
||||
__ SmiUntag(a4);
|
||||
// Return original string.
|
||||
__ Branch(&return_v0, eq, a2, Operand(a4));
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ Branch(&runtime, hi, a2, Operand(a4));
|
||||
// Shorter than original string's length: an actual substring.
|
||||
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into a5.
|
||||
// v0: original string
|
||||
// a1: instance type
|
||||
// a2: length
|
||||
// a3: from index (untagged)
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ And(a4, a1, Operand(kIsIndirectStringMask));
|
||||
__ Branch(USE_DELAY_SLOT, &seq_or_external_string, eq, a4, Operand(zero_reg));
|
||||
// a4 is used as a scratch register and can be overwritten in either case.
|
||||
__ And(a4, a1, Operand(kSlicedNotConsMask));
|
||||
__ Branch(&sliced_string, ne, a4, Operand(zero_reg));
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
__ ld(a5, FieldMemOperand(v0, ConsString::kSecondOffset));
|
||||
__ LoadRoot(a4, Heap::kempty_stringRootIndex);
|
||||
__ Branch(&runtime, ne, a5, Operand(a4));
|
||||
__ ld(a5, FieldMemOperand(v0, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ ld(a1, FieldMemOperand(a5, HeapObject::kMapOffset));
|
||||
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and correct start index by offset.
|
||||
__ ld(a5, FieldMemOperand(v0, SlicedString::kParentOffset));
|
||||
__ ld(a4, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
||||
__ SmiUntag(a4); // Add offset to index.
|
||||
__ Daddu(a3, a3, a4);
|
||||
// Update instance type.
|
||||
__ ld(a1, FieldMemOperand(a5, HeapObject::kMapOffset));
|
||||
__ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the expected register.
|
||||
__ mov(a5, v0);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// a5: underlying subject string
|
||||
// a1: instance type of underlying subject string
|
||||
// a2: length
|
||||
// a3: adjusted start index (untagged)
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ Branch(©_routine, lt, a2, Operand(SlicedString::kMinLength));
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ And(a4, a1, Operand(kStringEncodingMask));
|
||||
__ Branch(&two_byte_slice, eq, a4, Operand(zero_reg));
|
||||
__ AllocateOneByteSlicedString(v0, a2, a6, a7, &runtime);
|
||||
__ jmp(&set_slice_header);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(v0, a2, a6, a7, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ SmiTag(a3);
|
||||
__ sd(a5, FieldMemOperand(v0, SlicedString::kParentOffset));
|
||||
__ sd(a3, FieldMemOperand(v0, SlicedString::kOffsetOffset));
|
||||
__ jmp(&return_v0);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// a5: underlying subject string
|
||||
// a1: instance type of underlying subject string
|
||||
// a2: length
|
||||
// a3: adjusted start index (untagged)
|
||||
Label two_byte_sequential, sequential_string, allocate_result;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ And(a4, a1, Operand(kExternalStringTag));
|
||||
__ Branch(&sequential_string, eq, a4, Operand(zero_reg));
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ And(a4, a1, Operand(kShortExternalStringTag));
|
||||
__ Branch(&runtime, ne, a4, Operand(zero_reg));
|
||||
__ ld(a5, FieldMemOperand(a5, ExternalString::kResourceDataOffset));
|
||||
// a5 already points to the first character of underlying string.
|
||||
__ jmp(&allocate_result);
|
||||
|
||||
__ bind(&sequential_string);
|
||||
// Locate first character of underlying subject string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ Daddu(a5, a5, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&allocate_result);
|
||||
// Sequential acii string. Allocate the result.
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ And(a4, a1, Operand(kStringEncodingMask));
|
||||
__ Branch(&two_byte_sequential, eq, a4, Operand(zero_reg));
|
||||
|
||||
// Allocate and copy the resulting one_byte string.
|
||||
__ AllocateOneByteString(v0, a2, a4, a6, a7, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ Daddu(a5, a5, a3);
|
||||
|
||||
// Locate first character of result.
|
||||
__ Daddu(a1, v0, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// v0: result string
|
||||
// a1: first character of result string
|
||||
// a2: result string length
|
||||
// a5: first character of substring to copy
|
||||
STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, a1, a5, a2, a3, String::ONE_BYTE_ENCODING);
|
||||
__ jmp(&return_v0);
|
||||
|
||||
// Allocate and copy the resulting two-byte string.
|
||||
__ bind(&two_byte_sequential);
|
||||
__ AllocateTwoByteString(v0, a2, a4, a6, a7, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
|
||||
__ Dlsa(a5, a5, a3, 1);
|
||||
// Locate first character of result.
|
||||
__ Daddu(a1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// v0: result string.
|
||||
// a1: first character of result.
|
||||
// a2: result length.
|
||||
// a5: first character of substring to copy.
|
||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, a1, a5, a2, a3, String::TWO_BYTE_ENCODING);
|
||||
|
||||
__ bind(&return_v0);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1, a3, a4);
|
||||
__ DropAndRet(3);
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// v0: original string
|
||||
// a1: instance type
|
||||
// a2: length
|
||||
// a3: from index (untagged)
|
||||
__ SmiTag(a3);
|
||||
StringCharAtGenerator generator(v0, a3, a2, v0, &runtime, &runtime, &runtime,
|
||||
RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ DropAndRet(3);
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes on argument in a0.
|
||||
Label is_number;
|
||||
|
@ -2207,6 +2207,230 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm, Register dest,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
|
||||
// Stack frame on entry.
|
||||
// lr: return address
|
||||
// sp[0]: to
|
||||
// sp[4]: from
|
||||
// sp[8]: string
|
||||
|
||||
// This stub is called from the native-call %_SubString(...), so
|
||||
// nothing can be assumed about the arguments. It is tested that:
|
||||
// "string" is a sequential string,
|
||||
// both "from" and "to" are smis, and
|
||||
// 0 <= from <= to <= string.length.
|
||||
// If any of these assumptions fail, we call the runtime system.
|
||||
|
||||
const int kToOffset = 0 * kPointerSize;
|
||||
const int kFromOffset = 1 * kPointerSize;
|
||||
const int kStringOffset = 2 * kPointerSize;
|
||||
|
||||
__ LoadP(r5, MemOperand(sp, kToOffset));
|
||||
__ LoadP(r6, MemOperand(sp, kFromOffset));
|
||||
|
||||
// If either to or from had the smi tag bit set, then fail to generic runtime
|
||||
__ JumpIfNotSmi(r5, &runtime);
|
||||
__ JumpIfNotSmi(r6, &runtime);
|
||||
__ SmiUntag(r5);
|
||||
__ SmiUntag(r6, SetRC);
|
||||
// Both r5 and r6 are untagged integers.
|
||||
|
||||
// We want to bailout to runtime here if From is negative.
|
||||
__ blt(&runtime, cr0); // From < 0.
|
||||
|
||||
__ cmpl(r6, r5);
|
||||
__ bgt(&runtime); // Fail if from > to.
|
||||
__ sub(r5, r5, r6);
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ LoadP(r3, MemOperand(sp, kStringOffset));
|
||||
__ JumpIfSmi(r3, &runtime);
|
||||
Condition is_string = masm->IsObjectStringType(r3, r4);
|
||||
__ b(NegateCondition(is_string), &runtime, cr0);
|
||||
|
||||
Label single_char;
|
||||
__ cmpi(r5, Operand(1));
|
||||
__ b(eq, &single_char);
|
||||
|
||||
// Short-cut for the case of trivial substring.
|
||||
Label return_r3;
|
||||
// r3: original string
|
||||
// r5: result string length
|
||||
__ LoadP(r7, FieldMemOperand(r3, String::kLengthOffset));
|
||||
__ SmiUntag(r0, r7);
|
||||
__ cmpl(r5, r0);
|
||||
// Return original string.
|
||||
__ beq(&return_r3);
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ bgt(&runtime);
|
||||
// Shorter than original string's length: an actual substring.
|
||||
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into r8.
|
||||
// r3: original string
|
||||
// r4: instance type
|
||||
// r5: length
|
||||
// r6: from index (untagged)
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ andi(r0, r4, Operand(kIsIndirectStringMask));
|
||||
__ beq(&seq_or_external_string, cr0);
|
||||
|
||||
__ andi(r0, r4, Operand(kSlicedNotConsMask));
|
||||
__ bne(&sliced_string, cr0);
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
__ LoadP(r8, FieldMemOperand(r3, ConsString::kSecondOffset));
|
||||
__ CompareRoot(r8, Heap::kempty_stringRootIndex);
|
||||
__ bne(&runtime);
|
||||
__ LoadP(r8, FieldMemOperand(r3, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ LoadP(r4, FieldMemOperand(r8, HeapObject::kMapOffset));
|
||||
__ lbz(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
||||
__ b(&underlying_unpacked);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and correct start index by offset.
|
||||
__ LoadP(r8, FieldMemOperand(r3, SlicedString::kParentOffset));
|
||||
__ LoadP(r7, FieldMemOperand(r3, SlicedString::kOffsetOffset));
|
||||
__ SmiUntag(r4, r7);
|
||||
__ add(r6, r6, r4); // Add offset to index.
|
||||
// Update instance type.
|
||||
__ LoadP(r4, FieldMemOperand(r8, HeapObject::kMapOffset));
|
||||
__ lbz(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
||||
__ b(&underlying_unpacked);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the expected register.
|
||||
__ mr(r8, r3);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// r8: underlying subject string
|
||||
// r4: instance type of underlying subject string
|
||||
// r5: length
|
||||
// r6: adjusted start index (untagged)
|
||||
__ cmpi(r5, Operand(SlicedString::kMinLength));
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ blt(©_routine);
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ andi(r0, r4, Operand(kStringEncodingMask));
|
||||
__ beq(&two_byte_slice, cr0);
|
||||
__ AllocateOneByteSlicedString(r3, r5, r9, r10, &runtime);
|
||||
__ b(&set_slice_header);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(r3, r5, r9, r10, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ SmiTag(r6);
|
||||
__ StoreP(r8, FieldMemOperand(r3, SlicedString::kParentOffset), r0);
|
||||
__ StoreP(r6, FieldMemOperand(r3, SlicedString::kOffsetOffset), r0);
|
||||
__ b(&return_r3);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// r8: underlying subject string
|
||||
// r4: instance type of underlying subject string
|
||||
// r5: length
|
||||
// r6: adjusted start index (untagged)
|
||||
Label two_byte_sequential, sequential_string, allocate_result;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ andi(r0, r4, Operand(kExternalStringTag));
|
||||
__ beq(&sequential_string, cr0);
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ andi(r0, r4, Operand(kShortExternalStringTag));
|
||||
__ bne(&runtime, cr0);
|
||||
__ LoadP(r8, FieldMemOperand(r8, ExternalString::kResourceDataOffset));
|
||||
// r8 already points to the first character of underlying string.
|
||||
__ b(&allocate_result);
|
||||
|
||||
__ bind(&sequential_string);
|
||||
// Locate first character of underlying subject string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ addi(r8, r8, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&allocate_result);
|
||||
// Sequential acii string. Allocate the result.
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ andi(r0, r4, Operand(kStringEncodingMask));
|
||||
__ beq(&two_byte_sequential, cr0);
|
||||
|
||||
// Allocate and copy the resulting one-byte string.
|
||||
__ AllocateOneByteString(r3, r5, r7, r9, r10, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ add(r8, r8, r6);
|
||||
// Locate first character of result.
|
||||
__ addi(r4, r3, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// r3: result string
|
||||
// r4: first character of result string
|
||||
// r5: result string length
|
||||
// r8: first character of substring to copy
|
||||
STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(masm, r4, r8, r5, r6,
|
||||
String::ONE_BYTE_ENCODING);
|
||||
__ b(&return_r3);
|
||||
|
||||
// Allocate and copy the resulting two-byte string.
|
||||
__ bind(&two_byte_sequential);
|
||||
__ AllocateTwoByteString(r3, r5, r7, r9, r10, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ ShiftLeftImm(r4, r6, Operand(1));
|
||||
__ add(r8, r8, r4);
|
||||
// Locate first character of result.
|
||||
__ addi(r4, r3, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// r3: result string.
|
||||
// r4: first character of result.
|
||||
// r5: result length.
|
||||
// r8: first character of substring to copy.
|
||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(masm, r4, r8, r5, r6,
|
||||
String::TWO_BYTE_ENCODING);
|
||||
|
||||
__ bind(&return_r3);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1, r6, r7);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// r3: original string
|
||||
// r4: instance type
|
||||
// r5: length
|
||||
// r6: from index (untagged)
|
||||
__ SmiTag(r6, r6);
|
||||
StringCharAtGenerator generator(r3, r6, r5, r3, &runtime, &runtime, &runtime,
|
||||
RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes one argument in r3.
|
||||
Label is_number;
|
||||
|
@ -2202,6 +2202,235 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm, Register dest,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
|
||||
// Stack frame on entry.
|
||||
// lr: return address
|
||||
// sp[0]: to
|
||||
// sp[4]: from
|
||||
// sp[8]: string
|
||||
|
||||
// This stub is called from the native-call %_SubString(...), so
|
||||
// nothing can be assumed about the arguments. It is tested that:
|
||||
// "string" is a sequential string,
|
||||
// both "from" and "to" are smis, and
|
||||
// 0 <= from <= to <= string.length.
|
||||
// If any of these assumptions fail, we call the runtime system.
|
||||
|
||||
const int kToOffset = 0 * kPointerSize;
|
||||
const int kFromOffset = 1 * kPointerSize;
|
||||
const int kStringOffset = 2 * kPointerSize;
|
||||
|
||||
__ LoadP(r4, MemOperand(sp, kToOffset));
|
||||
__ LoadP(r5, MemOperand(sp, kFromOffset));
|
||||
|
||||
// If either to or from had the smi tag bit set, then fail to generic runtime
|
||||
__ JumpIfNotSmi(r4, &runtime);
|
||||
__ JumpIfNotSmi(r5, &runtime);
|
||||
__ SmiUntag(r4);
|
||||
__ SmiUntag(r5);
|
||||
// Both r4 and r5 are untagged integers.
|
||||
|
||||
// We want to bailout to runtime here if From is negative.
|
||||
__ blt(&runtime); // From < 0.
|
||||
|
||||
__ CmpLogicalP(r5, r4);
|
||||
__ bgt(&runtime); // Fail if from > to.
|
||||
__ SubP(r4, r4, r5);
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ LoadP(r2, MemOperand(sp, kStringOffset));
|
||||
__ JumpIfSmi(r2, &runtime);
|
||||
Condition is_string = masm->IsObjectStringType(r2, r3);
|
||||
__ b(NegateCondition(is_string), &runtime);
|
||||
|
||||
Label single_char;
|
||||
__ CmpP(r4, Operand(1));
|
||||
__ b(eq, &single_char);
|
||||
|
||||
// Short-cut for the case of trivial substring.
|
||||
Label return_r2;
|
||||
// r2: original string
|
||||
// r4: result string length
|
||||
__ LoadP(r6, FieldMemOperand(r2, String::kLengthOffset));
|
||||
__ SmiUntag(r0, r6);
|
||||
__ CmpLogicalP(r4, r0);
|
||||
// Return original string.
|
||||
__ beq(&return_r2);
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ bgt(&runtime);
|
||||
// Shorter than original string's length: an actual substring.
|
||||
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into r7.
|
||||
// r2: original string
|
||||
// r3: instance type
|
||||
// r4: length
|
||||
// r5: from index (untagged)
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ mov(r0, Operand(kIsIndirectStringMask));
|
||||
__ AndP(r0, r3);
|
||||
__ beq(&seq_or_external_string);
|
||||
|
||||
__ mov(r0, Operand(kSlicedNotConsMask));
|
||||
__ AndP(r0, r3);
|
||||
__ bne(&sliced_string);
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
__ LoadP(r7, FieldMemOperand(r2, ConsString::kSecondOffset));
|
||||
__ CompareRoot(r7, Heap::kempty_stringRootIndex);
|
||||
__ bne(&runtime);
|
||||
__ LoadP(r7, FieldMemOperand(r2, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ LoadP(r3, FieldMemOperand(r7, HeapObject::kMapOffset));
|
||||
__ LoadlB(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
|
||||
__ b(&underlying_unpacked);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and correct start index by offset.
|
||||
__ LoadP(r7, FieldMemOperand(r2, SlicedString::kParentOffset));
|
||||
__ LoadP(r6, FieldMemOperand(r2, SlicedString::kOffsetOffset));
|
||||
__ SmiUntag(r3, r6);
|
||||
__ AddP(r5, r3); // Add offset to index.
|
||||
// Update instance type.
|
||||
__ LoadP(r3, FieldMemOperand(r7, HeapObject::kMapOffset));
|
||||
__ LoadlB(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
|
||||
__ b(&underlying_unpacked);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the expected register.
|
||||
__ LoadRR(r7, r2);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// r7: underlying subject string
|
||||
// r3: instance type of underlying subject string
|
||||
// r4: length
|
||||
// r5: adjusted start index (untagged)
|
||||
__ CmpP(r4, Operand(SlicedString::kMinLength));
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ blt(©_routine);
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ mov(r0, Operand(kStringEncodingMask));
|
||||
__ AndP(r0, r3);
|
||||
__ beq(&two_byte_slice);
|
||||
__ AllocateOneByteSlicedString(r2, r4, r8, r9, &runtime);
|
||||
__ b(&set_slice_header);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(r2, r4, r8, r9, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ SmiTag(r5);
|
||||
__ StoreP(r7, FieldMemOperand(r2, SlicedString::kParentOffset));
|
||||
__ StoreP(r5, FieldMemOperand(r2, SlicedString::kOffsetOffset));
|
||||
__ b(&return_r2);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// r7: underlying subject string
|
||||
// r3: instance type of underlying subject string
|
||||
// r4: length
|
||||
// r5: adjusted start index (untagged)
|
||||
Label two_byte_sequential, sequential_string, allocate_result;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ mov(r0, Operand(kExternalStringTag));
|
||||
__ AndP(r0, r3);
|
||||
__ beq(&sequential_string);
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ mov(r0, Operand(kShortExternalStringTag));
|
||||
__ AndP(r0, r3);
|
||||
__ bne(&runtime);
|
||||
__ LoadP(r7, FieldMemOperand(r7, ExternalString::kResourceDataOffset));
|
||||
// r7 already points to the first character of underlying string.
|
||||
__ b(&allocate_result);
|
||||
|
||||
__ bind(&sequential_string);
|
||||
// Locate first character of underlying subject string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ AddP(r7, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&allocate_result);
|
||||
// Sequential acii string. Allocate the result.
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ mov(r0, Operand(kStringEncodingMask));
|
||||
__ AndP(r0, r3);
|
||||
__ beq(&two_byte_sequential);
|
||||
|
||||
// Allocate and copy the resulting one-byte string.
|
||||
__ AllocateOneByteString(r2, r4, r6, r8, r9, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ AddP(r7, r5);
|
||||
// Locate first character of result.
|
||||
__ AddP(r3, r2, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// r2: result string
|
||||
// r3: first character of result string
|
||||
// r4: result string length
|
||||
// r7: first character of substring to copy
|
||||
STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(masm, r3, r7, r4, r5,
|
||||
String::ONE_BYTE_ENCODING);
|
||||
__ b(&return_r2);
|
||||
|
||||
// Allocate and copy the resulting two-byte string.
|
||||
__ bind(&two_byte_sequential);
|
||||
__ AllocateTwoByteString(r2, r4, r6, r8, r9, &runtime);
|
||||
|
||||
// Locate first character of substring to copy.
|
||||
__ ShiftLeftP(r3, r5, Operand(1));
|
||||
__ AddP(r7, r3);
|
||||
// Locate first character of result.
|
||||
__ AddP(r3, r2, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
// r2: result string.
|
||||
// r3: first character of result.
|
||||
// r4: result length.
|
||||
// r7: first character of substring to copy.
|
||||
STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0);
|
||||
StringHelper::GenerateCopyCharacters(masm, r3, r7, r4, r5,
|
||||
String::TWO_BYTE_ENCODING);
|
||||
|
||||
__ bind(&return_r2);
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1, r5, r6);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// r2: original string
|
||||
// r3: instance type
|
||||
// r4: length
|
||||
// r5: from index (untagged)
|
||||
__ SmiTag(r5, r5);
|
||||
StringCharAtGenerator generator(r2, r5, r4, r2, &runtime, &runtime, &runtime,
|
||||
RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ Drop(3);
|
||||
__ Ret();
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes one argument in r2.
|
||||
Label done;
|
||||
|
@ -2011,6 +2011,227 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
|
||||
// Stack frame on entry.
|
||||
// rsp[0] : return address
|
||||
// rsp[8] : to
|
||||
// rsp[16] : from
|
||||
// rsp[24] : string
|
||||
|
||||
enum SubStringStubArgumentIndices {
|
||||
STRING_ARGUMENT_INDEX,
|
||||
FROM_ARGUMENT_INDEX,
|
||||
TO_ARGUMENT_INDEX,
|
||||
SUB_STRING_ARGUMENT_COUNT
|
||||
};
|
||||
|
||||
StackArgumentsAccessor args(rsp, SUB_STRING_ARGUMENT_COUNT,
|
||||
ARGUMENTS_DONT_CONTAIN_RECEIVER);
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ movp(rax, args.GetArgumentOperand(STRING_ARGUMENT_INDEX));
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
__ testl(rax, Immediate(kSmiTagMask));
|
||||
__ j(zero, &runtime);
|
||||
Condition is_string = masm->IsObjectStringType(rax, rbx, rbx);
|
||||
__ j(NegateCondition(is_string), &runtime);
|
||||
|
||||
// rax: string
|
||||
// rbx: instance type
|
||||
// Calculate length of sub string using the smi values.
|
||||
__ movp(rcx, args.GetArgumentOperand(TO_ARGUMENT_INDEX));
|
||||
__ movp(rdx, args.GetArgumentOperand(FROM_ARGUMENT_INDEX));
|
||||
__ JumpUnlessBothNonNegativeSmi(rcx, rdx, &runtime);
|
||||
|
||||
__ SmiSub(rcx, rcx, rdx); // Overflow doesn't happen.
|
||||
__ cmpp(rcx, FieldOperand(rax, String::kLengthOffset));
|
||||
Label not_original_string;
|
||||
// Shorter than original string's length: an actual substring.
|
||||
__ j(below, ¬_original_string, Label::kNear);
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ j(above, &runtime);
|
||||
// Return original string.
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(SUB_STRING_ARGUMENT_COUNT * kPointerSize);
|
||||
__ bind(¬_original_string);
|
||||
|
||||
Label single_char;
|
||||
__ SmiCompare(rcx, Smi::FromInt(1));
|
||||
__ j(equal, &single_char);
|
||||
|
||||
__ SmiToInteger32(rcx, rcx);
|
||||
|
||||
// rax: string
|
||||
// rbx: instance type
|
||||
// rcx: sub string length
|
||||
// rdx: from index (smi)
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into edi.
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ testb(rbx, Immediate(kIsIndirectStringMask));
|
||||
__ j(zero, &seq_or_external_string, Label::kNear);
|
||||
|
||||
__ testb(rbx, Immediate(kSlicedNotConsMask));
|
||||
__ j(not_zero, &sliced_string, Label::kNear);
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
// Flat cons strings have an empty second part.
|
||||
__ CompareRoot(FieldOperand(rax, ConsString::kSecondOffset),
|
||||
Heap::kempty_stringRootIndex);
|
||||
__ j(not_equal, &runtime);
|
||||
__ movp(rdi, FieldOperand(rax, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ movp(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
|
||||
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked, Label::kNear);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and correct start index by offset.
|
||||
__ addp(rdx, FieldOperand(rax, SlicedString::kOffsetOffset));
|
||||
__ movp(rdi, FieldOperand(rax, SlicedString::kParentOffset));
|
||||
// Update instance type.
|
||||
__ movp(rbx, FieldOperand(rdi, HeapObject::kMapOffset));
|
||||
__ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked, Label::kNear);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the correct register.
|
||||
__ movp(rdi, rax);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// rdi: underlying subject string
|
||||
// rbx: instance type of underlying subject string
|
||||
// rdx: adjusted start index (smi)
|
||||
// rcx: length
|
||||
// If coming from the make_two_character_string path, the string
|
||||
// is too short to be sliced anyways.
|
||||
__ cmpp(rcx, Immediate(SlicedString::kMinLength));
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ j(less, ©_routine);
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ testb(rbx, Immediate(kStringEncodingMask));
|
||||
__ j(zero, &two_byte_slice, Label::kNear);
|
||||
__ AllocateOneByteSlicedString(rax, rbx, r14, &runtime);
|
||||
__ jmp(&set_slice_header, Label::kNear);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(rax, rbx, r14, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ Integer32ToSmi(rcx, rcx);
|
||||
__ movp(FieldOperand(rax, SlicedString::kLengthOffset), rcx);
|
||||
__ movp(FieldOperand(rax, SlicedString::kHashFieldOffset),
|
||||
Immediate(String::kEmptyHashField));
|
||||
__ movp(FieldOperand(rax, SlicedString::kParentOffset), rdi);
|
||||
__ movp(FieldOperand(rax, SlicedString::kOffsetOffset), rdx);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// rdi: underlying subject string
|
||||
// rbx: instance type of underlying subject string
|
||||
// rdx: adjusted start index (smi)
|
||||
// rcx: length
|
||||
// The subject string can only be external or sequential string of either
|
||||
// encoding at this point.
|
||||
Label two_byte_sequential, sequential_string;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ testb(rbx, Immediate(kExternalStringTag));
|
||||
__ j(zero, &sequential_string);
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ testb(rbx, Immediate(kShortExternalStringMask));
|
||||
__ j(not_zero, &runtime);
|
||||
__ movp(rdi, FieldOperand(rdi, ExternalString::kResourceDataOffset));
|
||||
// Move the pointer so that offset-wise, it looks like a sequential string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ subp(rdi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&sequential_string);
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ testb(rbx, Immediate(kStringEncodingMask));
|
||||
__ j(zero, &two_byte_sequential);
|
||||
|
||||
// Allocate the result.
|
||||
__ AllocateOneByteString(rax, rcx, r11, r14, r15, &runtime);
|
||||
|
||||
// rax: result string
|
||||
// rcx: result string length
|
||||
{ // Locate character of sub string start.
|
||||
SmiIndex smi_as_index = masm->SmiToIndex(rdx, rdx, times_1);
|
||||
__ leap(r14, Operand(rdi, smi_as_index.reg, smi_as_index.scale,
|
||||
SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
}
|
||||
// Locate first character of result.
|
||||
__ leap(rdi, FieldOperand(rax, SeqOneByteString::kHeaderSize));
|
||||
|
||||
// rax: result string
|
||||
// rcx: result length
|
||||
// r14: first character of result
|
||||
// rsi: character of sub string start
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, rdi, r14, rcx, String::ONE_BYTE_ENCODING);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(SUB_STRING_ARGUMENT_COUNT * kPointerSize);
|
||||
|
||||
__ bind(&two_byte_sequential);
|
||||
// Allocate the result.
|
||||
__ AllocateTwoByteString(rax, rcx, r11, r14, r15, &runtime);
|
||||
|
||||
// rax: result string
|
||||
// rcx: result string length
|
||||
{ // Locate character of sub string start.
|
||||
SmiIndex smi_as_index = masm->SmiToIndex(rdx, rdx, times_2);
|
||||
__ leap(r14, Operand(rdi, smi_as_index.reg, smi_as_index.scale,
|
||||
SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
}
|
||||
// Locate first character of result.
|
||||
__ leap(rdi, FieldOperand(rax, SeqTwoByteString::kHeaderSize));
|
||||
|
||||
// rax: result string
|
||||
// rcx: result length
|
||||
// rdi: first character of result
|
||||
// r14: character of sub string start
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, rdi, r14, rcx, String::TWO_BYTE_ENCODING);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(SUB_STRING_ARGUMENT_COUNT * kPointerSize);
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// rax: string
|
||||
// rbx: instance type
|
||||
// rcx: sub string length (smi)
|
||||
// rdx: from index (smi)
|
||||
StringCharAtGenerator generator(rax, rdx, rcx, rax, &runtime, &runtime,
|
||||
&runtime, RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ ret(SUB_STRING_ARGUMENT_COUNT * kPointerSize);
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes one argument in rax.
|
||||
Label is_number;
|
||||
|
@ -1907,6 +1907,227 @@ void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
||||
__ bind(&done);
|
||||
}
|
||||
|
||||
|
||||
void SubStringStub::Generate(MacroAssembler* masm) {
|
||||
Label runtime;
|
||||
|
||||
// Stack frame on entry.
|
||||
// esp[0]: return address
|
||||
// esp[4]: to
|
||||
// esp[8]: from
|
||||
// esp[12]: string
|
||||
|
||||
// Make sure first argument is a string.
|
||||
__ mov(eax, Operand(esp, 3 * kPointerSize));
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
__ JumpIfSmi(eax, &runtime);
|
||||
Condition is_string = masm->IsObjectStringType(eax, ebx, ebx);
|
||||
__ j(NegateCondition(is_string), &runtime);
|
||||
|
||||
// eax: string
|
||||
// ebx: instance type
|
||||
|
||||
// Calculate length of sub string using the smi values.
|
||||
__ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
|
||||
__ JumpIfNotSmi(ecx, &runtime);
|
||||
__ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
|
||||
__ JumpIfNotSmi(edx, &runtime);
|
||||
__ sub(ecx, edx);
|
||||
__ cmp(ecx, FieldOperand(eax, String::kLengthOffset));
|
||||
Label not_original_string;
|
||||
// Shorter than original string's length: an actual substring.
|
||||
__ j(below, ¬_original_string, Label::kNear);
|
||||
// Longer than original string's length or negative: unsafe arguments.
|
||||
__ j(above, &runtime);
|
||||
// Return original string.
|
||||
Counters* counters = isolate()->counters();
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
__ bind(¬_original_string);
|
||||
|
||||
Label single_char;
|
||||
__ cmp(ecx, Immediate(Smi::FromInt(1)));
|
||||
__ j(equal, &single_char);
|
||||
|
||||
// eax: string
|
||||
// ebx: instance type
|
||||
// ecx: sub string length (smi)
|
||||
// edx: from index (smi)
|
||||
// Deal with different string types: update the index if necessary
|
||||
// and put the underlying string into edi.
|
||||
Label underlying_unpacked, sliced_string, seq_or_external_string;
|
||||
// If the string is not indirect, it can only be sequential or external.
|
||||
STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag));
|
||||
STATIC_ASSERT(kIsIndirectStringMask != 0);
|
||||
__ test(ebx, Immediate(kIsIndirectStringMask));
|
||||
__ j(zero, &seq_or_external_string, Label::kNear);
|
||||
|
||||
Factory* factory = isolate()->factory();
|
||||
__ test(ebx, Immediate(kSlicedNotConsMask));
|
||||
__ j(not_zero, &sliced_string, Label::kNear);
|
||||
// Cons string. Check whether it is flat, then fetch first part.
|
||||
// Flat cons strings have an empty second part.
|
||||
__ cmp(FieldOperand(eax, ConsString::kSecondOffset),
|
||||
factory->empty_string());
|
||||
__ j(not_equal, &runtime);
|
||||
__ mov(edi, FieldOperand(eax, ConsString::kFirstOffset));
|
||||
// Update instance type.
|
||||
__ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
|
||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked, Label::kNear);
|
||||
|
||||
__ bind(&sliced_string);
|
||||
// Sliced string. Fetch parent and adjust start index by offset.
|
||||
__ add(edx, FieldOperand(eax, SlicedString::kOffsetOffset));
|
||||
__ mov(edi, FieldOperand(eax, SlicedString::kParentOffset));
|
||||
// Update instance type.
|
||||
__ mov(ebx, FieldOperand(edi, HeapObject::kMapOffset));
|
||||
__ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
|
||||
__ jmp(&underlying_unpacked, Label::kNear);
|
||||
|
||||
__ bind(&seq_or_external_string);
|
||||
// Sequential or external string. Just move string to the expected register.
|
||||
__ mov(edi, eax);
|
||||
|
||||
__ bind(&underlying_unpacked);
|
||||
|
||||
if (FLAG_string_slices) {
|
||||
Label copy_routine;
|
||||
// edi: underlying subject string
|
||||
// ebx: instance type of underlying subject string
|
||||
// edx: adjusted start index (smi)
|
||||
// ecx: length (smi)
|
||||
__ cmp(ecx, Immediate(Smi::FromInt(SlicedString::kMinLength)));
|
||||
// Short slice. Copy instead of slicing.
|
||||
__ j(less, ©_routine);
|
||||
// Allocate new sliced string. At this point we do not reload the instance
|
||||
// type including the string encoding because we simply rely on the info
|
||||
// provided by the original string. It does not matter if the original
|
||||
// string's encoding is wrong because we always have to recheck encoding of
|
||||
// the newly created string's parent anyways due to externalized strings.
|
||||
Label two_byte_slice, set_slice_header;
|
||||
STATIC_ASSERT((kStringEncodingMask & kOneByteStringTag) != 0);
|
||||
STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0);
|
||||
__ test(ebx, Immediate(kStringEncodingMask));
|
||||
__ j(zero, &two_byte_slice, Label::kNear);
|
||||
__ AllocateOneByteSlicedString(eax, ebx, no_reg, &runtime);
|
||||
__ jmp(&set_slice_header, Label::kNear);
|
||||
__ bind(&two_byte_slice);
|
||||
__ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime);
|
||||
__ bind(&set_slice_header);
|
||||
__ mov(FieldOperand(eax, SlicedString::kLengthOffset), ecx);
|
||||
__ mov(FieldOperand(eax, SlicedString::kHashFieldOffset),
|
||||
Immediate(String::kEmptyHashField));
|
||||
__ mov(FieldOperand(eax, SlicedString::kParentOffset), edi);
|
||||
__ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
__ bind(©_routine);
|
||||
}
|
||||
|
||||
// edi: underlying subject string
|
||||
// ebx: instance type of underlying subject string
|
||||
// edx: adjusted start index (smi)
|
||||
// ecx: length (smi)
|
||||
// The subject string can only be external or sequential string of either
|
||||
// encoding at this point.
|
||||
Label two_byte_sequential, runtime_drop_two, sequential_string;
|
||||
STATIC_ASSERT(kExternalStringTag != 0);
|
||||
STATIC_ASSERT(kSeqStringTag == 0);
|
||||
__ test_b(ebx, Immediate(kExternalStringTag));
|
||||
__ j(zero, &sequential_string);
|
||||
|
||||
// Handle external string.
|
||||
// Rule out short external strings.
|
||||
STATIC_ASSERT(kShortExternalStringTag != 0);
|
||||
__ test_b(ebx, Immediate(kShortExternalStringMask));
|
||||
__ j(not_zero, &runtime);
|
||||
__ mov(edi, FieldOperand(edi, ExternalString::kResourceDataOffset));
|
||||
// Move the pointer so that offset-wise, it looks like a sequential string.
|
||||
STATIC_ASSERT(SeqTwoByteString::kHeaderSize == SeqOneByteString::kHeaderSize);
|
||||
__ sub(edi, Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
__ bind(&sequential_string);
|
||||
// Stash away (adjusted) index and (underlying) string.
|
||||
__ push(edx);
|
||||
__ push(edi);
|
||||
__ SmiUntag(ecx);
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
__ test_b(ebx, Immediate(kStringEncodingMask));
|
||||
__ j(zero, &two_byte_sequential);
|
||||
|
||||
// Sequential one byte string. Allocate the result.
|
||||
__ AllocateOneByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
|
||||
|
||||
// eax: result string
|
||||
// ecx: result string length
|
||||
// Locate first character of result.
|
||||
__ mov(edi, eax);
|
||||
__ add(edi, Immediate(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
||||
// Load string argument and locate character of sub string start.
|
||||
__ pop(edx);
|
||||
__ pop(ebx);
|
||||
__ SmiUntag(ebx);
|
||||
__ lea(edx, FieldOperand(edx, ebx, times_1, SeqOneByteString::kHeaderSize));
|
||||
|
||||
// eax: result string
|
||||
// ecx: result length
|
||||
// edi: first character of result
|
||||
// edx: character of sub string start
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, edi, edx, ecx, ebx, String::ONE_BYTE_ENCODING);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
__ bind(&two_byte_sequential);
|
||||
// Sequential two-byte string. Allocate the result.
|
||||
__ AllocateTwoByteString(eax, ecx, ebx, edx, edi, &runtime_drop_two);
|
||||
|
||||
// eax: result string
|
||||
// ecx: result string length
|
||||
// Locate first character of result.
|
||||
__ mov(edi, eax);
|
||||
__ add(edi,
|
||||
Immediate(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
// Load string argument and locate character of sub string start.
|
||||
__ pop(edx);
|
||||
__ pop(ebx);
|
||||
// As from is a smi it is 2 times the value which matches the size of a two
|
||||
// byte character.
|
||||
STATIC_ASSERT(kSmiTag == 0);
|
||||
STATIC_ASSERT(kSmiTagSize + kSmiShiftSize == 1);
|
||||
__ lea(edx, FieldOperand(edx, ebx, times_1, SeqTwoByteString::kHeaderSize));
|
||||
|
||||
// eax: result string
|
||||
// ecx: result length
|
||||
// edi: first character of result
|
||||
// edx: character of sub string start
|
||||
StringHelper::GenerateCopyCharacters(
|
||||
masm, edi, edx, ecx, ebx, String::TWO_BYTE_ENCODING);
|
||||
__ IncrementCounter(counters->sub_string_native(), 1);
|
||||
__ ret(3 * kPointerSize);
|
||||
|
||||
// Drop pushed values on the stack before tail call.
|
||||
__ bind(&runtime_drop_two);
|
||||
__ Drop(2);
|
||||
|
||||
// Just jump to runtime to create the sub string.
|
||||
__ bind(&runtime);
|
||||
__ TailCallRuntime(Runtime::kSubString);
|
||||
|
||||
__ bind(&single_char);
|
||||
// eax: string
|
||||
// ebx: instance type
|
||||
// ecx: sub string length (smi)
|
||||
// edx: from index (smi)
|
||||
StringCharAtGenerator generator(eax, edx, ecx, eax, &runtime, &runtime,
|
||||
&runtime, RECEIVER_IS_STRING);
|
||||
generator.GenerateFast(masm);
|
||||
__ ret(3 * kPointerSize);
|
||||
generator.SkipSlow(masm, &runtime);
|
||||
}
|
||||
|
||||
void ToStringStub::Generate(MacroAssembler* masm) {
|
||||
// The ToString stub takes one argument in eax.
|
||||
Label is_number;
|
||||
|
Loading…
Reference in New Issue
Block a user