Index: src/x64/codegen-x64.cc |
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc |
index 158dbbaff5f10789eb8ee5b1d7fb821a29f34fa6..b94fab7306bb65c6cbfe1177b02cc5b137db0ed3 100644 |
--- a/src/x64/codegen-x64.cc |
+++ b/src/x64/codegen-x64.cc |
@@ -3423,10 +3423,161 @@ void CodeGenerator::GenerateArgumentsLength(ZoneList<Expression*>* args) { |
} |
-void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* a) { |
- // TODO(X64): Implement this function. |
- // Ignore arguments and return undefined, to signal failure. |
- frame_->Push(Factory::undefined_value()); |
+void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) { |
+ Comment(masm_, "[ GenerateFastCharCodeAt"); |
+ ASSERT(args->length() == 2); |
+ |
+ 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)); |
+ Result index = frame_->Pop(); |
+ Result object = frame_->Pop(); |
+ |
+ // Get register rcx to use as shift amount later. |
+ Result shift_amount; |
+ if (object.is_register() && object.reg().is(rcx)) { |
+ Result fresh = allocator_->Allocate(); |
+ shift_amount = object; |
+ object = fresh; |
+ __ movq(object.reg(), rcx); |
+ } |
+ if (index.is_register() && index.reg().is(rcx)) { |
+ Result fresh = allocator_->Allocate(); |
+ shift_amount = index; |
+ index = fresh; |
+ __ movq(index.reg(), rcx); |
+ } |
+ // There could be references to ecx in the frame. Allocating will |
+ // spill them, otherwise spill explicitly. |
+ if (shift_amount.is_valid()) { |
+ frame_->Spill(rcx); |
+ } else { |
+ shift_amount = allocator()->Allocate(rcx); |
+ } |
+ ASSERT(shift_amount.is_register()); |
+ ASSERT(shift_amount.reg().is(rcx)); |
+ ASSERT(allocator_->count(rcx) == 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(); |
+ 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); |
+ __ testl(object.reg(), Immediate(kSmiTagMask)); |
+ __ j(zero, &slow_case); |
+ |
+ // If the index is negative or non-smi trigger the slow case. |
+ ASSERT(kSmiTag == 0); |
+ __ testl(index.reg(), |
+ Immediate(static_cast<uint32_t>(kSmiTagMask | 0x80000000U))); |
William Hesse
2009/08/07 10:27:15
shouldn't this be a static cast to int32_t. I thi
Lasse Reichstein
2009/08/07 10:44:27
Done.
|
+ __ j(not_zero, &slow_case); |
+ // Untag the index. |
+ __ sarl(index.reg(), Immediate(kSmiTagSize)); |
+ |
+ __ bind(&try_again_with_new_string); |
+ // Fetch the instance type of the receiver into rcx. |
+ __ movq(rcx, FieldOperand(object.reg(), HeapObject::kMapOffset)); |
+ __ movzxbl(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset)); |
+ // If the receiver is not a string trigger the slow case. |
+ __ testb(rcx, 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. |
+ ASSERT(kLongStringTag == 0); |
+ ASSERT(kMediumStringTag + String::kLongLengthShift == |
+ String::kMediumLengthShift); |
+ ASSERT(kShortStringTag + String::kLongLengthShift == |
+ String::kShortLengthShift); |
+ __ and_(rcx, Immediate(kStringSizeMask)); |
+ __ addq(rcx, Immediate(String::kLongLengthShift)); |
+ // Fetch the length field into the temporary register. |
+ __ movl(temp.reg(), FieldOperand(object.reg(), String::kLengthOffset)); |
+ __ shrl(temp.reg()); // The shift amount in ecx is implicit operand. |
William Hesse
2009/08/07 10:27:15
cl (low byte of rcx), not ecx.
Is this a shr or sa
Lasse Reichstein
2009/08/07 10:44:27
Not sure whether high bit can be 1 (would be waste
|
+ // Check for index out of range. |
+ __ cmpl(index.reg(), temp.reg()); |
+ __ j(greater_equal, &slow_case); |
William Hesse
2009/08/07 10:27:15
greater_equal or below_equal? Are either of these
Lasse Reichstein
2009/08/07 10:44:27
The string length is definitly positive, the index
|
+ // Reload the instance type (into the temp register this time).. |
+ __ movq(temp.reg(), FieldOperand(object.reg(), HeapObject::kMapOffset)); |
+ __ movzxbl(temp.reg(), FieldOperand(temp.reg(), Map::kInstanceTypeOffset)); |
+ |
+ // We need special handling for non-flat strings. |
+ ASSERT(kSeqStringTag == 0); |
+ __ testb(temp.reg(), Immediate(kStringRepresentationMask)); |
+ __ j(not_zero, ¬_a_flat_string); |
+ // Check for 1-byte or 2-byte string. |
+ __ testb(temp.reg(), Immediate(kStringEncodingMask)); |
+ __ j(not_zero, &ascii_string); |
+ |
+ // 2-byte string. |
+ // Load the 2-byte character code into the temp register. |
+ __ movzxwl(temp.reg(), FieldOperand(object.reg(), |
William Hesse
2009/08/07 10:27:15
Add the new operations to the disassembler.
Lasse Reichstein
2009/08/07 10:44:27
Already there. It's just a another operand size fo
Lasse Reichstein
2009/08/07 10:44:27
Already there. It's just a another operand size fo
|
+ index.reg(), |
+ times_2, |
+ SeqTwoByteString::kHeaderSize)); |
+ __ jmp(&got_char_code); |
+ |
+ // ASCII string. |
+ __ bind(&ascii_string); |
+ // Load the byte into the temp register. |
+ __ movzxbl(temp.reg(), FieldOperand(object.reg(), |
+ index.reg(), |
+ times_1, |
+ SeqAsciiString::kHeaderSize)); |
+ __ bind(&got_char_code); |
+ ASSERT(kSmiTag == 0); |
+ __ shl(temp.reg(), Immediate(kSmiTagSize)); |
+ __ jmp(&end); |
+ |
+ // Handle non-flat strings. |
+ __ bind(¬_a_flat_string); |
+ __ and_(temp.reg(), Immediate(kStringRepresentationMask)); |
+ __ cmpb(temp.reg(), Immediate(kConsStringTag)); |
+ __ j(equal, &a_cons_string); |
+ __ cmpb(temp.reg(), Immediate(kSlicedStringTag)); |
+ __ j(not_equal, &slow_case); |
+ |
+ // SlicedString. |
+ // Add the offset to the index and trigger the slow case on overflow. |
+ __ addl(index.reg(), FieldOperand(object.reg(), SlicedString::kStartOffset)); |
+ __ j(overflow, &slow_case); |
+ // Getting the underlying string is done by running the cons string code. |
+ |
+ // ConsString. |
+ __ 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); |
+ __ movq(object.reg(), FieldOperand(object.reg(), ConsString::kFirstOffset)); |
+ __ jmp(&try_again_with_new_string); |
+ |
+ __ bind(&slow_case); |
+ // Move the undefined value into the result register, which will |
+ // trigger the slow case. |
+ __ Move(temp.reg(), Factory::undefined_value()); |
+ |
+ __ bind(&end); |
+ frame_->Push(&temp); |
} |
@@ -4152,8 +4303,6 @@ void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot, |
void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) { |
- // TODO(X64): Enable more types of slot. |
- |
if (slot->type() == Slot::LOOKUP) { |
ASSERT(slot->var()->is_dynamic()); |