| Index: src/arm/codegen-arm.cc
|
| ===================================================================
|
| --- src/arm/codegen-arm.cc (revision 3890)
|
| +++ src/arm/codegen-arm.cc (working copy)
|
| @@ -121,14 +121,10 @@
|
| // -------------------------------------------------------------------------
|
| // CodeGenerator implementation
|
|
|
| -CodeGenerator::CodeGenerator(MacroAssembler* masm,
|
| - Handle<Script> script,
|
| - bool is_eval)
|
| - : is_eval_(is_eval),
|
| - script_(script),
|
| - deferred_(8),
|
| +CodeGenerator::CodeGenerator(MacroAssembler* masm)
|
| + : deferred_(8),
|
| masm_(masm),
|
| - scope_(NULL),
|
| + info_(NULL),
|
| frame_(NULL),
|
| allocator_(NULL),
|
| cc_reg_(al),
|
| @@ -137,23 +133,21 @@
|
| }
|
|
|
|
|
| +Scope* CodeGenerator::scope() { return info_->function()->scope(); }
|
| +
|
| +
|
| // Calling conventions:
|
| // fp: caller's frame pointer
|
| // sp: stack pointer
|
| // r1: called JS function
|
| // cp: callee's context
|
|
|
| -void CodeGenerator::Generate(FunctionLiteral* fun,
|
| - Mode mode,
|
| - CompilationInfo* info) {
|
| +void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
|
| // Record the position for debugging purposes.
|
| - CodeForFunctionPosition(fun);
|
| + CodeForFunctionPosition(info->function());
|
|
|
| - ZoneList<Statement*>* body = fun->body();
|
| -
|
| // Initialize state.
|
| - ASSERT(scope_ == NULL);
|
| - scope_ = fun->scope();
|
| + info_ = info;
|
| ASSERT(allocator_ == NULL);
|
| RegisterAllocator register_allocator(this);
|
| allocator_ = ®ister_allocator;
|
| @@ -174,7 +168,7 @@
|
|
|
| #ifdef DEBUG
|
| if (strlen(FLAG_stop_at) > 0 &&
|
| - fun->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
|
| + info->function()->name()->IsEqualTo(CStrVector(FLAG_stop_at))) {
|
| frame_->SpillAll();
|
| __ stop("stop-at");
|
| }
|
| @@ -189,7 +183,7 @@
|
| frame_->AllocateStackSlots();
|
|
|
| VirtualFrame::SpilledScope spilled_scope;
|
| - int heap_slots = scope_->num_heap_slots();
|
| + int heap_slots = scope()->num_heap_slots();
|
| if (heap_slots > 0) {
|
| // Allocate local context.
|
| // Get outer context and create a new context based on it.
|
| @@ -219,7 +213,6 @@
|
| // 3) don't copy parameter operand code from SlotOperand!
|
| {
|
| Comment cmnt2(masm_, "[ copy context parameters into .context");
|
| -
|
| // Note that iteration order is relevant here! If we have the same
|
| // parameter twice (e.g., function (x, y, x)), and that parameter
|
| // needs to be copied into the context, it must be the last argument
|
| @@ -228,12 +221,11 @@
|
| // order: such a parameter is copied repeatedly into the same
|
| // context location and thus the last value is what is seen inside
|
| // the function.
|
| - for (int i = 0; i < scope_->num_parameters(); i++) {
|
| - Variable* par = scope_->parameter(i);
|
| + for (int i = 0; i < scope()->num_parameters(); i++) {
|
| + Variable* par = scope()->parameter(i);
|
| Slot* slot = par->slot();
|
| if (slot != NULL && slot->type() == Slot::CONTEXT) {
|
| - // No parameters in global scope.
|
| - ASSERT(!scope_->is_global_scope());
|
| + ASSERT(!scope()->is_global_scope()); // No params in global scope.
|
| __ ldr(r1, frame_->ParameterAt(i));
|
| // Loads r2 with context; used below in RecordWrite.
|
| __ str(r1, SlotOperand(slot, r2));
|
| @@ -249,20 +241,20 @@
|
| // Store the arguments object. This must happen after context
|
| // initialization because the arguments object may be stored in the
|
| // context.
|
| - if (scope_->arguments() != NULL) {
|
| + if (scope()->arguments() != NULL) {
|
| Comment cmnt(masm_, "[ allocate arguments object");
|
| - ASSERT(scope_->arguments_shadow() != NULL);
|
| - Variable* arguments = scope_->arguments()->var();
|
| - Variable* shadow = scope_->arguments_shadow()->var();
|
| + ASSERT(scope()->arguments_shadow() != NULL);
|
| + Variable* arguments = scope()->arguments()->var();
|
| + Variable* shadow = scope()->arguments_shadow()->var();
|
| ASSERT(arguments != NULL && arguments->slot() != NULL);
|
| ASSERT(shadow != NULL && shadow->slot() != NULL);
|
| ArgumentsAccessStub stub(ArgumentsAccessStub::NEW_OBJECT);
|
| __ ldr(r2, frame_->Function());
|
| // The receiver is below the arguments, the return address, and the
|
| // frame pointer on the stack.
|
| - const int kReceiverDisplacement = 2 + scope_->num_parameters();
|
| + const int kReceiverDisplacement = 2 + scope()->num_parameters();
|
| __ add(r1, fp, Operand(kReceiverDisplacement * kPointerSize));
|
| - __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
|
| + __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
|
| frame_->Adjust(3);
|
| __ stm(db_w, sp, r0.bit() | r1.bit() | r2.bit());
|
| frame_->CallStub(&stub, 3);
|
| @@ -273,10 +265,10 @@
|
| }
|
|
|
| // Initialize ThisFunction reference if present.
|
| - if (scope_->is_function_scope() && scope_->function() != NULL) {
|
| + if (scope()->is_function_scope() && scope()->function() != NULL) {
|
| __ mov(ip, Operand(Factory::the_hole_value()));
|
| frame_->EmitPush(ip);
|
| - StoreToSlot(scope_->function()->slot(), NOT_CONST_INIT);
|
| + StoreToSlot(scope()->function()->slot(), NOT_CONST_INIT);
|
| }
|
| } else {
|
| // When used as the secondary compiler for splitting, r1, cp,
|
| @@ -295,12 +287,12 @@
|
| // Generate code to 'execute' declarations and initialize functions
|
| // (source elements). In case of an illegal redeclaration we need to
|
| // handle that instead of processing the declarations.
|
| - if (scope_->HasIllegalRedeclaration()) {
|
| + if (scope()->HasIllegalRedeclaration()) {
|
| Comment cmnt(masm_, "[ illegal redeclarations");
|
| - scope_->VisitIllegalRedeclaration(this);
|
| + scope()->VisitIllegalRedeclaration(this);
|
| } else {
|
| Comment cmnt(masm_, "[ declarations");
|
| - ProcessDeclarations(scope_->declarations());
|
| + ProcessDeclarations(scope()->declarations());
|
| // Bail out if a stack-overflow exception occurred when processing
|
| // declarations.
|
| if (HasStackOverflow()) return;
|
| @@ -314,7 +306,7 @@
|
| // Compile the body of the function in a vanilla state. Don't
|
| // bother compiling all the code if the scope has an illegal
|
| // redeclaration.
|
| - if (!scope_->HasIllegalRedeclaration()) {
|
| + if (!scope()->HasIllegalRedeclaration()) {
|
| Comment cmnt(masm_, "[ function body");
|
| #ifdef DEBUG
|
| bool is_builtin = Bootstrapper::IsActive();
|
| @@ -325,14 +317,14 @@
|
| // Ignore the return value.
|
| }
|
| #endif
|
| - VisitStatementsAndSpill(body);
|
| + VisitStatementsAndSpill(info->function()->body());
|
| }
|
| }
|
|
|
| // Generate the return sequence if necessary.
|
| if (has_valid_frame() || function_return_.is_linked()) {
|
| if (!function_return_.is_linked()) {
|
| - CodeForReturnPosition(fun);
|
| + CodeForReturnPosition(info->function());
|
| }
|
| // exit
|
| // r0: result
|
| @@ -355,7 +347,7 @@
|
|
|
| // Calculate the exact length of the return sequence and make sure that
|
| // the constant pool is not emitted inside of the return sequence.
|
| - int32_t sp_delta = (scope_->num_parameters() + 1) * kPointerSize;
|
| + int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
|
| int return_sequence_length = Assembler::kJSReturnSequenceLength;
|
| if (!masm_->ImmediateFitsAddrMode1Instruction(sp_delta)) {
|
| // Additional mov instruction generated.
|
| @@ -395,7 +387,6 @@
|
| }
|
|
|
| allocator_ = NULL;
|
| - scope_ = NULL;
|
| }
|
|
|
|
|
| @@ -2302,7 +2293,7 @@
|
| Comment cmnt(masm_, "[ DebuggerStatament");
|
| CodeForStatementPosition(node);
|
| #ifdef ENABLE_DEBUGGER_SUPPORT
|
| - DebugerStatementStub ces;
|
| + DebuggerStatementStub ces;
|
| frame_->CallStub(&ces, 0);
|
| #endif
|
| // Ignore the return value.
|
| @@ -2341,7 +2332,7 @@
|
|
|
| // Build the function boilerplate and instantiate it.
|
| Handle<JSFunction> boilerplate =
|
| - Compiler::BuildBoilerplate(node, script_, this);
|
| + Compiler::BuildBoilerplate(node, script(), this);
|
| // Check for stack-overflow exception.
|
| if (HasStackOverflow()) {
|
| ASSERT(frame_->height() == original_height);
|
| @@ -3519,7 +3510,7 @@
|
|
|
| // Seed the result with the formal parameters count, which will be used
|
| // in case no arguments adaptor frame is found below the current frame.
|
| - __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
|
| + __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
|
|
|
| // Call the shared stub to get to the arguments.length.
|
| ArgumentsAccessStub stub(ArgumentsAccessStub::READ_LENGTH);
|
| @@ -3536,7 +3527,7 @@
|
| // Load the key into r1 and the formal parameters count into r0.
|
| LoadAndSpill(args->at(0));
|
| frame_->EmitPop(r1);
|
| - __ mov(r0, Operand(Smi::FromInt(scope_->num_parameters())));
|
| + __ mov(r0, Operand(Smi::FromInt(scope()->num_parameters())));
|
|
|
| // Call the shared stub to get to arguments[key].
|
| ArgumentsAccessStub stub(ArgumentsAccessStub::READ_ELEMENT);
|
| @@ -3560,7 +3551,8 @@
|
| Load(args->at(0));
|
| Load(args->at(1));
|
|
|
| - frame_->CallRuntime(Runtime::kStringAdd, 2);
|
| + StringAddStub stub(NO_STRING_ADD_FLAGS);
|
| + frame_->CallStub(&stub, 2);
|
| frame_->EmitPush(r0);
|
| }
|
|
|
| @@ -3572,7 +3564,8 @@
|
| Load(args->at(1));
|
| Load(args->at(2));
|
|
|
| - frame_->CallRuntime(Runtime::kSubString, 3);
|
| + SubStringStub stub;
|
| + frame_->CallStub(&stub, 3);
|
| frame_->EmitPush(r0);
|
| }
|
|
|
| @@ -5340,7 +5333,7 @@
|
| // r1 : first argument
|
| // r0 : second argument
|
| // sp[0] : second argument
|
| - // sp[1] : first argument
|
| + // sp[4] : first argument
|
|
|
| Label not_strings, not_string1, string1;
|
| __ tst(r1, Operand(kSmiTagMask));
|
| @@ -5355,7 +5348,8 @@
|
| __ b(ge, &string1);
|
|
|
| // First and second argument are strings.
|
| - __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
|
| + StringAddStub stub(NO_STRING_CHECK_IN_STUB);
|
| + __ TailCallStub(&stub);
|
|
|
| // Only first argument is a string.
|
| __ bind(&string1);
|
| @@ -5369,7 +5363,6 @@
|
| __ b(ge, ¬_strings);
|
|
|
| // Only second argument is a string.
|
| - __ b(¬_strings);
|
| __ InvokeBuiltin(Builtins::STRING_ADD_RIGHT, JUMP_JS);
|
|
|
| __ bind(¬_strings);
|
| @@ -5851,6 +5844,7 @@
|
| }
|
|
|
|
|
| +
|
| void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
|
| // r1 : x
|
| // r0 : y
|
| @@ -6043,9 +6037,7 @@
|
| case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break;
|
| case Token::SAR:
|
| // Remove tags from right operand.
|
| - __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y
|
| - // Use only the 5 least significant bits of the shift count.
|
| - __ and_(r2, r2, Operand(0x1f));
|
| + __ GetLeastBitsFromSmi(r2, r0, 5);
|
| __ mov(r0, Operand(r1, ASR, r2));
|
| // Smi tag result.
|
| __ bic(r0, r0, Operand(kSmiTagMask));
|
| @@ -6054,9 +6046,7 @@
|
| // Remove tags from operands. We can't do this on a 31 bit number
|
| // because then the 0s get shifted into bit 30 instead of bit 31.
|
| __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x
|
| - __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y
|
| - // Use only the 5 least significant bits of the shift count.
|
| - __ and_(r2, r2, Operand(0x1f));
|
| + __ GetLeastBitsFromSmi(r2, r0, 5);
|
| __ mov(r3, Operand(r3, LSR, r2));
|
| // Unsigned shift is not allowed to produce a negative number, so
|
| // check the sign bit and the sign bit after Smi tagging.
|
| @@ -6068,9 +6058,7 @@
|
| case Token::SHL:
|
| // Remove tags from operands.
|
| __ mov(r3, Operand(r1, ASR, kSmiTagSize)); // x
|
| - __ mov(r2, Operand(r0, ASR, kSmiTagSize)); // y
|
| - // Use only the 5 least significant bits of the shift count.
|
| - __ and_(r2, r2, Operand(0x1f));
|
| + __ GetLeastBitsFromSmi(r2, r0, 5);
|
| __ mov(r3, Operand(r3, LSL, r2));
|
| // Check that the signed result fits in a Smi.
|
| __ add(r2, r3, Operand(0x40000000), SetCC);
|
| @@ -6478,8 +6466,7 @@
|
| // r1: function
|
| // r2: receiver
|
| // r3: argc
|
| - __ add(r4, sp, Operand((kNumCalleeSaved + 1)*kPointerSize));
|
| - __ ldr(r4, MemOperand(r4)); // argv
|
| + __ ldr(r4, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize)); // argv
|
|
|
| // Push a frame with special values setup to mark it as an entry frame.
|
| // r0: code entry
|
| @@ -6837,8 +6824,342 @@
|
| }
|
|
|
|
|
| +void StringStubBase::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(0));
|
| + }
|
| + __ 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
|
| +};
|
| +
|
| +
|
| +void StringStubBase::GenerateCopyCharactersLong(MacroAssembler* masm,
|
| + Register dest,
|
| + Register src,
|
| + Register count,
|
| + Register scratch1,
|
| + Register scratch2,
|
| + Register scratch3,
|
| + Register scratch4,
|
| + Register scratch5,
|
| + int flags) {
|
| + bool ascii = (flags & COPY_ASCII) != 0;
|
| + bool dest_always_aligned = (flags & DEST_ALWAYS_ALIGNED) != 0;
|
| +
|
| + if (dest_always_aligned && FLAG_debug_code) {
|
| + // Check that destination is actually word aligned if the flag says
|
| + // that it is.
|
| + __ tst(dest, Operand(kPointerAlignmentMask));
|
| + __ Check(eq, "Destination of copy not aligned.");
|
| + }
|
| +
|
| + const int kReadAlignment = 4;
|
| + const int kReadAlignmentMask = kReadAlignment - 1;
|
| + // Ensure that reading an entire aligned word containing the last character
|
| + // of a string will not read outside the allocated area (because we pad up
|
| + // to kObjectAlignment).
|
| + ASSERT(kObjectAlignment >= kReadAlignment);
|
| + // Assumes word reads and writes are little endian.
|
| + // Nothing to do for zero characters.
|
| + Label done;
|
| + if (!ascii) {
|
| + __ add(count, count, Operand(count), SetCC);
|
| + } else {
|
| + __ cmp(count, Operand(0));
|
| + }
|
| + __ b(eq, &done);
|
| +
|
| + // Assume that you cannot read (or write) unaligned.
|
| + Label byte_loop;
|
| + // Must copy at least eight bytes, otherwise just do it one byte at a time.
|
| + __ cmp(count, Operand(8));
|
| + __ add(count, dest, Operand(count));
|
| + Register limit = count; // Read until src equals this.
|
| + __ b(lt, &byte_loop);
|
| +
|
| + if (!dest_always_aligned) {
|
| + // Align dest by byte copying. Copies between zero and three bytes.
|
| + __ and_(scratch4, dest, Operand(kReadAlignmentMask), SetCC);
|
| + Label dest_aligned;
|
| + __ b(eq, &dest_aligned);
|
| + __ cmp(scratch4, Operand(2));
|
| + __ ldrb(scratch1, MemOperand(src, 1, PostIndex));
|
| + __ ldrb(scratch2, MemOperand(src, 1, PostIndex), le);
|
| + __ ldrb(scratch3, MemOperand(src, 1, PostIndex), lt);
|
| + __ strb(scratch1, MemOperand(dest, 1, PostIndex));
|
| + __ strb(scratch2, MemOperand(dest, 1, PostIndex), le);
|
| + __ strb(scratch3, MemOperand(dest, 1, PostIndex), lt);
|
| + __ bind(&dest_aligned);
|
| + }
|
| +
|
| + Label simple_loop;
|
| +
|
| + __ sub(scratch4, dest, Operand(src));
|
| + __ and_(scratch4, scratch4, Operand(0x03), SetCC);
|
| + __ b(eq, &simple_loop);
|
| + // Shift register is number of bits in a source word that
|
| + // must be combined with bits in the next source word in order
|
| + // to create a destination word.
|
| +
|
| + // Complex loop for src/dst that are not aligned the same way.
|
| + {
|
| + Label loop;
|
| + __ mov(scratch4, Operand(scratch4, LSL, 3));
|
| + Register left_shift = scratch4;
|
| + __ and_(src, src, Operand(~3)); // Round down to load previous word.
|
| + __ ldr(scratch1, MemOperand(src, 4, PostIndex));
|
| + // Store the "shift" most significant bits of scratch in the least
|
| + // signficant bits (i.e., shift down by (32-shift)).
|
| + __ rsb(scratch2, left_shift, Operand(32));
|
| + Register right_shift = scratch2;
|
| + __ mov(scratch1, Operand(scratch1, LSR, right_shift));
|
| +
|
| + __ bind(&loop);
|
| + __ ldr(scratch3, MemOperand(src, 4, PostIndex));
|
| + __ sub(scratch5, limit, Operand(dest));
|
| + __ orr(scratch1, scratch1, Operand(scratch3, LSL, left_shift));
|
| + __ str(scratch1, MemOperand(dest, 4, PostIndex));
|
| + __ mov(scratch1, Operand(scratch3, LSR, right_shift));
|
| + // Loop if four or more bytes left to copy.
|
| + // Compare to eight, because we did the subtract before increasing dst.
|
| + __ sub(scratch5, scratch5, Operand(8), SetCC);
|
| + __ b(ge, &loop);
|
| + }
|
| + // There is now between zero and three bytes left to copy (negative that
|
| + // number is in scratch5), and between one and three bytes already read into
|
| + // scratch1 (eight times that number in scratch4). We may have read past
|
| + // the end of the string, but because objects are aligned, we have not read
|
| + // past the end of the object.
|
| + // Find the minimum of remaining characters to move and preloaded characters
|
| + // and write those as bytes.
|
| + __ add(scratch5, scratch5, Operand(4), SetCC);
|
| + __ b(eq, &done);
|
| + __ cmp(scratch4, Operand(scratch5, LSL, 3), ne);
|
| + // Move minimum of bytes read and bytes left to copy to scratch4.
|
| + __ mov(scratch5, Operand(scratch4, LSR, 3), LeaveCC, lt);
|
| + // Between one and three (value in scratch5) characters already read into
|
| + // scratch ready to write.
|
| + __ cmp(scratch5, Operand(2));
|
| + __ strb(scratch1, MemOperand(dest, 1, PostIndex));
|
| + __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, ge);
|
| + __ strb(scratch1, MemOperand(dest, 1, PostIndex), ge);
|
| + __ mov(scratch1, Operand(scratch1, LSR, 8), LeaveCC, gt);
|
| + __ strb(scratch1, MemOperand(dest, 1, PostIndex), gt);
|
| + // Copy any remaining bytes.
|
| + __ b(&byte_loop);
|
| +
|
| + // Simple loop.
|
| + // Copy words from src to dst, until less than four bytes left.
|
| + // Both src and dest are word aligned.
|
| + __ bind(&simple_loop);
|
| + {
|
| + Label loop;
|
| + __ bind(&loop);
|
| + __ ldr(scratch1, MemOperand(src, 4, PostIndex));
|
| + __ sub(scratch3, limit, Operand(dest));
|
| + __ str(scratch1, MemOperand(dest, 4, PostIndex));
|
| + // Compare to 8, not 4, because we do the substraction before increasing
|
| + // dest.
|
| + __ cmp(scratch3, Operand(8));
|
| + __ b(ge, &loop);
|
| + }
|
| +
|
| + // Copy bytes from src to dst until dst hits limit.
|
| + __ bind(&byte_loop);
|
| + __ cmp(dest, Operand(limit));
|
| + __ ldrb(scratch1, MemOperand(src, 1, PostIndex), lt);
|
| + __ b(ge, &done);
|
| + __ strb(scratch1, MemOperand(dest, 1, PostIndex));
|
| + __ b(&byte_loop);
|
| +
|
| + __ 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.
|
| +
|
| + static const int kToOffset = 0 * kPointerSize;
|
| + static const int kFromOffset = 1 * kPointerSize;
|
| + static const int kStringOffset = 2 * kPointerSize;
|
| +
|
| +
|
| + // Check bounds and smi-ness.
|
| + __ ldr(r7, MemOperand(sp, kToOffset));
|
| + __ ldr(r6, MemOperand(sp, kFromOffset));
|
| + ASSERT_EQ(0, kSmiTag);
|
| + ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize);
|
| + // I.e., arithmetic shift right by one un-smi-tags.
|
| + __ mov(r2, Operand(r7, ASR, 1), SetCC);
|
| + __ mov(r3, Operand(r6, ASR, 1), SetCC, cc);
|
| + // If either r2 or r6 had the smi tag bit set, then carry is set now.
|
| + __ b(cs, &runtime); // Either "from" or "to" is not a smi.
|
| + __ b(mi, &runtime); // From is negative.
|
| +
|
| + __ sub(r2, r2, Operand(r3), SetCC);
|
| + __ b(mi, &runtime); // Fail if from > to.
|
| + // Handle sub-strings of length 2 and less in the runtime system.
|
| + __ cmp(r2, Operand(2));
|
| + __ b(le, &runtime);
|
| +
|
| + // r2: length
|
| + // r6: from (smi)
|
| + // r7: to (smi)
|
| +
|
| + // Make sure first argument is a sequential (or flat) string.
|
| + __ ldr(r5, MemOperand(sp, kStringOffset));
|
| + ASSERT_EQ(0, kSmiTag);
|
| + __ tst(r5, Operand(kSmiTagMask));
|
| + __ b(eq, &runtime);
|
| + Condition is_string = masm->IsObjectStringType(r5, r1);
|
| + __ b(NegateCondition(is_string), &runtime);
|
| +
|
| + // r1: instance type
|
| + // r2: length
|
| + // r5: string
|
| + // r6: from (smi)
|
| + // r7: to (smi)
|
| + Label seq_string;
|
| + __ and_(r4, r1, Operand(kStringRepresentationMask));
|
| + ASSERT(kSeqStringTag < kConsStringTag);
|
| + ASSERT(kExternalStringTag > kConsStringTag);
|
| + __ cmp(r4, Operand(kConsStringTag));
|
| + __ b(gt, &runtime); // External strings go to runtime.
|
| + __ b(lt, &seq_string); // Sequential strings are handled directly.
|
| +
|
| + // Cons string. Try to recurse (once) on the first substring.
|
| + // (This adds a little more generality than necessary to handle flattened
|
| + // cons strings, but not much).
|
| + __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset));
|
| + __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset));
|
| + __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| + __ tst(r1, Operand(kStringRepresentationMask));
|
| + ASSERT_EQ(0, kSeqStringTag);
|
| + __ b(ne, &runtime); // Cons and External strings go to runtime.
|
| +
|
| + // Definitly a sequential string.
|
| + __ bind(&seq_string);
|
| +
|
| + // r1: instance type.
|
| + // r2: length
|
| + // r5: string
|
| + // r6: from (smi)
|
| + // r7: to (smi)
|
| + __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset));
|
| + __ cmp(r4, Operand(r7, ASR, 1));
|
| + __ b(lt, &runtime); // Fail if to > length.
|
| +
|
| + // r1: instance type.
|
| + // r2: result string length.
|
| + // r5: string.
|
| + // r6: from offset (smi)
|
| + // Check for flat ascii string.
|
| + Label non_ascii_flat;
|
| + __ tst(r1, Operand(kStringEncodingMask));
|
| + ASSERT_EQ(0, kTwoByteStringTag);
|
| + __ b(eq, &non_ascii_flat);
|
| +
|
| + // Allocate the result.
|
| + __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime);
|
| +
|
| + // r0: result string.
|
| + // r2: result string length.
|
| + // r5: string.
|
| + // r6: from offset (smi)
|
| + // Locate first character of result.
|
| + __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // Locate 'from' character of string.
|
| + __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + __ add(r5, r5, Operand(r6, ASR, 1));
|
| +
|
| + // r0: result string.
|
| + // r1: first character of result string.
|
| + // r2: result string length.
|
| + // r5: first character of sub string to copy.
|
| + ASSERT_EQ(0, SeqAsciiString::kHeaderSize & kObjectAlignmentMask);
|
| + GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
|
| + COPY_ASCII | DEST_ALWAYS_ALIGNED);
|
| + __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
|
| + __ add(sp, sp, Operand(3 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&non_ascii_flat);
|
| + // r2: result string length.
|
| + // r5: string.
|
| + // r6: from offset (smi)
|
| + // Check for flat two byte string.
|
| +
|
| + // Allocate the result.
|
| + __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime);
|
| +
|
| + // r0: result string.
|
| + // r2: result string length.
|
| + // r5: string.
|
| + // Locate first character of result.
|
| + __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + // Locate 'from' character of string.
|
| + __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + // As "from" is a smi it is 2 times the value which matches the size of a two
|
| + // byte character.
|
| + __ add(r5, r5, Operand(r6));
|
| +
|
| + // r0: result string.
|
| + // r1: first character of result.
|
| + // r2: result length.
|
| + // r5: first character of string to copy.
|
| + ASSERT_EQ(0, SeqTwoByteString::kHeaderSize & kObjectAlignmentMask);
|
| + GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9,
|
| + DEST_ALWAYS_ALIGNED);
|
| + __ IncrementCounter(&Counters::sub_string_native, 1, r3, r4);
|
| + __ add(sp, sp, Operand(3 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + // Just jump to runtime to create the sub string.
|
| + __ bind(&runtime);
|
| + __ TailCallRuntime(ExternalReference(Runtime::kSubString), 3, 1);
|
| +}
|
| +
|
| +
|
| void StringCompareStub::GenerateCompareFlatAsciiStrings(MacroAssembler* masm,
|
| Register left,
|
| Register right,
|
| @@ -6898,13 +7219,11 @@
|
| Label runtime;
|
|
|
| // Stack frame on entry.
|
| - // sp[0]: return address
|
| - // sp[4]: right string
|
| - // sp[8]: left string
|
| + // sp[0]: right string
|
| + // sp[4]: left string
|
| + __ ldr(r0, MemOperand(sp, 1 * kPointerSize)); // left
|
| + __ ldr(r1, MemOperand(sp, 0 * kPointerSize)); // right
|
|
|
| - __ ldr(r0, MemOperand(sp, 2 * kPointerSize)); // left
|
| - __ ldr(r1, MemOperand(sp, 1 * kPointerSize)); // right
|
| -
|
| Label not_same;
|
| __ cmp(r0, r1);
|
| __ b(ne, ¬_same);
|
| @@ -6932,6 +7251,220 @@
|
| }
|
|
|
|
|
| +void StringAddStub::Generate(MacroAssembler* masm) {
|
| + Label string_add_runtime;
|
| + // Stack on entry:
|
| + // sp[0]: second argument.
|
| + // sp[4]: first argument.
|
| +
|
| + // 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.
|
| + if (string_check_) {
|
| + ASSERT_EQ(0, kSmiTag);
|
| + __ JumpIfEitherSmi(r0, r1, &string_add_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));
|
| + ASSERT_EQ(0, kStringTag);
|
| + // If either is not a string, go to runtime.
|
| + __ tst(r4, Operand(kIsNotStringMask));
|
| + __ tst(r5, Operand(kIsNotStringMask), eq);
|
| + __ b(ne, &string_add_runtime);
|
| + }
|
| +
|
| + // Both arguments are strings.
|
| + // r0: first string
|
| + // r1: second string
|
| + // r4: first string instance type (if string_check_)
|
| + // r5: second string instance type (if string_check_)
|
| + {
|
| + 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));
|
| + __ cmp(r2, Operand(0)); // Test if first string is empty.
|
| + __ mov(r0, Operand(r1), LeaveCC, eq); // If first is empty, return second.
|
| + __ cmp(r3, Operand(0), ne); // Else test if second string is empty.
|
| + __ 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);
|
| + }
|
| +
|
| + // 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 string_check_)
|
| + // r5: second string instance type (if string_check_)
|
| + // Look at the length of the result of adding the two strings.
|
| + Label string_add_flat_result;
|
| + // Adding two lengths can't overflow.
|
| + ASSERT(String::kMaxLength * 2 > String::kMaxLength);
|
| + __ add(r6, r2, Operand(r3));
|
| + // Use the runtime system when adding two one character strings, as it
|
| + // contains optimizations for this specific case using the symbol table.
|
| + __ cmp(r6, Operand(2));
|
| + __ b(eq, &string_add_runtime);
|
| + // Check if resulting string will be flat.
|
| + __ cmp(r6, Operand(String::kMinNonFlatLength));
|
| + __ b(lt, &string_add_flat_result);
|
| + // Handle exceptionally long strings in the runtime system.
|
| + 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, &string_add_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 (!string_check_) {
|
| + __ 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;
|
| + ASSERT_EQ(0, kTwoByteStringTag);
|
| + __ tst(r4, Operand(kStringEncodingMask));
|
| + __ tst(r5, Operand(kStringEncodingMask), ne);
|
| + __ b(eq, &non_ascii);
|
| +
|
| + // Allocate an ASCII cons string.
|
| + __ AllocateAsciiConsString(r7, r6, r4, r5, &string_add_runtime);
|
| + __ bind(&allocated);
|
| + // Fill the fields of the cons string.
|
| + __ str(r0, FieldMemOperand(r7, ConsString::kFirstOffset));
|
| + __ str(r1, FieldMemOperand(r7, ConsString::kSecondOffset));
|
| + __ mov(r0, Operand(r7));
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&non_ascii);
|
| + // Allocate a two byte cons string.
|
| + __ AllocateTwoByteConsString(r7, r6, r4, r5, &string_add_runtime);
|
| + __ jmp(&allocated);
|
| +
|
| + // Handle creating a flat result. First check that both strings are
|
| + // sequential and that they have the same encoding.
|
| + // r0: first string
|
| + // r1: second string
|
| + // r2: length of first string
|
| + // r3: length of second string
|
| + // r4: first string instance type (if string_check_)
|
| + // r5: second string instance type (if string_check_)
|
| + // r6: sum of lengths.
|
| + __ bind(&string_add_flat_result);
|
| + if (!string_check_) {
|
| + __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
|
| + __ ldr(r5, FieldMemOperand(r1, HeapObject::kMapOffset));
|
| + __ ldrb(r4, FieldMemOperand(r4, Map::kInstanceTypeOffset));
|
| + __ ldrb(r5, FieldMemOperand(r5, Map::kInstanceTypeOffset));
|
| + }
|
| + // Check that both strings are sequential.
|
| + ASSERT_EQ(0, kSeqStringTag);
|
| + __ tst(r4, Operand(kStringRepresentationMask));
|
| + __ tst(r5, Operand(kStringRepresentationMask), eq);
|
| + __ b(ne, &string_add_runtime);
|
| + // Now check if both strings have the same encoding (ASCII/Two-byte).
|
| + // r0: first string.
|
| + // r1: second string.
|
| + // r2: length of first string.
|
| + // r3: length of second string.
|
| + // r6: sum of lengths..
|
| + Label non_ascii_string_add_flat_result;
|
| + ASSERT(IsPowerOf2(kStringEncodingMask)); // Just one bit to test.
|
| + __ eor(r7, r4, Operand(r5));
|
| + __ tst(r7, Operand(kStringEncodingMask));
|
| + __ b(ne, &string_add_runtime);
|
| + // And see if it's ASCII or two-byte.
|
| + __ tst(r4, Operand(kStringEncodingMask));
|
| + __ b(eq, &non_ascii_string_add_flat_result);
|
| +
|
| + // Both strings are sequential ASCII strings. We also know that they are
|
| + // short (since the sum of the lengths is less than kMinNonFlatLength).
|
| + __ AllocateAsciiString(r7, r6, r4, r5, r9, &string_add_runtime);
|
| + // Locate first character of result.
|
| + __ add(r6, r7, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // Locate first character of first argument.
|
| + __ add(r0, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // r0: first character of first string.
|
| + // r1: second string.
|
| + // r2: length of first string.
|
| + // r3: length of second string.
|
| + // r6: first character of result.
|
| + // r7: result string.
|
| + GenerateCopyCharacters(masm, r6, r0, r2, r4, true);
|
| +
|
| + // Load second argument and locate first character.
|
| + __ add(r1, r1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag));
|
| + // r1: first character of second string.
|
| + // r3: length of second string.
|
| + // r6: next character of result.
|
| + // r7: result string.
|
| + GenerateCopyCharacters(masm, r6, r1, r3, r4, true);
|
| + __ mov(r0, Operand(r7));
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + __ bind(&non_ascii_string_add_flat_result);
|
| + // Both strings are sequential two byte strings.
|
| + // r0: first string.
|
| + // r1: second string.
|
| + // r2: length of first string.
|
| + // r3: length of second string.
|
| + // r6: sum of length of strings.
|
| + __ AllocateTwoByteString(r7, r6, r4, r5, r9, &string_add_runtime);
|
| + // r0: first string.
|
| + // r1: second string.
|
| + // r2: length of first string.
|
| + // r3: length of second string.
|
| + // r7: result string.
|
| +
|
| + // Locate first character of result.
|
| + __ add(r6, r7, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| + // Locate first character of first argument.
|
| + __ add(r0, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| +
|
| + // r0: first character of first string.
|
| + // r1: second string.
|
| + // r2: length of first string.
|
| + // r3: length of second string.
|
| + // r6: first character of result.
|
| + // r7: result string.
|
| + GenerateCopyCharacters(masm, r6, r0, r2, r4, false);
|
| +
|
| + // Locate first character of second argument.
|
| + __ add(r1, r1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag));
|
| +
|
| + // r1: first character of second string.
|
| + // r3: length of second string.
|
| + // r6: next character of result (after copy of first string).
|
| + // r7: result string.
|
| + GenerateCopyCharacters(masm, r6, r1, r3, r4, false);
|
| +
|
| + __ mov(r0, Operand(r7));
|
| + __ IncrementCounter(&Counters::string_add_native, 1, r2, r3);
|
| + __ add(sp, sp, Operand(2 * kPointerSize));
|
| + __ Ret();
|
| +
|
| + // Just jump to runtime to add the two strings.
|
| + __ bind(&string_add_runtime);
|
| + __ TailCallRuntime(ExternalReference(Runtime::kStringAdd), 2, 1);
|
| +}
|
| +
|
| +
|
| #undef __
|
|
|
| } } // namespace v8::internal
|
|
|