| Index: src/flow-graph.cc
|
| diff --git a/src/flow-graph.cc b/src/flow-graph.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..bd9602f84054a426411444026889ab544892332a
|
| --- /dev/null
|
| +++ b/src/flow-graph.cc
|
| @@ -0,0 +1,588 @@
|
| +// 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:
|
| +//
|
| +// * Redistributions of source code must retain the above copyright
|
| +// notice, this list of conditions and the following disclaimer.
|
| +// * Redistributions in binary form must reproduce the above
|
| +// copyright notice, this list of conditions and the following
|
| +// disclaimer in the documentation and/or other materials provided
|
| +// with the distribution.
|
| +// * Neither the name of Google Inc. nor the names of its
|
| +// contributors may be used to endorse or promote products derived
|
| +// from this software without specific prior written permission.
|
| +//
|
| +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
| +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
| +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
| +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
| +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
| +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
| +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
| +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| +
|
| +#include "flow-graph.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +
|
| +void FlowGraph::AppendInstruction(AstNode* instruction) {
|
| + // Add a (non-null) AstNode to the end of the graph fragment.
|
| + ASSERT(instruction != NULL);
|
| + if (exit()->IsExitNode()) return;
|
| + if (!exit()->IsBlockNode()) AppendNode(new BlockNode());
|
| + BlockNode::cast(exit())->AddInstruction(instruction);
|
| +}
|
| +
|
| +
|
| +void FlowGraph::AppendNode(Node* node) {
|
| + // Add a node to the end of the graph. An empty block is added to
|
| + // maintain edge-split form (that no join nodes or exit nodes as
|
| + // successors to branch nodes).
|
| + ASSERT(node != NULL);
|
| + if (exit()->IsExitNode()) return;
|
| + if (exit()->IsBranchNode() && (node->IsJoinNode() || node->IsExitNode())) {
|
| + AppendNode(new BlockNode());
|
| + }
|
| + exit()->AddSuccessor(node);
|
| + node->AddPredecessor(exit());
|
| + exit_ = node;
|
| +}
|
| +
|
| +
|
| +void FlowGraph::AppendGraph(FlowGraph* graph) {
|
| + // Add a flow graph fragment to the end of this one. An empty block is
|
| + // added to maintain edge-split form (that no join nodes or exit nodes as
|
| + // successors to branch nodes).
|
| + ASSERT(graph != NULL);
|
| + if (exit()->IsExitNode()) return;
|
| + Node* node = graph->entry();
|
| + if (exit()->IsBranchNode() && (node->IsJoinNode() || node->IsExitNode())) {
|
| + AppendNode(new BlockNode());
|
| + }
|
| + exit()->AddSuccessor(node);
|
| + node->AddPredecessor(exit());
|
| + exit_ = graph->exit();
|
| +}
|
| +
|
| +
|
| +void FlowGraph::Split(BranchNode* branch,
|
| + FlowGraph* left,
|
| + FlowGraph* right,
|
| + JoinNode* join) {
|
| + // Add the branch node, left flowgraph, join node.
|
| + AppendNode(branch);
|
| + AppendGraph(left);
|
| + AppendNode(join);
|
| +
|
| + // Splice in the right flowgraph.
|
| + right->AppendNode(join);
|
| + branch->AddSuccessor(right->entry());
|
| + right->entry()->AddPredecessor(branch);
|
| +}
|
| +
|
| +
|
| +void FlowGraph::Loop(JoinNode* join,
|
| + FlowGraph* condition,
|
| + BranchNode* branch,
|
| + FlowGraph* body) {
|
| + // Add the join, condition and branch. Add join's predecessors in
|
| + // left-to-right order.
|
| + AppendNode(join);
|
| + body->AppendNode(join);
|
| + AppendGraph(condition);
|
| + AppendNode(branch);
|
| +
|
| + // Splice in the body flowgraph.
|
| + branch->AddSuccessor(body->entry());
|
| + body->entry()->AddPredecessor(branch);
|
| +}
|
| +
|
| +
|
| +void ExitNode::Traverse(bool mark,
|
| + ZoneList<Node*>* preorder,
|
| + ZoneList<Node*>* postorder) {
|
| + preorder->Add(this);
|
| + postorder->Add(this);
|
| +}
|
| +
|
| +
|
| +void BlockNode::Traverse(bool mark,
|
| + ZoneList<Node*>* preorder,
|
| + ZoneList<Node*>* postorder) {
|
| + ASSERT(successor_ != NULL);
|
| + preorder->Add(this);
|
| + if (!successor_->IsMarkedWith(mark)) {
|
| + successor_->MarkWith(mark);
|
| + successor_->Traverse(mark, preorder, postorder);
|
| + }
|
| + postorder->Add(this);
|
| +}
|
| +
|
| +
|
| +void BranchNode::Traverse(bool mark,
|
| + ZoneList<Node*>* preorder,
|
| + ZoneList<Node*>* postorder) {
|
| + ASSERT(successor0_ != NULL && successor1_ != NULL);
|
| + preorder->Add(this);
|
| + if (!successor1_->IsMarkedWith(mark)) {
|
| + successor1_->MarkWith(mark);
|
| + successor1_->Traverse(mark, preorder, postorder);
|
| + }
|
| + if (!successor0_->IsMarkedWith(mark)) {
|
| + successor0_->MarkWith(mark);
|
| + successor0_->Traverse(mark, preorder, postorder);
|
| + }
|
| + postorder->Add(this);
|
| +}
|
| +
|
| +
|
| +void JoinNode::Traverse(bool mark,
|
| + ZoneList<Node*>* preorder,
|
| + ZoneList<Node*>* postorder) {
|
| + ASSERT(successor_ != NULL);
|
| + preorder->Add(this);
|
| + if (!successor_->IsMarkedWith(mark)) {
|
| + successor_->MarkWith(mark);
|
| + successor_->Traverse(mark, preorder, postorder);
|
| + }
|
| + postorder->Add(this);
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::Build(FunctionLiteral* lit) {
|
| + global_exit_ = new ExitNode();
|
| + VisitStatements(lit->body());
|
| +
|
| + if (HasStackOverflow()) return;
|
| +
|
| + // The graph can end with a branch node (if the function ended with a
|
| + // loop). Maintain edge-split form (no join nodes or exit nodes as
|
| + // successors to branch nodes).
|
| + if (graph_.exit()->IsBranchNode()) graph_.AppendNode(new BlockNode());
|
| + graph_.AppendNode(global_exit_);
|
| +
|
| + // Build preorder and postorder traversal orders. All the nodes in
|
| + // the graph have the same mark flag. For the traversal, use that
|
| + // flag's negation. Traversal will flip all the flags.
|
| + bool mark = graph_.entry()->IsMarkedWith(false);
|
| + graph_.entry()->MarkWith(mark);
|
| + graph_.entry()->Traverse(mark, &preorder_, &postorder_);
|
| +}
|
| +
|
| +
|
| +// This function peels off one iteration of a for-loop. The return value
|
| +// is either a block statement containing the peeled loop or NULL in case
|
| +// there is a stack overflow.
|
| +static Statement* PeelForLoop(ForStatement* stmt) {
|
| + // Mark this for-statement as processed.
|
| + stmt->set_peel_this_loop(false);
|
| +
|
| + // Create new block containing the init statement of the for-loop and
|
| + // an if-statement containing the peeled iteration and the original
|
| + // loop without the init-statement.
|
| + Block* block = new Block(NULL, 2, false);
|
| + if (stmt->init() != NULL) {
|
| + Statement* init = stmt->init();
|
| + // The init statement gets the statement position of the for-loop
|
| + // to make debugging of peeled loops possible.
|
| + init->set_statement_pos(stmt->statement_pos());
|
| + block->AddStatement(init);
|
| + }
|
| +
|
| + // Copy the condition.
|
| + CopyAstVisitor copy_visitor;
|
| + Expression* cond_copy = stmt->cond() != NULL
|
| + ? copy_visitor.DeepCopyExpr(stmt->cond())
|
| + : new Literal(Factory::true_value());
|
| + if (copy_visitor.HasStackOverflow()) return NULL;
|
| +
|
| + // Construct a block with the peeled body and the rest of the for-loop.
|
| + Statement* body_copy = copy_visitor.DeepCopyStmt(stmt->body());
|
| + if (copy_visitor.HasStackOverflow()) return NULL;
|
| +
|
| + Statement* next_copy = stmt->next() != NULL
|
| + ? copy_visitor.DeepCopyStmt(stmt->next())
|
| + : new EmptyStatement();
|
| + if (copy_visitor.HasStackOverflow()) return NULL;
|
| +
|
| + Block* peeled_body = new Block(NULL, 3, false);
|
| + peeled_body->AddStatement(body_copy);
|
| + peeled_body->AddStatement(next_copy);
|
| + peeled_body->AddStatement(stmt);
|
| +
|
| + // Remove the duplicated init statement from the for-statement.
|
| + stmt->set_init(NULL);
|
| +
|
| + // Create new test at the top and add it to the newly created block.
|
| + IfStatement* test = new IfStatement(cond_copy,
|
| + peeled_body,
|
| + new EmptyStatement());
|
| + block->AddStatement(test);
|
| + return block;
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitStatements(ZoneList<Statement*>* stmts) {
|
| + for (int i = 0, len = stmts->length(); i < len; i++) {
|
| + stmts->at(i) = ProcessStatement(stmts->at(i));
|
| + }
|
| +}
|
| +
|
| +
|
| +Statement* FlowGraphBuilder::ProcessStatement(Statement* stmt) {
|
| + if (FLAG_loop_peeling &&
|
| + stmt->AsForStatement() != NULL &&
|
| + stmt->AsForStatement()->peel_this_loop()) {
|
| + Statement* tmp_stmt = PeelForLoop(stmt->AsForStatement());
|
| + if (tmp_stmt == NULL) {
|
| + SetStackOverflow();
|
| + } else {
|
| + stmt = tmp_stmt;
|
| + }
|
| + }
|
| + Visit(stmt);
|
| + return stmt;
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitDeclaration(Declaration* decl) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitBlock(Block* stmt) {
|
| + VisitStatements(stmt->statements());
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitExpressionStatement(ExpressionStatement* stmt) {
|
| + Visit(stmt->expression());
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitEmptyStatement(EmptyStatement* stmt) {
|
| + // Nothing to do.
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitIfStatement(IfStatement* stmt) {
|
| + Visit(stmt->condition());
|
| +
|
| + BranchNode* branch = new BranchNode();
|
| + FlowGraph original = graph_;
|
| + graph_ = FlowGraph::Empty();
|
| + stmt->set_then_statement(ProcessStatement(stmt->then_statement()));
|
| +
|
| + FlowGraph left = graph_;
|
| + graph_ = FlowGraph::Empty();
|
| + stmt->set_else_statement(ProcessStatement(stmt->else_statement()));
|
| +
|
| + if (HasStackOverflow()) return;
|
| + JoinNode* join = new JoinNode();
|
| + original.Split(branch, &left, &graph_, join);
|
| + graph_ = original;
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitContinueStatement(ContinueStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitBreakStatement(BreakStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitDoWhileStatement(DoWhileStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitWhileStatement(WhileStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitForStatement(ForStatement* stmt) {
|
| + if (stmt->init() != NULL) stmt->set_init(ProcessStatement(stmt->init()));
|
| +
|
| + JoinNode* join = new JoinNode();
|
| + FlowGraph original = graph_;
|
| + graph_ = FlowGraph::Empty();
|
| + if (stmt->cond() != NULL) Visit(stmt->cond());
|
| +
|
| + BranchNode* branch = new BranchNode();
|
| + FlowGraph condition = graph_;
|
| + graph_ = FlowGraph::Empty();
|
| + stmt->set_body(ProcessStatement(stmt->body()));
|
| +
|
| + if (stmt->next() != NULL) stmt->set_next(ProcessStatement(stmt->next()));
|
| +
|
| + if (HasStackOverflow()) return;
|
| + original.Loop(join, &condition, branch, &graph_);
|
| + graph_ = original;
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitTryCatchStatement(TryCatchStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitFunctionLiteral(FunctionLiteral* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitSharedFunctionInfoLiteral(
|
| + SharedFunctionInfoLiteral* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitConditional(Conditional* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitSlot(Slot* expr) {
|
| + UNREACHABLE();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitVariableProxy(VariableProxy* expr) {
|
| + graph_.AppendInstruction(expr);
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitLiteral(Literal* expr) {
|
| + graph_.AppendInstruction(expr);
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitRegExpLiteral(RegExpLiteral* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitAssignment(Assignment* expr) {
|
| + Variable* var = expr->target()->AsVariableProxy()->AsVariable();
|
| + Property* prop = expr->target()->AsProperty();
|
| + // Left-hand side can be a variable or property (or reference error) but
|
| + // not both.
|
| + ASSERT(var == NULL || prop == NULL);
|
| + if (var != NULL) {
|
| + if (expr->is_compound()) Visit(expr->target());
|
| + Visit(expr->value());
|
| + if (var->IsStackAllocated()) {
|
| + // The first definition in the body is numbered n, where n is the
|
| + // number of parameters and stack-allocated locals.
|
| + expr->set_num(body_definitions_.length() + variable_count_);
|
| + body_definitions_.Add(expr);
|
| + }
|
| +
|
| + } else if (prop != NULL) {
|
| + Visit(prop->obj());
|
| + if (!prop->key()->IsPropertyName()) Visit(prop->key());
|
| + Visit(expr->value());
|
| + }
|
| +
|
| + if (HasStackOverflow()) return;
|
| + graph_.AppendInstruction(expr);
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitThrow(Throw* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitProperty(Property* expr) {
|
| + Visit(expr->obj());
|
| + if (!expr->key()->IsPropertyName()) Visit(expr->key());
|
| +
|
| + if (HasStackOverflow()) return;
|
| + graph_.AppendInstruction(expr);
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitCall(Call* expr) {
|
| + Visit(expr->expression());
|
| + ZoneList<Expression*>* arguments = expr->arguments();
|
| + for (int i = 0, len = arguments->length(); i < len; i++) {
|
| + Visit(arguments->at(i));
|
| + }
|
| +
|
| + if (HasStackOverflow()) return;
|
| + graph_.AppendInstruction(expr);
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitCallNew(CallNew* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitCallRuntime(CallRuntime* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) {
|
| + switch (expr->op()) {
|
| + case Token::NOT:
|
| + case Token::BIT_NOT:
|
| + case Token::DELETE:
|
| + case Token::TYPEOF:
|
| + case Token::VOID:
|
| + SetStackOverflow();
|
| + break;
|
| +
|
| + case Token::ADD:
|
| + case Token::SUB:
|
| + Visit(expr->expression());
|
| + if (HasStackOverflow()) return;
|
| + graph_.AppendInstruction(expr);
|
| + break;
|
| +
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitCountOperation(CountOperation* expr) {
|
| + Visit(expr->expression());
|
| + Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
|
| + if (var != NULL && var->IsStackAllocated()) {
|
| + // The first definition in the body is numbered n, where n is the number
|
| + // of parameters and stack-allocated locals.
|
| + expr->set_num(body_definitions_.length() + variable_count_);
|
| + body_definitions_.Add(expr);
|
| + }
|
| +
|
| + if (HasStackOverflow()) return;
|
| + graph_.AppendInstruction(expr);
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitBinaryOperation(BinaryOperation* expr) {
|
| + switch (expr->op()) {
|
| + case Token::COMMA:
|
| + case Token::OR:
|
| + case Token::AND:
|
| + SetStackOverflow();
|
| + break;
|
| +
|
| + case Token::BIT_OR:
|
| + case Token::BIT_XOR:
|
| + case Token::BIT_AND:
|
| + case Token::SHL:
|
| + case Token::SHR:
|
| + case Token::ADD:
|
| + case Token::SUB:
|
| + case Token::MUL:
|
| + case Token::DIV:
|
| + case Token::MOD:
|
| + case Token::SAR:
|
| + Visit(expr->left());
|
| + Visit(expr->right());
|
| + if (HasStackOverflow()) return;
|
| + graph_.AppendInstruction(expr);
|
| + break;
|
| +
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitCompareOperation(CompareOperation* expr) {
|
| + switch (expr->op()) {
|
| + case Token::EQ:
|
| + case Token::NE:
|
| + case Token::EQ_STRICT:
|
| + case Token::NE_STRICT:
|
| + case Token::INSTANCEOF:
|
| + case Token::IN:
|
| + SetStackOverflow();
|
| + break;
|
| +
|
| + case Token::LT:
|
| + case Token::GT:
|
| + case Token::LTE:
|
| + case Token::GTE:
|
| + Visit(expr->left());
|
| + Visit(expr->right());
|
| + if (HasStackOverflow()) return;
|
| + graph_.AppendInstruction(expr);
|
| + break;
|
| +
|
| + default:
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| +
|
| +void FlowGraphBuilder::VisitThisFunction(ThisFunction* expr) {
|
| + SetStackOverflow();
|
| +}
|
| +
|
| +
|
| +} } // namespace v8::internal
|
|
|