[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 Review-Url: https://codereview.chromium.org/2355793003 Cr-Original-Commit-Position: refs/heads/master@{#39653} Cr-Commit-Position: refs/heads/master@{#39851}
This commit is contained in:
parent
c5e735deac
commit
261d750ea5
@ -2124,231 +2124,6 @@ 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,257 +2673,6 @@ 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,10 +337,18 @@ 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);
|
||||
}
|
||||
@ -1301,11 +1309,44 @@ 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;
|
||||
}
|
||||
@ -1638,6 +1679,75 @@ 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 = SmiUntag(character_count);
|
||||
Node* from_byte_index = SmiUntag(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,
|
||||
@ -2348,6 +2458,282 @@ Node* CodeStubAssembler::StringFromCharCode(Node* code) {
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// A wrapper around CopyStringCharacters which determines the correct string
|
||||
// encoding, allocates a corresponding sequential string, and then copies the
|
||||
// given character range using CopyStringCharacters.
|
||||
// |from_string| must be a sequential string. |from_index| and
|
||||
// |character_count| must be Smis s.t.
|
||||
// 0 <= |from_index| <= |from_index| + |character_count| < from_string.length.
|
||||
Node* AllocAndCopyStringCharacters(CodeStubAssembler* a, Node* context,
|
||||
Node* from, Node* from_instance_type,
|
||||
Node* from_index, Node* character_count) {
|
||||
typedef CodeStubAssembler::Label Label;
|
||||
typedef CodeStubAssembler::Variable Variable;
|
||||
|
||||
Label end(a), two_byte_sequential(a);
|
||||
Variable var_result(a, MachineRepresentation::kTagged);
|
||||
|
||||
STATIC_ASSERT((kOneByteStringTag & kStringEncodingMask) != 0);
|
||||
a->GotoIf(a->Word32Equal(a->Word32And(from_instance_type,
|
||||
a->Int32Constant(kStringEncodingMask)),
|
||||
a->Int32Constant(0)),
|
||||
&two_byte_sequential);
|
||||
|
||||
// The subject string is a sequential one-byte string.
|
||||
{
|
||||
Node* result =
|
||||
a->AllocateSeqOneByteString(context, a->SmiToWord(character_count));
|
||||
a->CopyStringCharacters(from, result, from_index, character_count,
|
||||
String::ONE_BYTE_ENCODING);
|
||||
var_result.Bind(result);
|
||||
|
||||
a->Goto(&end);
|
||||
}
|
||||
|
||||
// The subject string is a sequential two-byte string.
|
||||
a->Bind(&two_byte_sequential);
|
||||
{
|
||||
Node* result =
|
||||
a->AllocateSeqTwoByteString(context, a->SmiToWord(character_count));
|
||||
a->CopyStringCharacters(from, result, from_index, character_count,
|
||||
String::TWO_BYTE_ENCODING);
|
||||
var_result.Bind(result);
|
||||
|
||||
a->Goto(&end);
|
||||
}
|
||||
|
||||
a->Bind(&end);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
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.
|
||||
|
||||
GotoUnless(WordIsPositiveSmi(from), &runtime);
|
||||
GotoUnless(WordIsPositiveSmi(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.
|
||||
Label external_string(this);
|
||||
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);
|
||||
GotoUnless(Word32Equal(Word32And(var_instance_type.value(),
|
||||
Int32Constant(kExternalStringTag)),
|
||||
Int32Constant(0)),
|
||||
&external_string);
|
||||
|
||||
var_result.Bind(AllocAndCopyStringCharacters(
|
||||
this, context, var_string.value(), var_instance_type.value(),
|
||||
var_from.value(), substr_length));
|
||||
|
||||
Counters* counters = isolate()->counters();
|
||||
IncrementCounter(counters->sub_string_native(), 1);
|
||||
|
||||
Goto(&end);
|
||||
}
|
||||
|
||||
// Handle external string.
|
||||
Bind(&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);
|
||||
Node* const fake_sequential_string = IntPtrSub(
|
||||
resource_data,
|
||||
IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
||||
|
||||
var_result.Bind(AllocAndCopyStringCharacters(
|
||||
this, context, fake_sequential_string, var_instance_type.value(),
|
||||
var_from.value(), substr_length));
|
||||
|
||||
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::StringFromCodePoint(compiler::Node* codepoint,
|
||||
UnicodeEncoding encoding) {
|
||||
Variable var_result(this, MachineRepresentation::kTagged);
|
||||
|
@ -117,7 +117,9 @@ 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* SmiMax(compiler::Node* a, compiler::Node* b);
|
||||
@ -141,7 +143,7 @@ class CodeStubAssembler : public compiler::CodeAssembler {
|
||||
|
||||
// Check a value for smi-ness
|
||||
compiler::Node* WordIsSmi(compiler::Node* a);
|
||||
// Check that the value is a positive smi.
|
||||
// Check that the value is a non-negative smi.
|
||||
compiler::Node* WordIsPositiveSmi(compiler::Node* a);
|
||||
|
||||
void BranchIfSmiEqual(compiler::Node* a, compiler::Node* b, Label* if_true,
|
||||
@ -328,6 +330,18 @@ 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,
|
||||
@ -378,6 +392,16 @@ 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
|
||||
@ -449,6 +473,10 @@ 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);
|
||||
|
||||
compiler::Node* StringFromCodePoint(compiler::Node* codepoint,
|
||||
UnicodeEncoding encoding);
|
||||
|
@ -2719,6 +2719,15 @@ 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;
|
||||
|
@ -3094,13 +3094,24 @@ class StoreBufferOverflowStub : public PlatformCodeStub {
|
||||
DEFINE_PLATFORM_CODE_STUB(StoreBufferOverflow, PlatformCodeStub);
|
||||
};
|
||||
|
||||
|
||||
class SubStringStub : public PlatformCodeStub {
|
||||
class SubStringStub : public TurboFanCodeStub {
|
||||
public:
|
||||
explicit SubStringStub(Isolate* isolate) : PlatformCodeStub(isolate) {}
|
||||
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)));
|
||||
}
|
||||
|
||||
DEFINE_CALL_INTERFACE_DESCRIPTOR(SubString);
|
||||
DEFINE_PLATFORM_CODE_STUB(SubString, PlatformCodeStub);
|
||||
DEFINE_CODE_STUB(SubString, TurboFanCodeStub);
|
||||
};
|
||||
|
||||
class ToStringStub final : public PlatformCodeStub {
|
||||
|
@ -568,6 +568,7 @@ void FullCodeGenerator::EmitSubString(CallRuntime* expr) {
|
||||
VisitForStackValue(args->at(1));
|
||||
VisitForStackValue(args->at(2));
|
||||
__ CallStub(&stub);
|
||||
RestoreContext();
|
||||
OperandStackDepthDecrement(3);
|
||||
context()->Plug(result_register());
|
||||
}
|
||||
|
@ -2065,227 +2065,6 @@ 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,229 +2268,6 @@ 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,229 +2271,6 @@ 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,230 +2207,6 @@ 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,235 +2202,6 @@ 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,227 +2011,6 @@ 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,227 +1907,6 @@ 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