Index: src/x64/codegen-x64.cc |
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc |
index 77c91a78dbe77af78010700961a3e03e81bfeb8f..9c4268db1e1c20bc3ad53bbcea1a715379fd2280 100644 |
--- a/src/x64/codegen-x64.cc |
+++ b/src/x64/codegen-x64.cc |
@@ -438,6 +438,18 @@ void CodeGenerator::CheckStack() { |
} |
+void CodeGenerator::VisitAndSpill(Statement* statement) { |
+ // TODO(X64): No architecture specific code. Move to shared location. |
+ ASSERT(in_spilled_code()); |
+ set_in_spilled_code(false); |
+ Visit(statement); |
+ if (frame_ != NULL) { |
+ frame_->SpillAll(); |
+ } |
+ set_in_spilled_code(true); |
+} |
+ |
+ |
void CodeGenerator::VisitStatements(ZoneList<Statement*>* statements) { |
ASSERT(!in_spilled_code()); |
for (int i = 0; has_valid_frame() && i < statements->length(); i++) { |
@@ -1021,8 +1033,195 @@ void CodeGenerator::VisitLoopStatement(LoopStatement* node) { |
} |
-void CodeGenerator::VisitForInStatement(ForInStatement* a) { |
- UNIMPLEMENTED(); |
+void CodeGenerator::VisitForInStatement(ForInStatement* node) { |
+ ASSERT(!in_spilled_code()); |
+ VirtualFrame::SpilledScope spilled_scope; |
+ Comment cmnt(masm_, "[ ForInStatement"); |
+ CodeForStatementPosition(node); |
+ |
+ JumpTarget primitive; |
+ JumpTarget jsobject; |
+ JumpTarget fixed_array; |
+ JumpTarget entry(JumpTarget::BIDIRECTIONAL); |
+ JumpTarget end_del_check; |
+ JumpTarget exit; |
+ |
+ // Get the object to enumerate over (converted to JSObject). |
+ LoadAndSpill(node->enumerable()); |
+ |
+ // Both SpiderMonkey and kjs ignore null and undefined in contrast |
+ // to the specification. 12.6.4 mandates a call to ToObject. |
+ frame_->EmitPop(rax); |
+ |
+ // rax: value to be iterated over |
+ __ Cmp(rax, Factory::undefined_value()); |
+ exit.Branch(equal); |
+ __ Cmp(rax, Factory::null_value()); |
+ exit.Branch(equal); |
+ |
+ // Stack layout in body: |
+ // [iteration counter (smi)] <- slot 0 |
+ // [length of array] <- slot 1 |
+ // [FixedArray] <- slot 2 |
+ // [Map or 0] <- slot 3 |
+ // [Object] <- slot 4 |
+ |
+ // Check if enumerable is already a JSObject |
+ // rax: value to be iterated over |
+ __ testl(rax, Immediate(kSmiTagMask)); |
+ primitive.Branch(zero); |
+ __ movq(rcx, FieldOperand(rax, HeapObject::kMapOffset)); |
+ __ movzxbq(rcx, FieldOperand(rcx, Map::kInstanceTypeOffset)); |
+ __ cmpq(rcx, Immediate(FIRST_JS_OBJECT_TYPE)); |
William Hesse
2009/06/25 22:39:10
Why not CmpObjectType macro? Or why not movb and
|
+ jsobject.Branch(above_equal); |
+ |
+ primitive.Bind(); |
+ frame_->EmitPush(rax); |
+ frame_->InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION, 1); |
+ // function call returns the value in rax, which is where we want it below |
+ |
+ jsobject.Bind(); |
+ // Get the set of properties (as a FixedArray or Map). |
+ // rax: value to be iterated over |
+ frame_->EmitPush(rax); // push the object being iterated over (slot 4) |
+ |
+ frame_->EmitPush(rax); // push the Object (slot 4) for the runtime call |
+ frame_->CallRuntime(Runtime::kGetPropertyNamesFast, 1); |
+ |
+ // If we got a Map, we can do a fast modification check. |
+ // Otherwise, we got a FixedArray, and we have to do a slow check. |
+ // rax: map or fixed array (result from call to |
+ // Runtime::kGetPropertyNamesFast) |
+ __ movq(rdx, rax); |
+ __ movq(rcx, FieldOperand(rdx, HeapObject::kMapOffset)); |
+ __ Cmp(rcx, Factory::meta_map()); |
+ fixed_array.Branch(not_equal); |
+ |
+ // Get enum cache |
+ // rax: map (result from call to Runtime::kGetPropertyNamesFast) |
+ __ movq(rcx, rax); |
+ __ movq(rcx, FieldOperand(rcx, Map::kInstanceDescriptorsOffset)); |
+ // Get the bridge array held in the enumeration index field. |
+ __ movq(rcx, FieldOperand(rcx, DescriptorArray::kEnumerationIndexOffset)); |
+ // Get the cache from the bridge array. |
+ __ movq(rdx, FieldOperand(rcx, DescriptorArray::kEnumCacheBridgeCacheOffset)); |
+ |
+ frame_->EmitPush(rax); // <- slot 3 |
+ frame_->EmitPush(rdx); // <- slot 2 |
+ __ movq(rax, FieldOperand(rdx, FixedArray::kLengthOffset)); |
+ __ shl(rax, Immediate(kSmiTagSize)); |
+ frame_->EmitPush(rax); // <- slot 1 |
+ frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0 |
+ entry.Jump(); |
+ |
+ fixed_array.Bind(); |
+ // rax: fixed array (result from call to Runtime::kGetPropertyNamesFast) |
+ frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 3 |
+ frame_->EmitPush(rax); // <- slot 2 |
+ |
+ // Push the length of the array and the initial index onto the stack. |
+ __ movq(rax, FieldOperand(rax, FixedArray::kLengthOffset)); |
+ __ shl(rax, Immediate(kSmiTagSize)); |
+ frame_->EmitPush(rax); // <- slot 1 |
+ frame_->EmitPush(Immediate(Smi::FromInt(0))); // <- slot 0 |
+ |
+ // Condition. |
+ entry.Bind(); |
+ // Grab the current frame's height for the break and continue |
+ // targets only after all the state is pushed on the frame. |
+ node->break_target()->set_direction(JumpTarget::FORWARD_ONLY); |
+ node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY); |
+ |
+ __ movq(rax, frame_->ElementAt(0)); // load the current count |
+ __ cmpq(rax, frame_->ElementAt(1)); // compare to the array length |
+ node->break_target()->Branch(above_equal); |
+ |
+ // Get the i'th entry of the array. |
+ __ movq(rdx, frame_->ElementAt(2)); |
+ ASSERT(kSmiTagSize == 1 && kSmiTag == 0); |
+ // Multiplier is times_4 since rax is already a Smi. |
+ __ movq(rbx, Operand(rdx, rax, times_4, |
+ FixedArray::kHeaderSize - kHeapObjectTag)); |
+ |
+ // Get the expected map from the stack or a zero map in the |
+ // permanent slow case rax: current iteration count rbx: i'th entry |
+ // of the enum cache |
+ __ movq(rdx, frame_->ElementAt(3)); |
+ // Check if the expected map still matches that of the enumerable. |
+ // If not, we have to filter the key. |
+ // rax: current iteration count |
+ // rbx: i'th entry of the enum cache |
+ // rdx: expected map value |
+ __ movq(rcx, frame_->ElementAt(4)); |
+ __ movq(rcx, FieldOperand(rcx, HeapObject::kMapOffset)); |
+ __ cmpq(rcx, rdx); |
+ end_del_check.Branch(equal); |
+ |
+ // Convert the entry to a string (or null if it isn't a property anymore). |
+ frame_->EmitPush(frame_->ElementAt(4)); // push enumerable |
+ frame_->EmitPush(rbx); // push entry |
+ frame_->InvokeBuiltin(Builtins::FILTER_KEY, CALL_FUNCTION, 2); |
+ __ movq(rbx, rax); |
+ |
+ // If the property has been removed while iterating, we just skip it. |
+ __ Cmp(rbx, Factory::null_value()); |
+ node->continue_target()->Branch(equal); |
+ |
+ end_del_check.Bind(); |
+ // Store the entry in the 'each' expression and take another spin in the |
+ // loop. rdx: i'th entry of the enum cache (or string there of) |
+ frame_->EmitPush(rbx); |
+ { Reference each(this, node->each()); |
+ // Loading a reference may leave the frame in an unspilled state. |
+ frame_->SpillAll(); |
+ if (!each.is_illegal()) { |
+ if (each.size() > 0) { |
+ frame_->EmitPush(frame_->ElementAt(each.size())); |
+ } |
+ // If the reference was to a slot we rely on the convenient property |
+ // that it doesn't matter whether a value (eg, ebx pushed above) is |
+ // right on top of or right underneath a zero-sized reference. |
+ each.SetValue(NOT_CONST_INIT); |
+ if (each.size() > 0) { |
+ // It's safe to pop the value lying on top of the reference before |
+ // unloading the reference itself (which preserves the top of stack, |
+ // ie, now the topmost value of the non-zero sized reference), since |
+ // we will discard the top of stack after unloading the reference |
+ // anyway. |
+ frame_->Drop(); |
+ } |
+ } |
+ } |
+ // Unloading a reference may leave the frame in an unspilled state. |
+ frame_->SpillAll(); |
+ |
+ // Discard the i'th entry pushed above or else the remainder of the |
+ // reference, whichever is currently on top of the stack. |
+ frame_->Drop(); |
+ |
+ // Body. |
+ CheckStack(); // TODO(1222600): ignore if body contains calls. |
+ VisitAndSpill(node->body()); |
+ |
+ // Next. Reestablish a spilled frame in case we are coming here via |
+ // a continue in the body. |
+ node->continue_target()->Bind(); |
William Hesse
2009/06/25 22:39:10
// TODO(make an issue?) Consider making a SpilledT
|
+ frame_->SpillAll(); |
+ frame_->EmitPop(rax); |
+ __ addq(rax, Immediate(Smi::FromInt(1))); |
+ frame_->EmitPush(rax); |
+ entry.Jump(); |
+ |
+ // Cleanup. No need to spill because VirtualFrame::Drop is safe for |
+ // any frame. |
+ node->break_target()->Bind(); |
+ frame_->Drop(5); |
+ |
+ // Exit. |
+ exit.Bind(); |
+ |
+ node->continue_target()->Unuse(); |
+ node->break_target()->Unuse(); |
} |
void CodeGenerator::VisitTryCatch(TryCatch* a) { |
@@ -2592,6 +2791,17 @@ void CodeGenerator::GenerateValueOf(ZoneList<Expression*>* args) { |
// ----------------------------------------------------------------------------- |
// CodeGenerator implementation of Expressions |
+void CodeGenerator::LoadAndSpill(Expression* expression, |
+ TypeofState typeof_state) { |
+ // TODO(x64): No architecture specific code. Move to shared location. |
+ ASSERT(in_spilled_code()); |
+ set_in_spilled_code(false); |
+ Load(expression, typeof_state); |
+ frame_->SpillAll(); |
+ set_in_spilled_code(true); |
+} |
+ |
+ |
void CodeGenerator::Load(Expression* x, TypeofState typeof_state) { |
#ifdef DEBUG |
int original_height = frame_->height(); |