Index: src/x64/codegen-x64.cc |
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc |
index ebadcd74f2adc1b0c25bc52f8b4c5b673ccafcfb..679ecbe76756e530c305cc3c6e2937204e7f480d 100644 |
--- a/src/x64/codegen-x64.cc |
+++ b/src/x64/codegen-x64.cc |
@@ -450,6 +450,17 @@ void CodeGenerator::VisitAndSpill(Statement* statement) { |
} |
+void CodeGenerator::VisitStatementsAndSpill(ZoneList<Statement*>* statements) { |
+ ASSERT(in_spilled_code()); |
+ set_in_spilled_code(false); |
+ VisitStatements(statements); |
+ 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++) { |
@@ -655,12 +666,19 @@ void CodeGenerator::VisitIfStatement(IfStatement* node) { |
} |
-void CodeGenerator::VisitContinueStatement(ContinueStatement* a) { |
- UNIMPLEMENTED(); |
+void CodeGenerator::VisitContinueStatement(ContinueStatement* node) { |
+ ASSERT(!in_spilled_code()); |
+ Comment cmnt(masm_, "[ ContinueStatement"); |
+ CodeForStatementPosition(node); |
+ node->target()->continue_target()->Jump(); |
} |
-void CodeGenerator::VisitBreakStatement(BreakStatement* a) { |
- UNIMPLEMENTED(); |
+ |
+void CodeGenerator::VisitBreakStatement(BreakStatement* node) { |
+ ASSERT(!in_spilled_code()); |
+ Comment cmnt(masm_, "[ BreakStatement"); |
+ CodeForStatementPosition(node); |
+ node->target()->break_target()->Jump(); |
} |
@@ -1369,16 +1387,353 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) { |
node->break_target()->Unuse(); |
} |
-void CodeGenerator::VisitTryCatch(TryCatch* a) { |
- UNIMPLEMENTED(); |
+void CodeGenerator::VisitTryCatch(TryCatch* node) { |
+ ASSERT(!in_spilled_code()); |
+ VirtualFrame::SpilledScope spilled_scope; |
+ Comment cmnt(masm_, "[ TryCatch"); |
+ CodeForStatementPosition(node); |
+ |
+ JumpTarget try_block; |
+ JumpTarget exit; |
+ |
+ try_block.Call(); |
+ // --- Catch block --- |
+ frame_->EmitPush(rax); |
+ |
+ // Store the caught exception in the catch variable. |
+ { Reference ref(this, node->catch_var()); |
+ ASSERT(ref.is_slot()); |
+ // Load the exception to the top of the stack. Here we make use of the |
+ // convenient property that it doesn't matter whether a value is |
+ // immediately on top of or underneath a zero-sized reference. |
+ ref.SetValue(NOT_CONST_INIT); |
+ } |
+ |
+ // Remove the exception from the stack. |
+ frame_->Drop(); |
+ |
+ VisitStatementsAndSpill(node->catch_block()->statements()); |
+ if (has_valid_frame()) { |
+ exit.Jump(); |
+ } |
+ |
+ |
+ // --- Try block --- |
+ try_block.Bind(); |
+ |
+ frame_->PushTryHandler(TRY_CATCH_HANDLER); |
+ int handler_height = frame_->height(); |
+ |
+ // Shadow the jump targets for all escapes from the try block, including |
+ // returns. During shadowing, the original target is hidden as the |
+ // ShadowTarget and operations on the original actually affect the |
+ // shadowing target. |
+ // |
+ // We should probably try to unify the escaping targets and the return |
+ // target. |
+ int nof_escapes = node->escaping_targets()->length(); |
+ List<ShadowTarget*> shadows(1 + nof_escapes); |
+ |
+ // Add the shadow target for the function return. |
+ static const int kReturnShadowIndex = 0; |
+ shadows.Add(new ShadowTarget(&function_return_)); |
+ bool function_return_was_shadowed = function_return_is_shadowed_; |
+ function_return_is_shadowed_ = true; |
+ ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_); |
+ |
+ // Add the remaining shadow targets. |
+ for (int i = 0; i < nof_escapes; i++) { |
+ shadows.Add(new ShadowTarget(node->escaping_targets()->at(i))); |
+ } |
+ |
+ // Generate code for the statements in the try block. |
+ VisitStatementsAndSpill(node->try_block()->statements()); |
+ |
+ // Stop the introduced shadowing and count the number of required unlinks. |
+ // After shadowing stops, the original targets are unshadowed and the |
+ // ShadowTargets represent the formerly shadowing targets. |
+ bool has_unlinks = false; |
+ for (int i = 0; i < shadows.length(); i++) { |
+ shadows[i]->StopShadowing(); |
+ has_unlinks = has_unlinks || shadows[i]->is_linked(); |
+ } |
+ function_return_is_shadowed_ = function_return_was_shadowed; |
+ |
+ // Get an external reference to the handler address. |
+ ExternalReference handler_address(Top::k_handler_address); |
+ |
+ // Make sure that there's nothing left on the stack above the |
+ // handler structure. |
+ if (FLAG_debug_code) { |
+ __ movq(kScratchRegister, handler_address); |
+ __ cmpq(rsp, Operand(kScratchRegister, 0)); |
+ __ Assert(equal, "stack pointer should point to top handler"); |
+ } |
+ |
+ // If we can fall off the end of the try block, unlink from try chain. |
+ if (has_valid_frame()) { |
+ // The next handler address is on top of the frame. Unlink from |
+ // the handler list and drop the rest of this handler from the |
+ // frame. |
+ ASSERT(StackHandlerConstants::kNextOffset == 0); |
+ __ movq(kScratchRegister, handler_address); |
+ frame_->EmitPop(Operand(kScratchRegister, 0)); |
+ frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); |
+ if (has_unlinks) { |
+ exit.Jump(); |
+ } |
+ } |
+ |
+ // Generate unlink code for the (formerly) shadowing targets that |
+ // have been jumped to. Deallocate each shadow target. |
+ Result return_value; |
+ for (int i = 0; i < shadows.length(); i++) { |
+ if (shadows[i]->is_linked()) { |
+ // Unlink from try chain; be careful not to destroy the TOS if |
+ // there is one. |
+ if (i == kReturnShadowIndex) { |
+ shadows[i]->Bind(&return_value); |
+ return_value.ToRegister(rax); |
+ } else { |
+ shadows[i]->Bind(); |
+ } |
+ // Because we can be jumping here (to spilled code) from |
+ // unspilled code, we need to reestablish a spilled frame at |
+ // this block. |
+ frame_->SpillAll(); |
+ |
+ // Reload sp from the top handler, because some statements that we |
+ // break from (eg, for...in) may have left stuff on the stack. |
+ __ movq(kScratchRegister, handler_address); |
+ __ movq(rsp, Operand(kScratchRegister, 0)); |
+ frame_->Forget(frame_->height() - handler_height); |
+ |
+ ASSERT(StackHandlerConstants::kNextOffset == 0); |
+ __ movq(kScratchRegister, handler_address); |
+ frame_->EmitPop(Operand(kScratchRegister, 0)); |
+ frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); |
+ |
+ if (i == kReturnShadowIndex) { |
+ if (!function_return_is_shadowed_) frame_->PrepareForReturn(); |
+ shadows[i]->other_target()->Jump(&return_value); |
+ } else { |
+ shadows[i]->other_target()->Jump(); |
+ } |
+ } |
+ } |
+ |
+ exit.Bind(); |
} |
-void CodeGenerator::VisitTryFinally(TryFinally* a) { |
- UNIMPLEMENTED(); |
+ |
+void CodeGenerator::VisitTryFinally(TryFinally* node) { |
+ ASSERT(!in_spilled_code()); |
+ VirtualFrame::SpilledScope spilled_scope; |
+ Comment cmnt(masm_, "[ TryFinally"); |
+ CodeForStatementPosition(node); |
+ |
+ // State: Used to keep track of reason for entering the finally |
+ // block. Should probably be extended to hold information for |
+ // break/continue from within the try block. |
+ enum { FALLING, THROWING, JUMPING }; |
+ |
+ JumpTarget try_block; |
+ JumpTarget finally_block; |
+ |
+ try_block.Call(); |
+ |
+ frame_->EmitPush(rax); |
+ // In case of thrown exceptions, this is where we continue. |
+ __ movq(rcx, Immediate(Smi::FromInt(THROWING))); |
+ finally_block.Jump(); |
+ |
+ // --- Try block --- |
+ try_block.Bind(); |
+ |
+ frame_->PushTryHandler(TRY_FINALLY_HANDLER); |
+ int handler_height = frame_->height(); |
+ |
+ // Shadow the jump targets for all escapes from the try block, including |
+ // returns. During shadowing, the original target is hidden as the |
+ // ShadowTarget and operations on the original actually affect the |
+ // shadowing target. |
+ // |
+ // We should probably try to unify the escaping targets and the return |
+ // target. |
+ int nof_escapes = node->escaping_targets()->length(); |
+ List<ShadowTarget*> shadows(1 + nof_escapes); |
+ |
+ // Add the shadow target for the function return. |
+ static const int kReturnShadowIndex = 0; |
+ shadows.Add(new ShadowTarget(&function_return_)); |
+ bool function_return_was_shadowed = function_return_is_shadowed_; |
+ function_return_is_shadowed_ = true; |
+ ASSERT(shadows[kReturnShadowIndex]->other_target() == &function_return_); |
+ |
+ // Add the remaining shadow targets. |
+ for (int i = 0; i < nof_escapes; i++) { |
+ shadows.Add(new ShadowTarget(node->escaping_targets()->at(i))); |
+ } |
+ |
+ // Generate code for the statements in the try block. |
+ VisitStatementsAndSpill(node->try_block()->statements()); |
+ |
+ // Stop the introduced shadowing and count the number of required unlinks. |
+ // After shadowing stops, the original targets are unshadowed and the |
+ // ShadowTargets represent the formerly shadowing targets. |
+ int nof_unlinks = 0; |
+ for (int i = 0; i < shadows.length(); i++) { |
+ shadows[i]->StopShadowing(); |
+ if (shadows[i]->is_linked()) nof_unlinks++; |
+ } |
+ function_return_is_shadowed_ = function_return_was_shadowed; |
+ |
+ // Get an external reference to the handler address. |
+ ExternalReference handler_address(Top::k_handler_address); |
+ |
+ // If we can fall off the end of the try block, unlink from the try |
+ // chain and set the state on the frame to FALLING. |
+ if (has_valid_frame()) { |
+ // The next handler address is on top of the frame. |
+ ASSERT(StackHandlerConstants::kNextOffset == 0); |
+ __ movq(kScratchRegister, handler_address); |
+ frame_->EmitPop(Operand(kScratchRegister, 0)); |
+ frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); |
+ |
+ // Fake a top of stack value (unneeded when FALLING) and set the |
+ // state in ecx, then jump around the unlink blocks if any. |
+ __ movq(kScratchRegister, |
+ Factory::undefined_value(), |
+ RelocInfo::EMBEDDED_OBJECT); |
+ frame_->EmitPush(kScratchRegister); |
+ __ movq(rcx, Immediate(Smi::FromInt(FALLING))); |
+ if (nof_unlinks > 0) { |
+ finally_block.Jump(); |
+ } |
+ } |
+ |
+ // Generate code to unlink and set the state for the (formerly) |
+ // shadowing targets that have been jumped to. |
+ for (int i = 0; i < shadows.length(); i++) { |
+ if (shadows[i]->is_linked()) { |
+ // If we have come from the shadowed return, the return value is |
+ // on the virtual frame. We must preserve it until it is |
+ // pushed. |
+ if (i == kReturnShadowIndex) { |
+ Result return_value; |
+ shadows[i]->Bind(&return_value); |
+ return_value.ToRegister(rax); |
+ } else { |
+ shadows[i]->Bind(); |
+ } |
+ // Because we can be jumping here (to spilled code) from |
+ // unspilled code, we need to reestablish a spilled frame at |
+ // this block. |
+ frame_->SpillAll(); |
+ |
+ // Reload sp from the top handler, because some statements that |
+ // we break from (eg, for...in) may have left stuff on the |
+ // stack. |
+ __ movq(kScratchRegister, handler_address); |
+ __ movq(rsp, Operand(kScratchRegister, 0)); |
+ frame_->Forget(frame_->height() - handler_height); |
+ |
+ // Unlink this handler and drop it from the frame. |
+ ASSERT(StackHandlerConstants::kNextOffset == 0); |
+ __ movq(kScratchRegister, handler_address); |
+ frame_->EmitPop(Operand(kScratchRegister, 0)); |
+ frame_->Drop(StackHandlerConstants::kSize / kPointerSize - 1); |
+ |
+ if (i == kReturnShadowIndex) { |
+ // If this target shadowed the function return, materialize |
+ // the return value on the stack. |
+ frame_->EmitPush(rax); |
+ } else { |
+ // Fake TOS for targets that shadowed breaks and continues. |
+ __ movq(kScratchRegister, |
+ Factory::undefined_value(), |
+ RelocInfo::EMBEDDED_OBJECT); |
+ frame_->EmitPush(kScratchRegister); |
+ } |
+ __ movq(rcx, Immediate(Smi::FromInt(JUMPING + i))); |
+ if (--nof_unlinks > 0) { |
+ // If this is not the last unlink block, jump around the next. |
+ finally_block.Jump(); |
+ } |
+ } |
+ } |
+ |
+ // --- Finally block --- |
+ finally_block.Bind(); |
+ |
+ // Push the state on the stack. |
+ frame_->EmitPush(rcx); |
+ |
+ // We keep two elements on the stack - the (possibly faked) result |
+ // and the state - while evaluating the finally block. |
+ // |
+ // Generate code for the statements in the finally block. |
+ VisitStatementsAndSpill(node->finally_block()->statements()); |
+ |
+ if (has_valid_frame()) { |
+ // Restore state and return value or faked TOS. |
+ frame_->EmitPop(rcx); |
+ frame_->EmitPop(rax); |
+ } |
+ |
+ // Generate code to jump to the right destination for all used |
+ // formerly shadowing targets. Deallocate each shadow target. |
+ for (int i = 0; i < shadows.length(); i++) { |
+ if (has_valid_frame() && shadows[i]->is_bound()) { |
+ BreakTarget* original = shadows[i]->other_target(); |
+ __ cmpq(rcx, Immediate(Smi::FromInt(JUMPING + i))); |
+ if (i == kReturnShadowIndex) { |
+ // The return value is (already) in rax. |
+ Result return_value = allocator_->Allocate(rax); |
+ ASSERT(return_value.is_valid()); |
+ if (function_return_is_shadowed_) { |
+ original->Branch(equal, &return_value); |
+ } else { |
+ // Branch around the preparation for return which may emit |
+ // code. |
+ JumpTarget skip; |
+ skip.Branch(not_equal); |
+ frame_->PrepareForReturn(); |
+ original->Jump(&return_value); |
+ skip.Bind(); |
+ } |
+ } else { |
+ original->Branch(equal); |
+ } |
+ } |
+ } |
+ |
+ if (has_valid_frame()) { |
+ // Check if we need to rethrow the exception. |
+ JumpTarget exit; |
+ __ cmpq(rcx, Immediate(Smi::FromInt(THROWING))); |
+ exit.Branch(not_equal); |
+ |
+ // Rethrow exception. |
+ frame_->EmitPush(rax); // undo pop from above |
+ frame_->CallRuntime(Runtime::kReThrow, 1); |
+ |
+ // Done. |
+ exit.Bind(); |
+ } |
} |
-void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* a) { |
- UNIMPLEMENTED(); |
+ |
+void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) { |
+ ASSERT(!in_spilled_code()); |
+ Comment cmnt(masm_, "[ DebuggerStatement"); |
+ CodeForStatementPosition(node); |
+#ifdef ENABLE_DEBUGGER_SUPPORT |
+ // Spill everything, even constants, to the frame. |
+ frame_->SpillAll(); |
+ frame_->CallRuntime(Runtime::kDebugBreak, 0); |
+ // Ignore the return value. |
+#endif |
} |
@@ -1802,8 +2157,16 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) { |
} |
-void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* a) { |
- UNIMPLEMENTED(); |
+void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) { |
+ ASSERT(!in_spilled_code()); |
+ // Call runtime routine to allocate the catch extension object and |
+ // assign the exception value to the catch variable. |
+ Comment cmnt(masm_, "[ CatchExtensionObject"); |
+ Load(node->key()); |
+ Load(node->value()); |
+ Result result = |
+ frame_->CallRuntime(Runtime::kCreateCatchExtensionObject, 2); |
+ frame_->Push(&result); |
} |
@@ -2794,8 +3157,8 @@ void CodeGenerator::VisitThisFunction(ThisFunction* node) { |
void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) { |
ASSERT(args->length() == 1); |
- // ArgumentsAccessStub expects the key in edx and the formal |
- // parameter count in eax. |
+ // ArgumentsAccessStub expects the key in rdx and the formal |
+ // parameter count in rax. |
Load(args->at(0)); |
Result key = frame_->Pop(); |
// Explicitly create a constant result. |