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