Change IA32's CodeGenerator::GenerateFastCharCodeAt to eagerly
allocate and spill registers, so that the register reference counts and virtual frame are unchanged in the main body. This eliminates a few sites of magic branching or binding of JumpTarget with arguments. Review URL: http://codereview.chromium.org/119302 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2126 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
dc9670bcb4
commit
4a96feeef3
@ -4625,48 +4625,82 @@ void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
|
|||||||
// cons. The slow case will flatten the string, which will ensure that
|
// cons. The slow case will flatten the string, which will ensure that
|
||||||
// the answer is in the left hand side the next time around.
|
// the answer is in the left hand side the next time around.
|
||||||
void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
|
void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
|
||||||
|
Comment(masm_, "[ GenerateFastCharCodeAt");
|
||||||
ASSERT(args->length() == 2);
|
ASSERT(args->length() == 2);
|
||||||
|
|
||||||
JumpTarget slow_case;
|
Label slow_case;
|
||||||
JumpTarget end;
|
Label end;
|
||||||
JumpTarget not_a_flat_string;
|
Label not_a_flat_string;
|
||||||
JumpTarget a_cons_string;
|
Label a_cons_string;
|
||||||
JumpTarget try_again_with_new_string(JumpTarget::BIDIRECTIONAL);
|
Label try_again_with_new_string;
|
||||||
JumpTarget ascii_string;
|
Label ascii_string;
|
||||||
JumpTarget got_char_code;
|
Label got_char_code;
|
||||||
|
|
||||||
Load(args->at(0));
|
Load(args->at(0));
|
||||||
Load(args->at(1));
|
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();
|
Result index = frame_->Pop();
|
||||||
index.ToRegister();
|
|
||||||
Result object = frame_->Pop();
|
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();
|
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);
|
ASSERT(kSmiTag == 0);
|
||||||
__ test(object.reg(), Immediate(kSmiTagMask));
|
__ 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);
|
ASSERT(kSmiTag == 0);
|
||||||
__ test(index.reg(), Immediate(kSmiTagMask | 0x80000000));
|
__ test(index.reg(), Immediate(kSmiTagMask | 0x80000000));
|
||||||
slow_case.Branch(not_zero, not_taken);
|
__ j(not_zero, &slow_case);
|
||||||
// Get rid of the smi tag on the index.
|
// Untag the index.
|
||||||
frame_->Spill(index.reg());
|
|
||||||
__ sar(index.reg(), kSmiTagSize);
|
__ sar(index.reg(), kSmiTagSize);
|
||||||
|
|
||||||
try_again_with_new_string.Bind(&object, &index, &shift_amount);
|
__ bind(&try_again_with_new_string);
|
||||||
// Get the type of the heap object.
|
// Fetch the instance type of the receiver into ecx.
|
||||||
Result object_type = allocator()->Allocate();
|
__ mov(ecx, FieldOperand(object.reg(), HeapObject::kMapOffset));
|
||||||
ASSERT(object_type.is_valid());
|
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
|
||||||
__ mov(object_type.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
|
// If the receiver is not a string trigger the slow case.
|
||||||
__ movzx_b(object_type.reg(),
|
__ test(ecx, Immediate(kIsNotStringMask));
|
||||||
FieldOperand(object_type.reg(), Map::kInstanceTypeOffset));
|
__ j(not_zero, &slow_case);
|
||||||
// We don't handle non-strings.
|
|
||||||
__ test(object_type.reg(), Immediate(kIsNotStringMask));
|
|
||||||
slow_case.Branch(not_zero, not_taken);
|
|
||||||
|
|
||||||
// Here we make assumptions about the tag values and the shifts needed.
|
// Here we make assumptions about the tag values and the shifts needed.
|
||||||
// See the comment in objects.h.
|
// See the comment in objects.h.
|
||||||
@ -4675,86 +4709,75 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
|
|||||||
String::kMediumLengthShift);
|
String::kMediumLengthShift);
|
||||||
ASSERT(kShortStringTag + String::kLongLengthShift ==
|
ASSERT(kShortStringTag + String::kLongLengthShift ==
|
||||||
String::kShortLengthShift);
|
String::kShortLengthShift);
|
||||||
__ mov(shift_amount.reg(), Operand(object_type.reg()));
|
__ and_(ecx, kStringSizeMask);
|
||||||
__ and_(shift_amount.reg(), kStringSizeMask);
|
__ add(Operand(ecx), Immediate(String::kLongLengthShift));
|
||||||
__ add(Operand(shift_amount.reg()), Immediate(String::kLongLengthShift));
|
// Fetch the length field into the temporary register.
|
||||||
// Get the length field. Temporary register now used for length.
|
__ mov(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset));
|
||||||
Result length = object_type;
|
__ shr(temp.reg()); // The shift amount in ecx is implicit operand.
|
||||||
__ mov(length.reg(), FieldOperand(object.reg(), String::kLengthOffset));
|
|
||||||
__ shr(length.reg()); // shift_amount, in ecx, is implicit operand.
|
|
||||||
// Check for index out of range.
|
// Check for index out of range.
|
||||||
__ cmp(index.reg(), Operand(length.reg()));
|
__ cmp(index.reg(), Operand(temp.reg()));
|
||||||
slow_case.Branch(greater_equal, not_taken);
|
__ j(greater_equal, &slow_case);
|
||||||
length.Unuse();
|
// Reload the instance type (into the temp register this time)..
|
||||||
// Load the object type into object_type again.
|
__ mov(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
|
||||||
// These two instructions are duplicated from above, to save a register.
|
__ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
|
||||||
__ mov(object_type.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset));
|
|
||||||
__ movzx_b(object_type.reg(),
|
|
||||||
FieldOperand(object_type.reg(), Map::kInstanceTypeOffset));
|
|
||||||
|
|
||||||
// We need special handling for non-flat strings.
|
// We need special handling for non-flat strings.
|
||||||
ASSERT(kSeqStringTag == 0);
|
ASSERT(kSeqStringTag == 0);
|
||||||
__ test(object_type.reg(), Immediate(kStringRepresentationMask));
|
__ test(temp.reg(), Immediate(kStringRepresentationMask));
|
||||||
not_a_flat_string.Branch(not_zero, &object, &index, &object_type,
|
__ j(not_zero, ¬_a_flat_string);
|
||||||
&shift_amount, not_taken);
|
|
||||||
shift_amount.Unuse();
|
|
||||||
// Check for 1-byte or 2-byte string.
|
// Check for 1-byte or 2-byte string.
|
||||||
__ test(object_type.reg(), Immediate(kStringEncodingMask));
|
__ test(temp.reg(), Immediate(kStringEncodingMask));
|
||||||
ascii_string.Branch(not_zero, &object, &index, &object_type, taken);
|
__ j(not_zero, &ascii_string);
|
||||||
|
|
||||||
// 2-byte string.
|
// 2-byte string.
|
||||||
// Load the 2-byte character code.
|
// Load the 2-byte character code into the temp register.
|
||||||
__ movzx_w(object_type.reg(), FieldOperand(object.reg(),
|
__ movzx_w(temp.reg(), FieldOperand(object.reg(),
|
||||||
index.reg(),
|
index.reg(),
|
||||||
times_2,
|
times_2,
|
||||||
SeqTwoByteString::kHeaderSize));
|
SeqTwoByteString::kHeaderSize));
|
||||||
object.Unuse();
|
__ jmp(&got_char_code);
|
||||||
index.Unuse();
|
|
||||||
got_char_code.Jump(&object_type);
|
|
||||||
|
|
||||||
// ASCII string.
|
// ASCII string.
|
||||||
ascii_string.Bind(&object, &index, &object_type);
|
__ bind(&ascii_string);
|
||||||
// Load the byte.
|
// Load the byte into the temp register.
|
||||||
__ movzx_b(object_type.reg(), FieldOperand(object.reg(),
|
__ movzx_b(temp.reg(), FieldOperand(object.reg(),
|
||||||
index.reg(),
|
index.reg(),
|
||||||
times_1,
|
times_1,
|
||||||
SeqAsciiString::kHeaderSize));
|
SeqAsciiString::kHeaderSize));
|
||||||
object.Unuse();
|
__ bind(&got_char_code);
|
||||||
index.Unuse();
|
|
||||||
got_char_code.Bind(&object_type);
|
|
||||||
ASSERT(kSmiTag == 0);
|
ASSERT(kSmiTag == 0);
|
||||||
__ shl(object_type.reg(), kSmiTagSize);
|
__ shl(temp.reg(), kSmiTagSize);
|
||||||
frame_->Push(&object_type);
|
__ jmp(&end);
|
||||||
end.Jump();
|
|
||||||
|
|
||||||
// Handle non-flat strings.
|
// Handle non-flat strings.
|
||||||
not_a_flat_string.Bind(&object, &index, &object_type, &shift_amount);
|
__ bind(¬_a_flat_string);
|
||||||
__ and_(object_type.reg(), kStringRepresentationMask);
|
__ and_(temp.reg(), kStringRepresentationMask);
|
||||||
__ cmp(object_type.reg(), kConsStringTag);
|
__ cmp(temp.reg(), kConsStringTag);
|
||||||
a_cons_string.Branch(equal, &object, &index, &shift_amount, taken);
|
__ j(equal, &a_cons_string);
|
||||||
__ cmp(object_type.reg(), kSlicedStringTag);
|
__ cmp(temp.reg(), kSlicedStringTag);
|
||||||
slow_case.Branch(not_equal, not_taken);
|
__ j(not_equal, &slow_case);
|
||||||
object_type.Unuse();
|
|
||||||
|
|
||||||
// SlicedString.
|
// 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));
|
__ 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.
|
// Getting the underlying string is done by running the cons string code.
|
||||||
|
|
||||||
// ConsString.
|
// ConsString.
|
||||||
a_cons_string.Bind(&object, &index, &shift_amount);
|
__ bind(&a_cons_string);
|
||||||
// Get the first of the two strings.
|
// Get the first of the two strings. Both sliced and cons strings
|
||||||
frame_->Spill(object.reg());
|
// store their source string at the same offset.
|
||||||
// Both sliced and cons strings store their source string at the same place.
|
|
||||||
ASSERT(SlicedString::kBufferOffset == ConsString::kFirstOffset);
|
ASSERT(SlicedString::kBufferOffset == ConsString::kFirstOffset);
|
||||||
__ mov(object.reg(), FieldOperand(object.reg(), 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.
|
__ bind(&slow_case);
|
||||||
slow_case.Bind();
|
// Move the undefined value into the result register, which will
|
||||||
frame_->Push(Factory::undefined_value());
|
// trigger the slow case.
|
||||||
end.Bind();
|
__ Set(temp.reg(), Immediate(Factory::undefined_value()));
|
||||||
|
|
||||||
|
__ bind(&end);
|
||||||
|
frame_->Push(&temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user