Revert of [stubs] Port SubStringStub to TurboFan (patchset #8 id:140001 of https://codereview.chromium.org/2355793003/ )

Reason for revert:
Speculative revert because of stability problems

Original issue's description:
> [stubs] Port SubStringStub to TurboFan
>
> This ports the platform-specific SubStringStub to TurboFan.
>
> It also contains a minor bug-fix for the case when the requested substring
> length equals the subject string length, but the start index is not equal to 0.
> The old stub implementation returned the subject string, while the new
> implementation calls into runtime, which finally results in a thrown exception.
>
> BUG=v8:5415
>
> Committed: https://crrev.com/49be31921536716706a6790fbbf9c346b975af16
> Cr-Commit-Position: refs/heads/master@{#39653}

TBR=ishell@chromium.org,bmeurer@chromium.org,jgruber@chromium.org
# Not skipping CQ checks because original CL landed more than 1 days ago.
BUG=v8:5415, chromium:649967
NOPRESUBMIT=true
NOTRY=true

Review-Url: https://codereview.chromium.org/2365413002
Cr-Commit-Position: refs/heads/master@{#39737}
This commit is contained in:
hablich 2016-09-26 11:03:46 -07:00 committed by Commit bot
parent b9cdb630dd
commit 10a801f12b
14 changed files with 2044 additions and 416 deletions

View File

@ -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, &copy_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(&copy_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;

View File

@ -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, &copy_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(&copy_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;

View File

@ -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))),
&copy_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(&copy_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);

View File

@ -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.

View File

@ -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;

View File

@ -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 {

View File

@ -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());
}

View File

@ -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, &not_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(&not_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, &copy_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(&copy_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;

View File

@ -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(&copy_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(&copy_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;

View File

@ -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(&copy_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(&copy_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;

View File

@ -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(&copy_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(&copy_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;

View File

@ -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(&copy_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(&copy_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;

View File

@ -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, &not_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(&not_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, &copy_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(&copy_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;

View File

@ -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, &not_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(&not_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, &copy_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(&copy_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;