| Index: src/ia32/codegen-ia32.cc
|
| ===================================================================
|
| --- src/ia32/codegen-ia32.cc (revision 2118)
|
| +++ src/ia32/codegen-ia32.cc (working copy)
|
| @@ -4625,48 +4625,82 @@
|
| // cons. The slow case will flatten the string, which will ensure that
|
| // the answer is in the left hand side the next time around.
|
| void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
|
| + Comment(masm_, "[ GenerateFastCharCodeAt");
|
| ASSERT(args->length() == 2);
|
|
|
| - JumpTarget slow_case;
|
| - JumpTarget end;
|
| - JumpTarget not_a_flat_string;
|
| - JumpTarget a_cons_string;
|
| - JumpTarget try_again_with_new_string(JumpTarget::BIDIRECTIONAL);
|
| - JumpTarget ascii_string;
|
| - JumpTarget got_char_code;
|
| + Label slow_case;
|
| + Label end;
|
| + Label not_a_flat_string;
|
| + Label a_cons_string;
|
| + Label try_again_with_new_string;
|
| + Label ascii_string;
|
| + Label got_char_code;
|
|
|
| Load(args->at(0));
|
| Load(args->at(1));
|
| - // Reserve register ecx, to use as shift amount later
|
| - Result shift_amount = allocator()->Allocate(ecx);
|
| - ASSERT(shift_amount.is_valid());
|
| Result index = frame_->Pop();
|
| - index.ToRegister();
|
| Result object = frame_->Pop();
|
| +
|
| + // Get register ecx to use as shift amount later.
|
| + Result shift_amount;
|
| + if (object.is_register() && object.reg().is(ecx)) {
|
| + Result fresh = allocator_->Allocate();
|
| + shift_amount = object;
|
| + object = fresh;
|
| + __ mov(object.reg(), ecx);
|
| + }
|
| + if (index.is_register() && index.reg().is(ecx)) {
|
| + Result fresh = allocator_->Allocate();
|
| + shift_amount = index;
|
| + index = fresh;
|
| + __ mov(index.reg(), ecx);
|
| + }
|
| + // There could be references to ecx in the frame. Allocating will
|
| + // spill them, otherwise spill explicitly.
|
| + if (shift_amount.is_valid()) {
|
| + frame_->Spill(ecx);
|
| + } else {
|
| + shift_amount = allocator()->Allocate(ecx);
|
| + }
|
| + ASSERT(shift_amount.is_register());
|
| + ASSERT(shift_amount.reg().is(ecx));
|
| + ASSERT(allocator_->count(ecx) == 1);
|
| +
|
| + // We will mutate the index register and possibly the object register.
|
| + // The case where they are somehow the same register is handled
|
| + // because we only mutate them in the case where the receiver is a
|
| + // heap object and the index is not.
|
| object.ToRegister();
|
| - // If the receiver is a smi return undefined.
|
| + index.ToRegister();
|
| + frame_->Spill(object.reg());
|
| + frame_->Spill(index.reg());
|
| +
|
| + // We need a single extra temporary register.
|
| + Result temp = allocator()->Allocate();
|
| + ASSERT(temp.is_valid());
|
| +
|
| + // There is no virtual frame effect from here up to the final result
|
| + // push.
|
| +
|
| + // If the receiver is a smi trigger the slow case.
|
| ASSERT(kSmiTag == 0);
|
| __ test(object.reg(), Immediate(kSmiTagMask));
|
| - slow_case.Branch(zero, not_taken);
|
| + __ j(zero, &slow_case);
|
|
|
| - // Check for negative or non-smi index.
|
| + // If the index is negative or non-smi trigger the slow case.
|
| ASSERT(kSmiTag == 0);
|
| __ test(index.reg(), Immediate(kSmiTagMask | 0x80000000));
|
| - slow_case.Branch(not_zero, not_taken);
|
| - // Get rid of the smi tag on the index.
|
| - frame_->Spill(index.reg());
|
| + __ j(not_zero, &slow_case);
|
| + // Untag the index.
|
| __ sar(index.reg(), kSmiTagSize);
|
|
|
| - try_again_with_new_string.Bind(&object, &index, &shift_amount);
|
| - // Get the type of the heap object.
|
| - Result object_type = allocator()->Allocate();
|
| - ASSERT(object_type.is_valid());
|
| - __ mov(object_type.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
|
| - __ movzx_b(object_type.reg(),
|
| - FieldOperand(object_type.reg(), Map::kInstanceTypeOffset));
|
| - // We don't handle non-strings.
|
| - __ test(object_type.reg(), Immediate(kIsNotStringMask));
|
| - slow_case.Branch(not_zero, not_taken);
|
| + __ bind(&try_again_with_new_string);
|
| + // Fetch the instance type of the receiver into ecx.
|
| + __ mov(ecx, FieldOperand(object.reg(), HeapObject::kMapOffset));
|
| + __ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
| + // If the receiver is not a string trigger the slow case.
|
| + __ test(ecx, Immediate(kIsNotStringMask));
|
| + __ j(not_zero, &slow_case);
|
|
|
| // Here we make assumptions about the tag values and the shifts needed.
|
| // See the comment in objects.h.
|
| @@ -4675,86 +4709,75 @@
|
| String::kMediumLengthShift);
|
| ASSERT(kShortStringTag + String::kLongLengthShift ==
|
| String::kShortLengthShift);
|
| - __ mov(shift_amount.reg(), Operand(object_type.reg()));
|
| - __ and_(shift_amount.reg(), kStringSizeMask);
|
| - __ add(Operand(shift_amount.reg()), Immediate(String::kLongLengthShift));
|
| - // Get the length field. Temporary register now used for length.
|
| - Result length = object_type;
|
| - __ mov(length.reg(), FieldOperand(object.reg(), String::kLengthOffset));
|
| - __ shr(length.reg()); // shift_amount, in ecx, is implicit operand.
|
| + __ and_(ecx, kStringSizeMask);
|
| + __ add(Operand(ecx), Immediate(String::kLongLengthShift));
|
| + // Fetch the length field into the temporary register.
|
| + __ mov(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset));
|
| + __ shr(temp.reg()); // The shift amount in ecx is implicit operand.
|
| // Check for index out of range.
|
| - __ cmp(index.reg(), Operand(length.reg()));
|
| - slow_case.Branch(greater_equal, not_taken);
|
| - length.Unuse();
|
| - // Load the object type into object_type again.
|
| - // These two instructions are duplicated from above, to save a register.
|
| - __ mov(object_type.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
|
| - __ movzx_b(object_type.reg(),
|
| - FieldOperand(object_type.reg(), Map::kInstanceTypeOffset));
|
| + __ cmp(index.reg(), Operand(temp.reg()));
|
| + __ j(greater_equal, &slow_case);
|
| + // Reload the instance type (into the temp register this time)..
|
| + __ mov(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
|
| + __ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
|
|
|
| // We need special handling for non-flat strings.
|
| ASSERT(kSeqStringTag == 0);
|
| - __ test(object_type.reg(), Immediate(kStringRepresentationMask));
|
| - not_a_flat_string.Branch(not_zero, &object, &index, &object_type,
|
| - &shift_amount, not_taken);
|
| - shift_amount.Unuse();
|
| + __ test(temp.reg(), Immediate(kStringRepresentationMask));
|
| + __ j(not_zero, ¬_a_flat_string);
|
| // Check for 1-byte or 2-byte string.
|
| - __ test(object_type.reg(), Immediate(kStringEncodingMask));
|
| - ascii_string.Branch(not_zero, &object, &index, &object_type, taken);
|
| + __ test(temp.reg(), Immediate(kStringEncodingMask));
|
| + __ j(not_zero, &ascii_string);
|
|
|
| // 2-byte string.
|
| - // Load the 2-byte character code.
|
| - __ movzx_w(object_type.reg(), FieldOperand(object.reg(),
|
| - index.reg(),
|
| - times_2,
|
| - SeqTwoByteString::kHeaderSize));
|
| - object.Unuse();
|
| - index.Unuse();
|
| - got_char_code.Jump(&object_type);
|
| + // Load the 2-byte character code into the temp register.
|
| + __ movzx_w(temp.reg(), FieldOperand(object.reg(),
|
| + index.reg(),
|
| + times_2,
|
| + SeqTwoByteString::kHeaderSize));
|
| + __ jmp(&got_char_code);
|
|
|
| // ASCII string.
|
| - ascii_string.Bind(&object, &index, &object_type);
|
| - // Load the byte.
|
| - __ movzx_b(object_type.reg(), FieldOperand(object.reg(),
|
| - index.reg(),
|
| - times_1,
|
| - SeqAsciiString::kHeaderSize));
|
| - object.Unuse();
|
| - index.Unuse();
|
| - got_char_code.Bind(&object_type);
|
| + __ bind(&ascii_string);
|
| + // Load the byte into the temp register.
|
| + __ movzx_b(temp.reg(), FieldOperand(object.reg(),
|
| + index.reg(),
|
| + times_1,
|
| + SeqAsciiString::kHeaderSize));
|
| + __ bind(&got_char_code);
|
| ASSERT(kSmiTag == 0);
|
| - __ shl(object_type.reg(), kSmiTagSize);
|
| - frame_->Push(&object_type);
|
| - end.Jump();
|
| + __ shl(temp.reg(), kSmiTagSize);
|
| + __ jmp(&end);
|
|
|
| // Handle non-flat strings.
|
| - not_a_flat_string.Bind(&object, &index, &object_type, &shift_amount);
|
| - __ and_(object_type.reg(), kStringRepresentationMask);
|
| - __ cmp(object_type.reg(), kConsStringTag);
|
| - a_cons_string.Branch(equal, &object, &index, &shift_amount, taken);
|
| - __ cmp(object_type.reg(), kSlicedStringTag);
|
| - slow_case.Branch(not_equal, not_taken);
|
| - object_type.Unuse();
|
| + __ bind(¬_a_flat_string);
|
| + __ and_(temp.reg(), kStringRepresentationMask);
|
| + __ cmp(temp.reg(), kConsStringTag);
|
| + __ j(equal, &a_cons_string);
|
| + __ cmp(temp.reg(), kSlicedStringTag);
|
| + __ j(not_equal, &slow_case);
|
|
|
| // SlicedString.
|
| - // Add the offset to the index.
|
| + // Add the offset to the index and trigger the slow case on overflow.
|
| __ add(index.reg(), FieldOperand(object.reg(), SlicedString::kStartOffset));
|
| - slow_case.Branch(overflow);
|
| + __ j(overflow, &slow_case);
|
| // Getting the underlying string is done by running the cons string code.
|
|
|
| // ConsString.
|
| - a_cons_string.Bind(&object, &index, &shift_amount);
|
| - // Get the first of the two strings.
|
| - frame_->Spill(object.reg());
|
| - // Both sliced and cons strings store their source string at the same place.
|
| + __ bind(&a_cons_string);
|
| + // Get the first of the two strings. Both sliced and cons strings
|
| + // store their source string at the same offset.
|
| ASSERT(SlicedString::kBufferOffset == ConsString::kFirstOffset);
|
| __ mov(object.reg(), FieldOperand(object.reg(), ConsString::kFirstOffset));
|
| - try_again_with_new_string.Jump(&object, &index, &shift_amount);
|
| + __ jmp(&try_again_with_new_string);
|
|
|
| - // No results live at this point.
|
| - slow_case.Bind();
|
| - frame_->Push(Factory::undefined_value());
|
| - end.Bind();
|
| + __ bind(&slow_case);
|
| + // Move the undefined value into the result register, which will
|
| + // trigger the slow case.
|
| + __ Set(temp.reg(), Immediate(Factory::undefined_value()));
|
| +
|
| + __ bind(&end);
|
| + frame_->Push(&temp);
|
| }
|
|
|
|
|
|
|