| Index: src/arm/code-stubs-arm.cc
|
| diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
|
| index ed340ff1937507931921549a1de8bac9fa1e6001..12742fbea1a3ea5c962bace28702338797d0b2a8 100644
|
| --- a/src/arm/code-stubs-arm.cc
|
| +++ b/src/arm/code-stubs-arm.cc
|
| @@ -49,6 +49,16 @@ void FastNewClosureStub::InitializeInterfaceDescriptor(
|
| }
|
|
|
|
|
| +void FastNewContextStub::InitializeInterfaceDescriptor(
|
| + Isolate* isolate,
|
| + CodeStubInterfaceDescriptor* descriptor) {
|
| + static Register registers[] = { r1 };
|
| + descriptor->register_param_count_ = 1;
|
| + descriptor->register_params_ = registers;
|
| + descriptor->deoptimization_handler_ = NULL;
|
| +}
|
| +
|
| +
|
| void ToNumberStub::InitializeInterfaceDescriptor(
|
| Isolate* isolate,
|
| CodeStubInterfaceDescriptor* descriptor) {
|
| @@ -200,7 +210,7 @@ static void InitializeArrayConstructorDescriptor(
|
| // register state
|
| // r0 -- number of arguments
|
| // r1 -- function
|
| - // r2 -- type info cell with elements kind
|
| + // r2 -- allocation site with elements kind
|
| static Register registers_variable_args[] = { r1, r2, r0 };
|
| static Register registers_no_args[] = { r1, r2 };
|
|
|
| @@ -350,7 +360,7 @@ void BinaryOpWithAllocationSiteStub::InitializeInterfaceDescriptor(
|
| }
|
|
|
|
|
| -void NewStringAddStub::InitializeInterfaceDescriptor(
|
| +void StringAddStub::InitializeInterfaceDescriptor(
|
| Isolate* isolate,
|
| CodeStubInterfaceDescriptor* descriptor) {
|
| static Register registers[] = { r1, r0 };
|
| @@ -461,48 +471,6 @@ void HydrogenCodeStub::GenerateLightweightMiss(MacroAssembler* masm) {
|
| }
|
|
|
|
|
| -void FastNewContextStub::Generate(MacroAssembler* masm) {
|
| - // Try to allocate the context in new space.
|
| - Label gc;
|
| - int length = slots_ + Context::MIN_CONTEXT_SLOTS;
|
| -
|
| - // Attempt to allocate the context in new space.
|
| - __ Allocate(FixedArray::SizeFor(length), r0, r1, r2, &gc, TAG_OBJECT);
|
| -
|
| - // Load the function from the stack.
|
| - __ ldr(r3, MemOperand(sp, 0));
|
| -
|
| - // Set up the object header.
|
| - __ LoadRoot(r1, Heap::kFunctionContextMapRootIndex);
|
| - __ mov(r2, Operand(Smi::FromInt(length)));
|
| - __ str(r2, FieldMemOperand(r0, FixedArray::kLengthOffset));
|
| - __ str(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| -
|
| - // Set up the fixed slots, copy the global object from the previous context.
|
| - __ ldr(r2, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
|
| - __ mov(r1, Operand(Smi::FromInt(0)));
|
| - __ str(r3, MemOperand(r0, Context::SlotOffset(Context::CLOSURE_INDEX)));
|
| - __ str(cp, MemOperand(r0, Context::SlotOffset(Context::PREVIOUS_INDEX)));
|
| - __ str(r1, MemOperand(r0, Context::SlotOffset(Context::EXTENSION_INDEX)));
|
| - __ str(r2, MemOperand(r0, Context::SlotOffset(Context::GLOBAL_OBJECT_INDEX)));
|
| -
|
| - // Initialize the rest of the slots to undefined.
|
| - __ LoadRoot(r1, Heap::kUndefinedValueRootIndex);
|
| - for (int i = Context::MIN_CONTEXT_SLOTS; i < length; i++) {
|
| - __ str(r1, MemOperand(r0, Context::SlotOffset(i)));
|
| - }
|
| -
|
| - // Remove the on-stack argument and return.
|
| - __ mov(cp, r0);
|
| - __ pop();
|
| - __ Ret();
|
| -
|
| - // Need to collect. Call into runtime system.
|
| - __ bind(&gc);
|
| - __ TailCallRuntime(Runtime::kNewFunctionContext, 1, 1);
|
| -}
|
| -
|
| -
|
| void FastNewBlockContextStub::Generate(MacroAssembler* masm) {
|
| // Stack layout on entry:
|
| //
|
| @@ -665,17 +633,16 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
|
|
|
| int double_offset = offset();
|
| // Account for saved regs if input is sp.
|
| - if (input_reg.is(sp)) double_offset += 2 * kPointerSize;
|
| + if (input_reg.is(sp)) double_offset += 3 * kPointerSize;
|
|
|
| - // Immediate values for this stub fit in instructions, so it's safe to use ip.
|
| - Register scratch = ip;
|
| + Register scratch = GetRegisterThatIsNotOneOf(input_reg, result_reg);
|
| Register scratch_low =
|
| GetRegisterThatIsNotOneOf(input_reg, result_reg, scratch);
|
| Register scratch_high =
|
| GetRegisterThatIsNotOneOf(input_reg, result_reg, scratch, scratch_low);
|
| LowDwVfpRegister double_scratch = kScratchDoubleReg;
|
|
|
| - __ Push(scratch_high, scratch_low);
|
| + __ Push(scratch_high, scratch_low, scratch);
|
|
|
| if (!skip_fastpath()) {
|
| // Load double input.
|
| @@ -758,7 +725,7 @@ void DoubleToIStub::Generate(MacroAssembler* masm) {
|
|
|
| __ bind(&done);
|
|
|
| - __ Pop(scratch_high, scratch_low);
|
| + __ Pop(scratch_high, scratch_low, scratch);
|
| __ Ret();
|
| }
|
|
|
| @@ -3469,36 +3436,6 @@ void StringCharFromCodeGenerator::GenerateSlow(
|
| }
|
|
|
|
|
| -void StringHelper::GenerateCopyCharacters(MacroAssembler* masm,
|
| - Register dest,
|
| - Register src,
|
| - Register count,
|
| - Register scratch,
|
| - bool ascii) {
|
| - Label loop;
|
| - Label done;
|
| - // This loop just copies one character at a time, as it is only used for very
|
| - // short strings.
|
| - if (!ascii) {
|
| - __ add(count, count, Operand(count), SetCC);
|
| - } else {
|
| - __ cmp(count, Operand::Zero());
|
| - }
|
| - __ b(eq, &done);
|
| -
|
| - __ bind(&loop);
|
| - __ ldrb(scratch, MemOperand(src, 1, PostIndex));
|
| - // Perform sub between load and dependent store to get the load time to
|
| - // complete.
|
| - __ sub(count, count, Operand(1), SetCC);
|
| - __ strb(scratch, MemOperand(dest, 1, PostIndex));
|
| - // last iteration.
|
| - __ b(gt, &loop);
|
| -
|
| - __ bind(&done);
|
| -}
|
| -
|
| -
|
| enum CopyCharactersFlags {
|
| COPY_ASCII = 1,
|
| DEST_ALWAYS_ALIGNED = 2
|
| @@ -3646,143 +3583,6 @@ void StringHelper::GenerateCopyCharactersLong(MacroAssembler* masm,
|
| }
|
|
|
|
|
| -void StringHelper::GenerateTwoCharacterStringTableProbe(MacroAssembler* masm,
|
| - Register c1,
|
| - Register c2,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - Register scratch3,
|
| - Register scratch4,
|
| - Register scratch5,
|
| - Label* not_found) {
|
| - // Register scratch3 is the general scratch register in this function.
|
| - Register scratch = scratch3;
|
| -
|
| - // Make sure that both characters are not digits as such strings has a
|
| - // different hash algorithm. Don't try to look for these in the string table.
|
| - Label not_array_index;
|
| - __ sub(scratch, c1, Operand(static_cast<int>('0')));
|
| - __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
|
| - __ b(hi, ¬_array_index);
|
| - __ sub(scratch, c2, Operand(static_cast<int>('0')));
|
| - __ cmp(scratch, Operand(static_cast<int>('9' - '0')));
|
| -
|
| - // If check failed combine both characters into single halfword.
|
| - // This is required by the contract of the method: code at the
|
| - // not_found branch expects this combination in c1 register
|
| - __ orr(c1, c1, Operand(c2, LSL, kBitsPerByte), LeaveCC, ls);
|
| - __ b(ls, not_found);
|
| -
|
| - __ bind(¬_array_index);
|
| - // Calculate the two character string hash.
|
| - Register hash = scratch1;
|
| - StringHelper::GenerateHashInit(masm, hash, c1);
|
| - StringHelper::GenerateHashAddCharacter(masm, hash, c2);
|
| - StringHelper::GenerateHashGetHash(masm, hash);
|
| -
|
| - // Collect the two characters in a register.
|
| - Register chars = c1;
|
| - __ orr(chars, chars, Operand(c2, LSL, kBitsPerByte));
|
| -
|
| - // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
|
| - // hash: hash of two character string.
|
| -
|
| - // Load string table
|
| - // Load address of first element of the string table.
|
| - Register string_table = c2;
|
| - __ LoadRoot(string_table, Heap::kStringTableRootIndex);
|
| -
|
| - Register undefined = scratch4;
|
| - __ LoadRoot(undefined, Heap::kUndefinedValueRootIndex);
|
| -
|
| - // Calculate capacity mask from the string table capacity.
|
| - Register mask = scratch2;
|
| - __ ldr(mask, FieldMemOperand(string_table, StringTable::kCapacityOffset));
|
| - __ mov(mask, Operand(mask, ASR, 1));
|
| - __ sub(mask, mask, Operand(1));
|
| -
|
| - // Calculate untagged address of the first element of the string table.
|
| - Register first_string_table_element = string_table;
|
| - __ add(first_string_table_element, string_table,
|
| - Operand(StringTable::kElementsStartOffset - kHeapObjectTag));
|
| -
|
| - // Registers
|
| - // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
|
| - // hash: hash of two character string
|
| - // mask: capacity mask
|
| - // first_string_table_element: address of the first element of
|
| - // the string table
|
| - // undefined: the undefined object
|
| - // scratch: -
|
| -
|
| - // Perform a number of probes in the string table.
|
| - const int kProbes = 4;
|
| - Label found_in_string_table;
|
| - Label next_probe[kProbes];
|
| - Register candidate = scratch5; // Scratch register contains candidate.
|
| - for (int i = 0; i < kProbes; i++) {
|
| - // Calculate entry in string table.
|
| - if (i > 0) {
|
| - __ add(candidate, hash, Operand(StringTable::GetProbeOffset(i)));
|
| - } else {
|
| - __ mov(candidate, hash);
|
| - }
|
| -
|
| - __ and_(candidate, candidate, Operand(mask));
|
| -
|
| - // Load the entry from the symble table.
|
| - STATIC_ASSERT(StringTable::kEntrySize == 1);
|
| - __ ldr(candidate,
|
| - MemOperand(first_string_table_element,
|
| - candidate,
|
| - LSL,
|
| - kPointerSizeLog2));
|
| -
|
| - // If entry is undefined no string with this hash can be found.
|
| - Label is_string;
|
| - __ CompareObjectType(candidate, scratch, scratch, ODDBALL_TYPE);
|
| - __ b(ne, &is_string);
|
| -
|
| - __ cmp(undefined, candidate);
|
| - __ b(eq, not_found);
|
| - // Must be the hole (deleted entry).
|
| - if (FLAG_debug_code) {
|
| - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
| - __ cmp(ip, candidate);
|
| - __ Assert(eq, kOddballInStringTableIsNotUndefinedOrTheHole);
|
| - }
|
| - __ jmp(&next_probe[i]);
|
| -
|
| - __ bind(&is_string);
|
| -
|
| - // Check that the candidate is a non-external ASCII string. The instance
|
| - // type is still in the scratch register from the CompareObjectType
|
| - // operation.
|
| - __ JumpIfInstanceTypeIsNotSequentialAscii(scratch, scratch, &next_probe[i]);
|
| -
|
| - // If length is not 2 the string is not a candidate.
|
| - __ ldr(scratch, FieldMemOperand(candidate, String::kLengthOffset));
|
| - __ cmp(scratch, Operand(Smi::FromInt(2)));
|
| - __ b(ne, &next_probe[i]);
|
| -
|
| - // Check if the two characters match.
|
| - // Assumes that word load is little endian.
|
| - __ ldrh(scratch, FieldMemOperand(candidate, SeqOneByteString::kHeaderSize));
|
| - __ cmp(chars, scratch);
|
| - __ b(eq, &found_in_string_table);
|
| - __ bind(&next_probe[i]);
|
| - }
|
| -
|
| - // No matching 2 character string found by probing.
|
| - __ jmp(not_found);
|
| -
|
| - // Scratch register contains result when we fall through to here.
|
| - Register result = candidate;
|
| - __ bind(&found_in_string_table);
|
| - __ Move(r0, result);
|
| -}
|
| -
|
| -
|
| void StringHelper::GenerateHashInit(MacroAssembler* masm,
|
| Register hash,
|
| Register character) {
|
| @@ -4197,391 +3997,240 @@ void StringCompareStub::Generate(MacroAssembler* masm) {
|
| }
|
|
|
|
|
| -void BinaryOpICWithAllocationSiteStub::Generate(MacroAssembler* masm) {
|
| - // ----------- S t a t e -------------
|
| - // -- r1 : left
|
| - // -- r0 : right
|
| - // -- lr : return address
|
| - // -----------------------------------
|
| - Isolate* isolate = masm->isolate();
|
| +void ArrayPushStub::Generate(MacroAssembler* masm) {
|
| + Register receiver = r0;
|
| + Register scratch = r1;
|
|
|
| - // Load r2 with the allocation site. We stick an undefined dummy value here
|
| - // and replace it with the real allocation site later when we instantiate this
|
| - // stub in BinaryOpICWithAllocationSiteStub::GetCodeCopyFromTemplate().
|
| - __ Move(r2, handle(isolate->heap()->undefined_value()));
|
| + int argc = arguments_count();
|
|
|
| - // Make sure that we actually patched the allocation site.
|
| - if (FLAG_debug_code) {
|
| - __ tst(r2, Operand(kSmiTagMask));
|
| - __ Assert(ne, kExpectedAllocationSite);
|
| - __ push(r2);
|
| - __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
|
| - __ LoadRoot(ip, Heap::kAllocationSiteMapRootIndex);
|
| - __ cmp(r2, ip);
|
| - __ pop(r2);
|
| - __ Assert(eq, kExpectedAllocationSite);
|
| + if (argc == 0) {
|
| + // Nothing to do, just return the length.
|
| + __ ldr(r0, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
| + __ Drop(argc + 1);
|
| + __ Ret();
|
| + return;
|
| }
|
|
|
| - // Tail call into the stub that handles binary operations with allocation
|
| - // sites.
|
| - BinaryOpWithAllocationSiteStub stub(state_);
|
| - __ TailCallStub(&stub);
|
| -}
|
| + Isolate* isolate = masm->isolate();
|
|
|
| + if (argc != 1) {
|
| + __ TailCallExternalReference(
|
| + ExternalReference(Builtins::c_ArrayPush, isolate), argc + 1, 1);
|
| + return;
|
| + }
|
|
|
| -void StringAddStub::Generate(MacroAssembler* masm) {
|
| - Label call_runtime, call_builtin;
|
| - Builtins::JavaScript builtin_id = Builtins::ADD;
|
| + Label call_builtin, attempt_to_grow_elements, with_write_barrier;
|
|
|
| - Counters* counters = masm->isolate()->counters();
|
| + Register elements = r6;
|
| + Register end_elements = r5;
|
| + // Get the elements array of the object.
|
| + __ ldr(elements, FieldMemOperand(receiver, JSArray::kElementsOffset));
|
|
|
| - // Stack on entry:
|
| - // sp[0]: second argument (right).
|
| - // sp[4]: first argument (left).
|
| -
|
| - // Load the two arguments.
|
| - __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // First argument.
|
| - __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // Second argument.
|
| -
|
| - // Make sure that both arguments are strings if not known in advance.
|
| - // Otherwise, at least one of the arguments is definitely a string,
|
| - // and we convert the one that is not known to be a string.
|
| - if ((flags_ & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
|
| - ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT);
|
| - ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT);
|
| - __ JumpIfEitherSmi(r0, r1, &call_runtime);
|
| - // Load instance types.
|
| - __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| - __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| - __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| - __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| - STATIC_ASSERT(kStringTag == 0);
|
| - // If either is not a string, go to runtime.
|
| - __ tst(r4, Operand(kIsNotStringMask));
|
| - __ tst(r5, Operand(kIsNotStringMask), eq);
|
| - __ b(ne, &call_runtime);
|
| - } else if ((flags_ & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
|
| - ASSERT((flags_ & STRING_ADD_CHECK_RIGHT) == 0);
|
| - GenerateConvertArgument(
|
| - masm, 1 * kPointerSize, r0, r2, r3, r4, r5, &call_builtin);
|
| - builtin_id = Builtins::STRING_ADD_RIGHT;
|
| - } else if ((flags_ & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
|
| - ASSERT((flags_ & STRING_ADD_CHECK_LEFT) == 0);
|
| - GenerateConvertArgument(
|
| - masm, 0 * kPointerSize, r1, r2, r3, r4, r5, &call_builtin);
|
| - builtin_id = Builtins::STRING_ADD_LEFT;
|
| + if (IsFastSmiOrObjectElementsKind(elements_kind())) {
|
| + // Check that the elements are in fast mode and writable.
|
| + __ CheckMap(elements,
|
| + scratch,
|
| + Heap::kFixedArrayMapRootIndex,
|
| + &call_builtin,
|
| + DONT_DO_SMI_CHECK);
|
| }
|
|
|
| - // Both arguments are strings.
|
| - // r0: first string
|
| - // r1: second string
|
| - // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
| - // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
| - {
|
| - Label strings_not_empty;
|
| - // Check if either of the strings are empty. In that case return the other.
|
| - __ ldr(r2, FieldMemOperand(r0, String::kLengthOffset));
|
| - __ ldr(r3, FieldMemOperand(r1, String::kLengthOffset));
|
| - STATIC_ASSERT(kSmiTag == 0);
|
| - __ cmp(r2, Operand(Smi::FromInt(0))); // Test if first string is empty.
|
| - __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
|
| - STATIC_ASSERT(kSmiTag == 0);
|
| - // Else test if second string is empty.
|
| - __ cmp(r3, Operand(Smi::FromInt(0)), ne);
|
| - __ b(ne, &strings_not_empty); // If either string was empty, return r0.
|
| -
|
| - __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
| - __ add(sp, sp, Operand(2 * kPointerSize));
|
| - __ Ret();
|
| -
|
| - __ bind(&strings_not_empty);
|
| - }
|
| + // Get the array's length into scratch and calculate new length.
|
| + __ ldr(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
| + __ add(scratch, scratch, Operand(Smi::FromInt(argc)));
|
|
|
| - __ SmiUntag(r2);
|
| - __ SmiUntag(r3);
|
| - // Both strings are non-empty.
|
| - // r0: first string
|
| - // r1: second string
|
| - // r2: length of first string
|
| - // r3: length of second string
|
| - // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
| - // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
| - // Look at the length of the result of adding the two strings.
|
| - Label string_add_flat_result, longer_than_two;
|
| - // Adding two lengths can't overflow.
|
| - STATIC_ASSERT(String::kMaxLength < String::kMaxLength * 2);
|
| - __ add(r6, r2, Operand(r3));
|
| - // Use the string table when adding two one character strings, as it
|
| - // helps later optimizations to return a string here.
|
| - __ cmp(r6, Operand(2));
|
| - __ b(ne, &longer_than_two);
|
| -
|
| - // Check that both strings are non-external ASCII strings.
|
| - if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
|
| - __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| - __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| - __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| - __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| - }
|
| - __ JumpIfBothInstanceTypesAreNotSequentialAscii(r4, r5, r6, r3,
|
| - &call_runtime);
|
| -
|
| - // Get the two characters forming the sub string.
|
| - __ ldrb(r2, FieldMemOperand(r0, SeqOneByteString::kHeaderSize));
|
| - __ ldrb(r3, FieldMemOperand(r1, SeqOneByteString::kHeaderSize));
|
| -
|
| - // Try to lookup two character string in string table. If it is not found
|
| - // just allocate a new one.
|
| - Label make_two_character_string;
|
| - StringHelper::GenerateTwoCharacterStringTableProbe(
|
| - masm, r2, r3, r6, r0, r4, r5, r9, &make_two_character_string);
|
| - __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
| - __ add(sp, sp, Operand(2 * kPointerSize));
|
| - __ Ret();
|
| + // Get the elements' length.
|
| + __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
|
|
| - __ bind(&make_two_character_string);
|
| - // Resulting string has length 2 and first chars of two strings
|
| - // are combined into single halfword in r2 register.
|
| - // So we can fill resulting string without two loops by a single
|
| - // halfword store instruction (which assumes that processor is
|
| - // in a little endian mode)
|
| - __ mov(r6, Operand(2));
|
| - __ AllocateAsciiString(r0, r6, r4, r5, r9, &call_runtime);
|
| - __ strh(r2, FieldMemOperand(r0, SeqOneByteString::kHeaderSize));
|
| - __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
| - __ add(sp, sp, Operand(2 * kPointerSize));
|
| - __ Ret();
|
| + // Check if we could survive without allocation.
|
| + __ cmp(scratch, r4);
|
|
|
| - __ bind(&longer_than_two);
|
| - // Check if resulting string will be flat.
|
| - __ cmp(r6, Operand(ConsString::kMinLength));
|
| - __ b(lt, &string_add_flat_result);
|
| - // Handle exceptionally long strings in the runtime system.
|
| - STATIC_ASSERT((String::kMaxLength & 0x80000000) == 0);
|
| - ASSERT(IsPowerOf2(String::kMaxLength + 1));
|
| - // kMaxLength + 1 is representable as shifted literal, kMaxLength is not.
|
| - __ cmp(r6, Operand(String::kMaxLength + 1));
|
| - __ b(hs, &call_runtime);
|
| -
|
| - // If result is not supposed to be flat, allocate a cons string object.
|
| - // If both strings are ASCII the result is an ASCII cons string.
|
| - if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
|
| - __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| - __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| - __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| - __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| - }
|
| - Label non_ascii, allocated, ascii_data;
|
| - STATIC_ASSERT(kTwoByteStringTag == 0);
|
| - __ tst(r4, Operand(kStringEncodingMask));
|
| - __ tst(r5, Operand(kStringEncodingMask), ne);
|
| - __ b(eq, &non_ascii);
|
| -
|
| - // Allocate an ASCII cons string.
|
| - __ bind(&ascii_data);
|
| - __ AllocateAsciiConsString(r3, r6, r4, r5, &call_runtime);
|
| - __ bind(&allocated);
|
| - // Fill the fields of the cons string.
|
| - Label skip_write_barrier, after_writing;
|
| - ExternalReference high_promotion_mode = ExternalReference::
|
| - new_space_high_promotion_mode_active_address(masm->isolate());
|
| - __ mov(r4, Operand(high_promotion_mode));
|
| - __ ldr(r4, MemOperand(r4, 0));
|
| - __ cmp(r4, Operand::Zero());
|
| - __ b(eq, &skip_write_barrier);
|
| -
|
| - __ str(r0, FieldMemOperand(r3, ConsString::kFirstOffset));
|
| - __ RecordWriteField(r3,
|
| - ConsString::kFirstOffset,
|
| - r0,
|
| - r4,
|
| - kLRHasNotBeenSaved,
|
| - kDontSaveFPRegs);
|
| - __ str(r1, FieldMemOperand(r3, ConsString::kSecondOffset));
|
| - __ RecordWriteField(r3,
|
| - ConsString::kSecondOffset,
|
| - r1,
|
| - r4,
|
| - kLRHasNotBeenSaved,
|
| - kDontSaveFPRegs);
|
| - __ jmp(&after_writing);
|
| + const int kEndElementsOffset =
|
| + FixedArray::kHeaderSize - kHeapObjectTag - argc * kPointerSize;
|
|
|
| - __ bind(&skip_write_barrier);
|
| - __ str(r0, FieldMemOperand(r3, ConsString::kFirstOffset));
|
| - __ str(r1, FieldMemOperand(r3, ConsString::kSecondOffset));
|
| + if (IsFastSmiOrObjectElementsKind(elements_kind())) {
|
| + __ b(gt, &attempt_to_grow_elements);
|
|
|
| - __ bind(&after_writing);
|
| + // Check if value is a smi.
|
| + __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize));
|
| + __ JumpIfNotSmi(r4, &with_write_barrier);
|
|
|
| - __ mov(r0, Operand(r3));
|
| - __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
| - __ add(sp, sp, Operand(2 * kPointerSize));
|
| - __ Ret();
|
| + // Store the value.
|
| + // We may need a register containing the address end_elements below, so
|
| + // write back the value in end_elements.
|
| + __ add(end_elements, elements, Operand::PointerOffsetFromSmiKey(scratch));
|
| + __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex));
|
| + } else {
|
| + // Check if we could survive without allocation.
|
| + __ cmp(scratch, r4);
|
| + __ b(gt, &call_builtin);
|
|
|
| - __ bind(&non_ascii);
|
| - // At least one of the strings is two-byte. Check whether it happens
|
| - // to contain only one byte characters.
|
| - // r4: first instance type.
|
| - // r5: second instance type.
|
| - __ tst(r4, Operand(kOneByteDataHintMask));
|
| - __ tst(r5, Operand(kOneByteDataHintMask), ne);
|
| - __ b(ne, &ascii_data);
|
| - __ eor(r4, r4, Operand(r5));
|
| - STATIC_ASSERT(kOneByteStringTag != 0 && kOneByteDataHintTag != 0);
|
| - __ and_(r4, r4, Operand(kOneByteStringTag | kOneByteDataHintTag));
|
| - __ cmp(r4, Operand(kOneByteStringTag | kOneByteDataHintTag));
|
| - __ b(eq, &ascii_data);
|
| -
|
| - // Allocate a two byte cons string.
|
| - __ AllocateTwoByteConsString(r3, r6, r4, r5, &call_runtime);
|
| - __ jmp(&allocated);
|
| -
|
| - // We cannot encounter sliced strings or cons strings here since:
|
| - STATIC_ASSERT(SlicedString::kMinLength >= ConsString::kMinLength);
|
| - // Handle creating a flat result from either external or sequential strings.
|
| - // Locate the first characters' locations.
|
| - // r0: first string
|
| - // r1: second string
|
| - // r2: length of first string
|
| - // r3: length of second string
|
| - // r4: first string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
| - // r5: second string instance type (if flags_ == NO_STRING_ADD_FLAGS)
|
| - // r6: sum of lengths.
|
| - Label first_prepared, second_prepared;
|
| - __ bind(&string_add_flat_result);
|
| - if ((flags_ & STRING_ADD_CHECK_BOTH) != STRING_ADD_CHECK_BOTH) {
|
| - __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| - __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| - __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| - __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| + __ ldr(r4, MemOperand(sp, (argc - 1) * kPointerSize));
|
| + __ StoreNumberToDoubleElements(r4, scratch, elements, r5, d0,
|
| + &call_builtin, argc * kDoubleSize);
|
| }
|
|
|
| - // Check whether both strings have same encoding
|
| - __ eor(ip, r4, Operand(r5));
|
| - ASSERT(__ ImmediateFitsAddrMode1Instruction(kStringEncodingMask));
|
| - __ tst(ip, Operand(kStringEncodingMask));
|
| - __ b(ne, &call_runtime);
|
| + // Save new length.
|
| + __ str(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
| + __ Drop(argc + 1);
|
| + __ mov(r0, scratch);
|
| + __ Ret();
|
|
|
| - STATIC_ASSERT(kSeqStringTag == 0);
|
| - __ tst(r4, Operand(kStringRepresentationMask));
|
| - STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
| - __ add(r6,
|
| - r0,
|
| - Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag),
|
| - LeaveCC,
|
| - eq);
|
| - __ b(eq, &first_prepared);
|
| - // External string: rule out short external string and load string resource.
|
| - STATIC_ASSERT(kShortExternalStringTag != 0);
|
| - __ tst(r4, Operand(kShortExternalStringMask));
|
| - __ b(ne, &call_runtime);
|
| - __ ldr(r6, FieldMemOperand(r0, ExternalString::kResourceDataOffset));
|
| - __ bind(&first_prepared);
|
| + if (IsFastDoubleElementsKind(elements_kind())) {
|
| + __ bind(&call_builtin);
|
| + __ TailCallExternalReference(
|
| + ExternalReference(Builtins::c_ArrayPush, isolate), argc + 1, 1);
|
| + return;
|
| + }
|
|
|
| - STATIC_ASSERT(kSeqStringTag == 0);
|
| - __ tst(r5, Operand(kStringRepresentationMask));
|
| - STATIC_ASSERT(SeqOneByteString::kHeaderSize == SeqTwoByteString::kHeaderSize);
|
| - __ add(r1,
|
| - r1,
|
| - Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag),
|
| - LeaveCC,
|
| - eq);
|
| - __ b(eq, &second_prepared);
|
| - // External string: rule out short external string and load string resource.
|
| - STATIC_ASSERT(kShortExternalStringTag != 0);
|
| - __ tst(r5, Operand(kShortExternalStringMask));
|
| - __ b(ne, &call_runtime);
|
| - __ ldr(r1, FieldMemOperand(r1, ExternalString::kResourceDataOffset));
|
| - __ bind(&second_prepared);
|
| -
|
| - Label non_ascii_string_add_flat_result;
|
| - // r6: first character of first string
|
| - // r1: first character of second string
|
| - // r2: length of first string.
|
| - // r3: length of second string.
|
| - // Both strings have the same encoding.
|
| - STATIC_ASSERT(kTwoByteStringTag == 0);
|
| - __ tst(r5, Operand(kStringEncodingMask));
|
| - __ b(eq, &non_ascii_string_add_flat_result);
|
| + __ bind(&with_write_barrier);
|
| +
|
| + if (IsFastSmiElementsKind(elements_kind())) {
|
| + if (FLAG_trace_elements_transitions) __ jmp(&call_builtin);
|
| +
|
| + __ ldr(r9, FieldMemOperand(r4, HeapObject::kMapOffset));
|
| + __ LoadRoot(ip, Heap::kHeapNumberMapRootIndex);
|
| + __ cmp(r9, ip);
|
| + __ b(eq, &call_builtin);
|
| +
|
| + ElementsKind target_kind = IsHoleyElementsKind(elements_kind())
|
| + ? FAST_HOLEY_ELEMENTS : FAST_ELEMENTS;
|
| + __ ldr(r3, ContextOperand(cp, Context::GLOBAL_OBJECT_INDEX));
|
| + __ ldr(r3, FieldMemOperand(r3, GlobalObject::kNativeContextOffset));
|
| + __ ldr(r3, ContextOperand(r3, Context::JS_ARRAY_MAPS_INDEX));
|
| + const int header_size = FixedArrayBase::kHeaderSize;
|
| + // Verify that the object can be transitioned in place.
|
| + const int origin_offset = header_size + elements_kind() * kPointerSize;
|
| + __ ldr(r2, FieldMemOperand(receiver, origin_offset));
|
| + __ ldr(ip, FieldMemOperand(r3, HeapObject::kMapOffset));
|
| + __ cmp(r2, ip);
|
| + __ b(ne, &call_builtin);
|
|
|
| - __ add(r2, r2, Operand(r3));
|
| - __ AllocateAsciiString(r0, r2, r4, r5, r9, &call_runtime);
|
| - __ sub(r2, r2, Operand(r3));
|
| - __ add(r5, r0, Operand(SeqOneByteString::kHeaderSize - kHeapObjectTag));
|
| - // r0: result string.
|
| - // r6: first character of first string.
|
| - // r1: first character of second string.
|
| - // r2: length of first string.
|
| - // r3: length of second string.
|
| - // r5: first character of result.
|
| - StringHelper::GenerateCopyCharacters(masm, r5, r6, r2, r4, true);
|
| - // r5: next character of result.
|
| - StringHelper::GenerateCopyCharacters(masm, r5, r1, r3, r4, true);
|
| - __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
| - __ add(sp, sp, Operand(2 * kPointerSize));
|
| - __ Ret();
|
| + const int target_offset = header_size + target_kind * kPointerSize;
|
| + __ ldr(r3, FieldMemOperand(r3, target_offset));
|
| + __ mov(r2, receiver);
|
| + ElementsTransitionGenerator::GenerateMapChangeElementsTransition(
|
| + masm, DONT_TRACK_ALLOCATION_SITE, NULL);
|
| + }
|
|
|
| - __ bind(&non_ascii_string_add_flat_result);
|
| - __ add(r2, r2, Operand(r3));
|
| - __ AllocateTwoByteString(r0, r2, r4, r5, r9, &call_runtime);
|
| - __ sub(r2, r2, Operand(r3));
|
| - __ add(r5, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| - // r0: result string.
|
| - // r6: first character of first string.
|
| - // r1: first character of second string.
|
| - // r2: length of first string.
|
| - // r3: length of second string.
|
| - // r5: first character of result.
|
| - StringHelper::GenerateCopyCharacters(masm, r5, r6, r2, r4, false);
|
| - // r5: next character of result.
|
| - StringHelper::GenerateCopyCharacters(masm, r5, r1, r3, r4, false);
|
| - __ IncrementCounter(counters->string_add_native(), 1, r2, r3);
|
| - __ add(sp, sp, Operand(2 * kPointerSize));
|
| + // Save new length.
|
| + __ str(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
| +
|
| + // Store the value.
|
| + // We may need a register containing the address end_elements below, so write
|
| + // back the value in end_elements.
|
| + __ add(end_elements, elements, Operand::PointerOffsetFromSmiKey(scratch));
|
| + __ str(r4, MemOperand(end_elements, kEndElementsOffset, PreIndex));
|
| +
|
| + __ RecordWrite(elements,
|
| + end_elements,
|
| + r4,
|
| + kLRHasNotBeenSaved,
|
| + kDontSaveFPRegs,
|
| + EMIT_REMEMBERED_SET,
|
| + OMIT_SMI_CHECK);
|
| + __ Drop(argc + 1);
|
| + __ mov(r0, scratch);
|
| __ Ret();
|
|
|
| - // Just jump to runtime to add the two strings.
|
| - __ bind(&call_runtime);
|
| - __ TailCallRuntime(Runtime::kStringAdd, 2, 1);
|
| + __ bind(&attempt_to_grow_elements);
|
| + // scratch: array's length + 1.
|
|
|
| - if (call_builtin.is_linked()) {
|
| + if (!FLAG_inline_new) {
|
| __ bind(&call_builtin);
|
| - __ InvokeBuiltin(builtin_id, JUMP_FUNCTION);
|
| + __ TailCallExternalReference(
|
| + ExternalReference(Builtins::c_ArrayPush, isolate), argc + 1, 1);
|
| + return;
|
| }
|
| -}
|
|
|
| + __ ldr(r2, MemOperand(sp, (argc - 1) * kPointerSize));
|
| + // Growing elements that are SMI-only requires special handling in case the
|
| + // new element is non-Smi. For now, delegate to the builtin.
|
| + if (IsFastSmiElementsKind(elements_kind())) {
|
| + __ JumpIfNotSmi(r2, &call_builtin);
|
| + }
|
|
|
| -void StringAddStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
|
| - __ push(r0);
|
| - __ push(r1);
|
| -}
|
| + // We could be lucky and the elements array could be at the top of new-space.
|
| + // In this case we can just grow it in place by moving the allocation pointer
|
| + // up.
|
| + ExternalReference new_space_allocation_top =
|
| + ExternalReference::new_space_allocation_top_address(isolate);
|
| + ExternalReference new_space_allocation_limit =
|
| + ExternalReference::new_space_allocation_limit_address(isolate);
|
| +
|
| + const int kAllocationDelta = 4;
|
| + ASSERT(kAllocationDelta >= argc);
|
| + // Load top and check if it is the end of elements.
|
| + __ add(end_elements, elements, Operand::PointerOffsetFromSmiKey(scratch));
|
| + __ add(end_elements, end_elements, Operand(kEndElementsOffset));
|
| + __ mov(r4, Operand(new_space_allocation_top));
|
| + __ ldr(r3, MemOperand(r4));
|
| + __ cmp(end_elements, r3);
|
| + __ b(ne, &call_builtin);
|
| +
|
| + __ mov(r9, Operand(new_space_allocation_limit));
|
| + __ ldr(r9, MemOperand(r9));
|
| + __ add(r3, r3, Operand(kAllocationDelta * kPointerSize));
|
| + __ cmp(r3, r9);
|
| + __ b(hi, &call_builtin);
|
| +
|
| + // We fit and could grow elements.
|
| + // Update new_space_allocation_top.
|
| + __ str(r3, MemOperand(r4));
|
| + // Push the argument.
|
| + __ str(r2, MemOperand(end_elements));
|
| + // Fill the rest with holes.
|
| + __ LoadRoot(r3, Heap::kTheHoleValueRootIndex);
|
| + for (int i = 1; i < kAllocationDelta; i++) {
|
| + __ str(r3, MemOperand(end_elements, i * kPointerSize));
|
| + }
|
| +
|
| + // Update elements' and array's sizes.
|
| + __ str(scratch, FieldMemOperand(receiver, JSArray::kLengthOffset));
|
| + __ ldr(r4, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
| + __ add(r4, r4, Operand(Smi::FromInt(kAllocationDelta)));
|
| + __ str(r4, FieldMemOperand(elements, FixedArray::kLengthOffset));
|
|
|
| + // Elements are in new space, so write barrier is not required.
|
| + __ Drop(argc + 1);
|
| + __ mov(r0, scratch);
|
| + __ Ret();
|
|
|
| -void StringAddStub::GenerateRegisterArgsPop(MacroAssembler* masm) {
|
| - __ pop(r1);
|
| - __ pop(r0);
|
| + __ bind(&call_builtin);
|
| + __ TailCallExternalReference(
|
| + ExternalReference(Builtins::c_ArrayPush, isolate), argc + 1, 1);
|
| }
|
|
|
|
|
| -void StringAddStub::GenerateConvertArgument(MacroAssembler* masm,
|
| - int stack_offset,
|
| - Register arg,
|
| - Register scratch1,
|
| - Register scratch2,
|
| - Register scratch3,
|
| - Register scratch4,
|
| - Label* slow) {
|
| - // First check if the argument is already a string.
|
| - Label not_string, done;
|
| - __ JumpIfSmi(arg, ¬_string);
|
| - __ CompareObjectType(arg, scratch1, scratch1, FIRST_NONSTRING_TYPE);
|
| - __ b(lt, &done);
|
| -
|
| - // Check the number to string cache.
|
| - __ bind(¬_string);
|
| - // Puts the cached result into scratch1.
|
| - __ LookupNumberStringCache(arg, scratch1, scratch2, scratch3, scratch4, slow);
|
| - __ mov(arg, scratch1);
|
| - __ str(arg, MemOperand(sp, stack_offset));
|
| - __ bind(&done);
|
| +void BinaryOpICWithAllocationSiteStub::Generate(MacroAssembler* masm) {
|
| + // ----------- S t a t e -------------
|
| + // -- r1 : left
|
| + // -- r0 : right
|
| + // -- lr : return address
|
| + // -----------------------------------
|
| + Isolate* isolate = masm->isolate();
|
| +
|
| + // Load r2 with the allocation site. We stick an undefined dummy value here
|
| + // and replace it with the real allocation site later when we instantiate this
|
| + // stub in BinaryOpICWithAllocationSiteStub::GetCodeCopyFromTemplate().
|
| + __ Move(r2, handle(isolate->heap()->undefined_value()));
|
| +
|
| + // Make sure that we actually patched the allocation site.
|
| + if (FLAG_debug_code) {
|
| + __ tst(r2, Operand(kSmiTagMask));
|
| + __ Assert(ne, kExpectedAllocationSite);
|
| + __ push(r2);
|
| + __ ldr(r2, FieldMemOperand(r2, HeapObject::kMapOffset));
|
| + __ LoadRoot(ip, Heap::kAllocationSiteMapRootIndex);
|
| + __ cmp(r2, ip);
|
| + __ pop(r2);
|
| + __ Assert(eq, kExpectedAllocationSite);
|
| + }
|
| +
|
| + // Tail call into the stub that handles binary operations with allocation
|
| + // sites.
|
| + BinaryOpWithAllocationSiteStub stub(state_);
|
| + __ TailCallStub(&stub);
|
| }
|
|
|
|
|
| @@ -5580,7 +5229,7 @@ static void CreateArrayDispatch(MacroAssembler* masm,
|
|
|
| static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
|
| AllocationSiteOverrideMode mode) {
|
| - // r2 - type info cell (if mode != DISABLE_ALLOCATION_SITES)
|
| + // r2 - allocation site (if mode != DISABLE_ALLOCATION_SITES)
|
| // r3 - kind (if mode != DISABLE_ALLOCATION_SITES)
|
| // r0 - number of arguments
|
| // r1 - constructor?
|
| @@ -5620,22 +5269,20 @@ static void CreateArrayDispatchOneArgument(MacroAssembler* masm,
|
| // We are going to create a holey array, but our kind is non-holey.
|
| // Fix kind and retry (only if we have an allocation site in the cell).
|
| __ add(r3, r3, Operand(1));
|
| - __ ldr(r5, FieldMemOperand(r2, Cell::kValueOffset));
|
|
|
| if (FLAG_debug_code) {
|
| - __ ldr(r5, FieldMemOperand(r5, 0));
|
| + __ ldr(r5, FieldMemOperand(r2, 0));
|
| __ CompareRoot(r5, Heap::kAllocationSiteMapRootIndex);
|
| - __ Assert(eq, kExpectedAllocationSiteInCell);
|
| - __ ldr(r5, FieldMemOperand(r2, Cell::kValueOffset));
|
| + __ Assert(eq, kExpectedAllocationSite);
|
| }
|
|
|
| // Save the resulting elements kind in type info. We can't just store r3
|
| // in the AllocationSite::transition_info field because elements kind is
|
| // restricted to a portion of the field...upper bits need to be left alone.
|
| STATIC_ASSERT(AllocationSite::ElementsKindBits::kShift == 0);
|
| - __ ldr(r4, FieldMemOperand(r5, AllocationSite::kTransitionInfoOffset));
|
| + __ ldr(r4, FieldMemOperand(r2, AllocationSite::kTransitionInfoOffset));
|
| __ add(r4, r4, Operand(Smi::FromInt(kFastElementsKindPackedToHoley)));
|
| - __ str(r4, FieldMemOperand(r5, AllocationSite::kTransitionInfoOffset));
|
| + __ str(r4, FieldMemOperand(r2, AllocationSite::kTransitionInfoOffset));
|
|
|
| __ bind(&normal_sequence);
|
| int last_index = GetSequenceIndexFromFastElementsKind(
|
| @@ -5759,15 +5406,15 @@ void ArrayConstructorStub::Generate(MacroAssembler* masm) {
|
| // Get the elements kind and case on that.
|
| __ CompareRoot(r2, Heap::kUndefinedValueRootIndex);
|
| __ b(eq, &no_info);
|
| - __ ldr(r3, FieldMemOperand(r2, Cell::kValueOffset));
|
| + __ ldr(r2, FieldMemOperand(r2, Cell::kValueOffset));
|
|
|
| // If the type cell is undefined, or contains anything other than an
|
| // AllocationSite, call an array constructor that doesn't use AllocationSites.
|
| - __ ldr(r4, FieldMemOperand(r3, 0));
|
| + __ ldr(r4, FieldMemOperand(r2, 0));
|
| __ CompareRoot(r4, Heap::kAllocationSiteMapRootIndex);
|
| __ b(ne, &no_info);
|
|
|
| - __ ldr(r3, FieldMemOperand(r3, AllocationSite::kTransitionInfoOffset));
|
| + __ ldr(r3, FieldMemOperand(r2, AllocationSite::kTransitionInfoOffset));
|
| __ SmiUntag(r3);
|
| STATIC_ASSERT(AllocationSite::ElementsKindBits::kShift == 0);
|
| __ and_(r3, r3, Operand(AllocationSite::ElementsKindBits::kMask));
|
|
|