Version 3.0.0.
Improved performance by (partially) addressing issue 957 on IA-32. Still needs more work for the other architectures.
git-svn-id: http://v8.googlecode.com/svn/trunk@5932 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/full-codegen.cc b/src/full-codegen.cc
index a890f15..4eb10c7 100644
--- a/src/full-codegen.cc
+++ b/src/full-codegen.cc
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2010 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -29,12 +29,13 @@
#include "codegen-inl.h"
#include "compiler.h"
+#include "debug.h"
#include "full-codegen.h"
+#include "liveedit.h"
#include "macro-assembler.h"
+#include "prettyprinter.h"
#include "scopes.h"
#include "stub-cache.h"
-#include "debug.h"
-#include "liveedit.h"
namespace v8 {
namespace internal {
@@ -166,10 +167,6 @@
}
-void BreakableStatementChecker::VisitSlot(Slot* expr) {
-}
-
-
void BreakableStatementChecker::VisitVariableProxy(VariableProxy* expr) {
}
@@ -283,6 +280,9 @@
int len = String::cast(script->source())->length();
Counters::total_full_codegen_source_size.Increment(len);
}
+ if (FLAG_trace_codegen) {
+ PrintF("Full Compiler - ");
+ }
CodeGenerator::MakeCodePrologue(info);
const int kInitialBufferSize = 4 * KB;
MacroAssembler masm(NULL, kInitialBufferSize);
@@ -293,14 +293,105 @@
ASSERT(!Top::has_pending_exception());
return false;
}
+ unsigned table_offset = cgen.EmitStackCheckTable();
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP);
Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
+ code->set_optimizable(info->IsOptimizable());
+ cgen.PopulateDeoptimizationData(code);
+ code->set_has_deoptimization_support(info->HasDeoptimizationSupport());
+ code->set_allow_osr_at_loop_nesting_level(0);
+ code->set_stack_check_table_start(table_offset);
+ CodeGenerator::PrintCode(code, info);
info->SetCode(code); // may be an empty handle.
return !code.is_null();
}
+unsigned FullCodeGenerator::EmitStackCheckTable() {
+ // The stack check table consists of a length (in number of entries)
+ // field, and then a sequence of entries. Each entry is a pair of AST id
+ // and code-relative pc offset.
+ masm()->Align(kIntSize);
+ masm()->RecordComment("[ Stack check table");
+ unsigned offset = masm()->pc_offset();
+ unsigned length = stack_checks_.length();
+ __ dd(length);
+ for (unsigned i = 0; i < length; ++i) {
+ __ dd(stack_checks_[i].id);
+ __ dd(stack_checks_[i].pc_and_state);
+ }
+ masm()->RecordComment("]");
+ return offset;
+}
+
+
+void FullCodeGenerator::PopulateDeoptimizationData(Handle<Code> code) {
+ // Fill in the deoptimization information.
+ ASSERT(info_->HasDeoptimizationSupport() || bailout_entries_.is_empty());
+ if (!info_->HasDeoptimizationSupport()) return;
+ int length = bailout_entries_.length();
+ Handle<DeoptimizationOutputData> data =
+ Factory::NewDeoptimizationOutputData(length, TENURED);
+ for (int i = 0; i < length; i++) {
+ data->SetAstId(i, Smi::FromInt(bailout_entries_[i].id));
+ data->SetPcAndState(i, Smi::FromInt(bailout_entries_[i].pc_and_state));
+ }
+ code->set_deoptimization_data(*data);
+}
+
+
+void FullCodeGenerator::PrepareForBailout(AstNode* node, State state) {
+ PrepareForBailoutForId(node->id(), state);
+}
+
+
+void FullCodeGenerator::RecordJSReturnSite(Call* call) {
+ // We record the offset of the function return so we can rebuild the frame
+ // if the function was inlined, i.e., this is the return address in the
+ // inlined function's frame.
+ //
+ // The state is ignored. We defensively set it to TOS_REG, which is the
+ // real state of the unoptimized code at the return site.
+ PrepareForBailoutForId(call->ReturnId(), TOS_REG);
+#ifdef DEBUG
+ // In debug builds, mark the return so we can verify that this function
+ // was called.
+ ASSERT(!call->return_is_recorded_);
+ call->return_is_recorded_ = true;
+#endif
+}
+
+
+void FullCodeGenerator::PrepareForBailoutForId(int id, State state) {
+ // There's no need to prepare this code for bailouts from already optimized
+ // code or code that can't be optimized.
+ if (!FLAG_deopt || !info_->HasDeoptimizationSupport()) return;
+ unsigned pc_and_state =
+ StateField::encode(state) | PcField::encode(masm_->pc_offset());
+ BailoutEntry entry = { id, pc_and_state };
+#ifdef DEBUG
+ // Assert that we don't have multiple bailout entries for the same node.
+ for (int i = 0; i < bailout_entries_.length(); i++) {
+ if (bailout_entries_.at(i).id == entry.id) {
+ AstPrinter printer;
+ PrintF("%s", printer.PrintProgram(info_->function()));
+ UNREACHABLE();
+ }
+ }
+#endif // DEBUG
+ bailout_entries_.Add(entry);
+}
+
+
+void FullCodeGenerator::RecordStackCheck(int ast_id) {
+ // The pc offset does not need to be encoded and packed together with a
+ // state.
+ BailoutEntry entry = { ast_id, masm_->pc_offset() };
+ stack_checks_.Add(entry);
+}
+
+
int FullCodeGenerator::SlotOffset(Slot* slot) {
ASSERT(slot != NULL);
// Offset is negative because higher indexes are at lower addresses.
@@ -335,13 +426,11 @@
void FullCodeGenerator::AccumulatorValueContext::Plug(Register reg) const {
- // Move value into place.
__ Move(result_register(), reg);
}
void FullCodeGenerator::StackValueContext::Plug(Register reg) const {
- // Move value into place.
__ push(reg);
}
@@ -349,6 +438,7 @@
void FullCodeGenerator::TestContext::Plug(Register reg) const {
// For simplicity we always test the accumulator register.
__ Move(result_register(), reg);
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
codegen()->DoTest(true_label_, false_label_, fall_through_);
}
@@ -370,6 +460,7 @@
void FullCodeGenerator::TestContext::PlugTOS() const {
// For simplicity we always test the accumulator register.
__ pop(result_register());
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
codegen()->DoTest(true_label_, false_label_, fall_through_);
}
@@ -558,10 +649,9 @@
}
-void FullCodeGenerator::SetSourcePosition(
- int pos, PositionRecordingType recording_type) {
+void FullCodeGenerator::SetSourcePosition(int pos) {
if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
- masm_->positions_recorder()->RecordPosition(pos, recording_type);
+ masm_->positions_recorder()->RecordPosition(pos);
}
}
@@ -615,7 +705,8 @@
switch (op) {
case Token::COMMA:
VisitForEffect(left);
- Visit(right);
+ if (context()->IsTest()) ForwardBailoutToChild(expr);
+ context()->HandleExpression(right);
break;
case Token::OR:
@@ -671,7 +762,8 @@
context()->EmitLogicalLeft(expr, &eval_right, &done);
__ bind(&eval_right);
- Visit(expr->right());
+ if (context()->IsTest()) ForwardBailoutToChild(expr);
+ context()->HandleExpression(expr->right());
__ bind(&done);
}
@@ -693,15 +785,17 @@
BinaryOperation* expr,
Label* eval_right,
Label* done) const {
- codegen()->Visit(expr->left());
+ HandleExpression(expr->left());
// We want the value in the accumulator for the test, and on the stack in case
// we need it.
__ push(result_register());
Label discard, restore;
if (expr->op() == Token::OR) {
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
codegen()->DoTest(&restore, &discard, &restore);
} else {
ASSERT(expr->op() == Token::AND);
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
codegen()->DoTest(&discard, &restore, &restore);
}
__ bind(&restore);
@@ -722,9 +816,11 @@
__ push(result_register());
Label discard;
if (expr->op() == Token::OR) {
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
codegen()->DoTest(done, &discard, &discard);
} else {
ASSERT(expr->op() == Token::AND);
+ codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL);
codegen()->DoTest(&discard, done, &discard);
}
__ bind(&discard);
@@ -746,12 +842,66 @@
}
+void FullCodeGenerator::ForwardBailoutToChild(Expression* expr) {
+ if (!info_->HasDeoptimizationSupport()) return;
+ ASSERT(context()->IsTest());
+ ASSERT(expr == forward_bailout_stack_->expr());
+ forward_bailout_pending_ = forward_bailout_stack_;
+}
+
+
+void FullCodeGenerator::EffectContext::HandleExpression(
+ Expression* expr) const {
+ codegen()->HandleInNonTestContext(expr, NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::AccumulatorValueContext::HandleExpression(
+ Expression* expr) const {
+ codegen()->HandleInNonTestContext(expr, TOS_REG);
+}
+
+
+void FullCodeGenerator::StackValueContext::HandleExpression(
+ Expression* expr) const {
+ codegen()->HandleInNonTestContext(expr, NO_REGISTERS);
+}
+
+
+void FullCodeGenerator::TestContext::HandleExpression(Expression* expr) const {
+ codegen()->VisitInTestContext(expr);
+}
+
+
+void FullCodeGenerator::HandleInNonTestContext(Expression* expr, State state) {
+ ASSERT(forward_bailout_pending_ == NULL);
+ AstVisitor::Visit(expr);
+ PrepareForBailout(expr, state);
+ // Forwarding bailouts to children is a one shot operation. It
+ // should have been processed at this point.
+ ASSERT(forward_bailout_pending_ == NULL);
+}
+
+
+void FullCodeGenerator::VisitInTestContext(Expression* expr) {
+ ForwardBailoutStack stack(expr, forward_bailout_pending_);
+ ForwardBailoutStack* saved = forward_bailout_stack_;
+ forward_bailout_pending_ = NULL;
+ forward_bailout_stack_ = &stack;
+ AstVisitor::Visit(expr);
+ forward_bailout_stack_ = saved;
+}
+
+
void FullCodeGenerator::VisitBlock(Block* stmt) {
Comment cmnt(masm_, "[ Block");
Breakable nested_statement(this, stmt);
SetStatementPosition(stmt);
+
+ PrepareForBailoutForId(stmt->EntryId(), TOS_REG);
VisitStatements(stmt->statements());
__ bind(nested_statement.break_target());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
}
@@ -787,6 +937,7 @@
Visit(stmt->then_statement());
}
__ bind(&done);
+ PrepareForBailoutForId(stmt->id(), NO_REGISTERS);
}
@@ -873,7 +1024,7 @@
void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
Comment cmnt(masm_, "[ DoWhileStatement");
SetStatementPosition(stmt);
- Label body, stack_limit_hit, stack_check_success, done;
+ Label body, stack_check;
Iteration loop_statement(this, stmt);
increment_loop_depth();
@@ -881,75 +1032,63 @@
__ bind(&body);
Visit(stmt->body());
- // Check stack before looping.
- __ bind(loop_statement.continue_target());
- __ StackLimitCheck(&stack_limit_hit);
- __ bind(&stack_check_success);
-
// Record the position of the do while condition and make sure it is
// possible to break on the condition.
+ __ bind(loop_statement.continue_target());
+ PrepareForBailoutForId(stmt->ContinueId(), NO_REGISTERS);
SetExpressionPosition(stmt->cond(), stmt->condition_position());
VisitForControl(stmt->cond(),
- &body,
+ &stack_check,
loop_statement.break_target(),
- loop_statement.break_target());
+ &stack_check);
+
+ // Check stack before looping.
+ __ bind(&stack_check);
+ EmitStackCheck(stmt);
+ __ jmp(&body);
__ bind(loop_statement.break_target());
- __ jmp(&done);
-
- __ bind(&stack_limit_hit);
- StackCheckStub stack_stub;
- __ CallStub(&stack_stub);
- __ jmp(&stack_check_success);
-
- __ bind(&done);
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
decrement_loop_depth();
}
void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
Comment cmnt(masm_, "[ WhileStatement");
- Label body, stack_limit_hit, stack_check_success, done;
+ Label test, body;
Iteration loop_statement(this, stmt);
increment_loop_depth();
// Emit the test at the bottom of the loop.
- __ jmp(loop_statement.continue_target());
+ __ jmp(&test);
__ bind(&body);
Visit(stmt->body());
- __ bind(loop_statement.continue_target());
// Emit the statement position here as this is where the while
// statement code starts.
+ __ bind(loop_statement.continue_target());
SetStatementPosition(stmt);
// Check stack before looping.
- __ StackLimitCheck(&stack_limit_hit);
- __ bind(&stack_check_success);
+ EmitStackCheck(stmt);
+ __ bind(&test);
VisitForControl(stmt->cond(),
&body,
loop_statement.break_target(),
loop_statement.break_target());
__ bind(loop_statement.break_target());
- __ jmp(&done);
-
- __ bind(&stack_limit_hit);
- StackCheckStub stack_stub;
- __ CallStub(&stack_stub);
- __ jmp(&stack_check_success);
-
- __ bind(&done);
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
decrement_loop_depth();
}
void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
Comment cmnt(masm_, "[ ForStatement");
- Label test, body, stack_limit_hit, stack_check_success;
+ Label test, body;
Iteration loop_statement(this, stmt);
if (stmt->init() != NULL) {
@@ -960,30 +1099,25 @@
// Emit the test at the bottom of the loop (even if empty).
__ jmp(&test);
- __ bind(&stack_limit_hit);
- StackCheckStub stack_stub;
- __ CallStub(&stack_stub);
- __ jmp(&stack_check_success);
-
__ bind(&body);
Visit(stmt->body());
__ bind(loop_statement.continue_target());
+ PrepareForBailoutForId(stmt->ContinueId(), NO_REGISTERS);
SetStatementPosition(stmt);
if (stmt->next() != NULL) {
Visit(stmt->next());
}
- __ bind(&test);
// Emit the statement position here as this is where the for
// statement code starts.
SetStatementPosition(stmt);
// Check stack before looping.
- __ StackLimitCheck(&stack_limit_hit);
- __ bind(&stack_check_success);
+ EmitStackCheck(stmt);
+ __ bind(&test);
if (stmt->cond() != NULL) {
VisitForControl(stmt->cond(),
&body,
@@ -994,6 +1128,7 @@
}
__ bind(loop_statement.break_target());
+ PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
decrement_loop_depth();
}
@@ -1131,14 +1266,15 @@
for_test->false_label(),
NULL);
} else {
- Visit(expr->then_expression());
+ context()->HandleExpression(expr->then_expression());
__ jmp(&done);
}
__ bind(&false_case);
+ if (context()->IsTest()) ForwardBailoutToChild(expr);
SetExpressionPosition(expr->else_expression(),
expr->else_expression_position());
- Visit(expr->else_expression());
+ context()->HandleExpression(expr->else_expression());
// If control flow falls through Visit, merge it with true case here.
if (!context()->IsTest()) {
__ bind(&done);
@@ -1146,12 +1282,6 @@
}
-void FullCodeGenerator::VisitSlot(Slot* expr) {
- // Slots do not appear directly in the AST.
- UNREACHABLE();
-}
-
-
void FullCodeGenerator::VisitLiteral(Literal* expr) {
Comment cmnt(masm_, "[ Literal");
context()->Plug(expr->handle());