// 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" #include "scopes.h" namespace v8 { namespace internal { void BasicBlock::BuildTraversalOrder(ZoneList* preorder, ZoneList* postorder, bool mark) { if (mark_ == mark) return; mark_ = mark; preorder->Add(this); if (right_successor_ != NULL) { right_successor_->BuildTraversalOrder(preorder, postorder, mark); } if (left_successor_ != NULL) { left_successor_->BuildTraversalOrder(preorder, postorder, mark); } postorder->Add(this); } FlowGraph* FlowGraphBuilder::Build(FunctionLiteral* lit) { // Create new entry and exit nodes. These will not change during // construction. entry_ = new BasicBlock(NULL); exit_ = new BasicBlock(NULL); // Begin accumulating instructions in the entry block. current_ = entry_; VisitDeclarations(lit->scope()->declarations()); VisitStatements(lit->body()); // In the event of stack overflow or failure to handle a syntactic // construct, return an invalid flow graph. if (HasStackOverflow()) return new FlowGraph(NULL, NULL); // If current is not the exit, add a link to the exit. if (current_ != exit_) { // If current already has a successor (i.e., will be a branch node) and // if the exit already has a predecessor, insert an empty block to // maintain edge split form. if (current_->HasSuccessor() && exit_->HasPredecessor()) { current_ = new BasicBlock(current_); } Literal* undefined = new Literal(Factory::undefined_value()); current_->AddInstruction(new ReturnStatement(undefined)); exit_->AddPredecessor(current_); } FlowGraph* graph = new FlowGraph(entry_, exit_); bool mark = !entry_->GetMark(); entry_->BuildTraversalOrder(graph->preorder(), graph->postorder(), mark); #ifdef DEBUG // Number the nodes in reverse postorder. int n = 0; for (int i = graph->postorder()->length() - 1; i >= 0; --i) { graph->postorder()->at(i)->set_number(n++); } #endif return graph; } void FlowGraphBuilder::VisitDeclaration(Declaration* decl) { Variable* var = decl->proxy()->AsVariable(); Slot* slot = var->slot(); // We allow only declarations that do not require code generation. // The following all require code generation: global variables and // functions, variables with slot type LOOKUP, declarations with // mode CONST, and functions. if (var->is_global() || (slot != NULL && slot->type() == Slot::LOOKUP) || decl->mode() == Variable::CONST || decl->fun() != NULL) { // Here and in the rest of the flow graph builder we indicate an // unsupported syntactic construct by setting the stack overflow // flag on the visitor. This causes bailout of the visitor. SetStackOverflow(); } } 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) { // Build a diamond in the flow graph. First accumulate the instructions // of the test in the current basic block. Visit(stmt->condition()); // Remember the branch node and accumulate the true branch as its left // successor. This relies on the successors being added left to right. BasicBlock* branch = current_; current_ = new BasicBlock(branch); Visit(stmt->then_statement()); // Construct a join node and then accumulate the false branch in a fresh // successor of the branch node. BasicBlock* join = new BasicBlock(current_); current_ = new BasicBlock(branch); Visit(stmt->else_statement()); join->AddPredecessor(current_); current_ = join; } 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) { // Build a loop in the flow graph. First accumulate the instructions of // the initializer in the current basic block. if (stmt->init() != NULL) Visit(stmt->init()); // Create a new basic block for the test. This will be the join node. BasicBlock* join = new BasicBlock(current_); current_ = join; if (stmt->cond() != NULL) Visit(stmt->cond()); // The current node is the branch node. Create a new basic block to begin // the body. BasicBlock* branch = current_; current_ = new BasicBlock(branch); Visit(stmt->body()); if (stmt->next() != NULL) Visit(stmt->next()); // Add the backward edge from the end of the body and continue with the // false arm of the branch. join->AddPredecessor(current_); current_ = new BasicBlock(branch); } 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) { // Slots do not appear in the AST. UNREACHABLE(); } void FlowGraphBuilder::VisitVariableProxy(VariableProxy* expr) { current_->AddInstruction(expr); } void FlowGraphBuilder::VisitLiteral(Literal* expr) { current_->AddInstruction(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) { // There are three basic kinds of assignment: variable assignments, // property assignments, and invalid left-hand sides (which are translated // to "throw ReferenceError" by the parser). Variable* var = expr->target()->AsVariableProxy()->AsVariable(); Property* prop = expr->target()->AsProperty(); ASSERT(var == NULL || prop == NULL); if (var != NULL) { if (expr->is_compound() && !expr->target()->IsTrivial()) { Visit(expr->target()); } if (!expr->value()->IsTrivial()) Visit(expr->value()); current_->AddInstruction(expr); } else if (prop != NULL) { if (!prop->obj()->IsTrivial()) Visit(prop->obj()); if (!prop->key()->IsPropertyName() && !prop->key()->IsTrivial()) { Visit(prop->key()); } if (!expr->value()->IsTrivial()) Visit(expr->value()); current_->AddInstruction(expr); } else { Visit(expr->target()); } } void FlowGraphBuilder::VisitThrow(Throw* expr) { SetStackOverflow(); } void FlowGraphBuilder::VisitProperty(Property* expr) { if (!expr->obj()->IsTrivial()) Visit(expr->obj()); if (!expr->key()->IsPropertyName() && !expr->key()->IsTrivial()) { Visit(expr->key()); } current_->AddInstruction(expr); } void FlowGraphBuilder::VisitCall(Call* expr) { Visit(expr->expression()); VisitExpressions(expr->arguments()); current_->AddInstruction(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()); current_->AddInstruction(expr); break; default: UNREACHABLE(); } } void FlowGraphBuilder::VisitCountOperation(CountOperation* expr) { Visit(expr->expression()); current_->AddInstruction(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::SAR: case Token::SHR: case Token::ADD: case Token::SUB: case Token::MUL: case Token::DIV: case Token::MOD: if (!expr->left()->IsTrivial()) Visit(expr->left()); if (!expr->right()->IsTrivial()) Visit(expr->right()); current_->AddInstruction(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: if (!expr->left()->IsTrivial()) Visit(expr->left()); if (!expr->right()->IsTrivial()) Visit(expr->right()); current_->AddInstruction(expr); break; default: UNREACHABLE(); } } void FlowGraphBuilder::VisitThisFunction(ThisFunction* expr) { SetStackOverflow(); } #ifdef DEBUG // Print a textual representation of an instruction in a flow graph. class InstructionPrinter: public AstVisitor { public: InstructionPrinter() {} private: // Overridden from the base class. virtual void VisitExpressions(ZoneList* exprs); // AST node visit functions. #define DECLARE_VISIT(type) virtual void Visit##type(type* node); AST_NODE_LIST(DECLARE_VISIT) #undef DECLARE_VISIT DISALLOW_COPY_AND_ASSIGN(InstructionPrinter); }; static void PrintSubexpression(Expression* expr) { if (!expr->IsTrivial()) { PrintF("@%d", expr->num()); } else if (expr->AsLiteral() != NULL) { expr->AsLiteral()->handle()->Print(); } else if (expr->AsVariableProxy() != NULL) { PrintF("%s", *expr->AsVariableProxy()->name()->ToCString()); } else { UNREACHABLE(); } } void InstructionPrinter::VisitExpressions(ZoneList* exprs) { for (int i = 0; i < exprs->length(); ++i) { if (i != 0) PrintF(", "); PrintF("@%d", exprs->at(i)->num()); } } // We only define printing functions for the node types that can occur as // instructions in a flow graph. The rest are unreachable. void InstructionPrinter::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } void InstructionPrinter::VisitBlock(Block* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitExpressionStatement(ExpressionStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitEmptyStatement(EmptyStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitIfStatement(IfStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitContinueStatement(ContinueStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitBreakStatement(BreakStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitReturnStatement(ReturnStatement* stmt) { PrintF("return "); PrintSubexpression(stmt->expression()); } void InstructionPrinter::VisitWithEnterStatement(WithEnterStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitWithExitStatement(WithExitStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitSwitchStatement(SwitchStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitDoWhileStatement(DoWhileStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitWhileStatement(WhileStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitForStatement(ForStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitForInStatement(ForInStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitTryCatchStatement(TryCatchStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitTryFinallyStatement(TryFinallyStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitDebuggerStatement(DebuggerStatement* stmt) { UNREACHABLE(); } void InstructionPrinter::VisitFunctionLiteral(FunctionLiteral* expr) { UNREACHABLE(); } void InstructionPrinter::VisitSharedFunctionInfoLiteral( SharedFunctionInfoLiteral* expr) { UNREACHABLE(); } void InstructionPrinter::VisitConditional(Conditional* expr) { UNREACHABLE(); } void InstructionPrinter::VisitSlot(Slot* expr) { UNREACHABLE(); } void InstructionPrinter::VisitVariableProxy(VariableProxy* expr) { Variable* var = expr->AsVariable(); if (var != NULL) { PrintF("%s", *var->name()->ToCString()); } else { ASSERT(expr->AsProperty() != NULL); Visit(expr->AsProperty()); } } void InstructionPrinter::VisitLiteral(Literal* expr) { expr->handle()->Print(); } void InstructionPrinter::VisitRegExpLiteral(RegExpLiteral* expr) { UNREACHABLE(); } void InstructionPrinter::VisitObjectLiteral(ObjectLiteral* expr) { UNREACHABLE(); } void InstructionPrinter::VisitArrayLiteral(ArrayLiteral* expr) { UNREACHABLE(); } void InstructionPrinter::VisitCatchExtensionObject( CatchExtensionObject* expr) { UNREACHABLE(); } void InstructionPrinter::VisitAssignment(Assignment* expr) { Variable* var = expr->target()->AsVariableProxy()->AsVariable(); Property* prop = expr->target()->AsProperty(); // Print the left-hand side. Visit(expr->target()); if (var == NULL && prop == NULL) return; // Throw reference error. PrintF(" = "); // For compound assignments, print the left-hand side again and the // corresponding binary operator. if (expr->is_compound()) { PrintSubexpression(expr->target()); PrintF(" %s ", Token::String(expr->binary_op())); } // Print the right-hand side. PrintSubexpression(expr->value()); } void InstructionPrinter::VisitThrow(Throw* expr) { UNREACHABLE(); } void InstructionPrinter::VisitProperty(Property* expr) { PrintSubexpression(expr->obj()); if (expr->key()->IsPropertyName()) { PrintF("."); ASSERT(expr->key()->AsLiteral() != NULL); expr->key()->AsLiteral()->handle()->Print(); } else { PrintF("["); PrintSubexpression(expr->key()); PrintF("]"); } } void InstructionPrinter::VisitCall(Call* expr) { PrintF("@%d(", expr->expression()->num()); VisitExpressions(expr->arguments()); PrintF(")"); } void InstructionPrinter::VisitCallNew(CallNew* expr) { UNREACHABLE(); } void InstructionPrinter::VisitCallRuntime(CallRuntime* expr) { UNREACHABLE(); } void InstructionPrinter::VisitUnaryOperation(UnaryOperation* expr) { PrintF("%s(@%d)", Token::String(expr->op()), expr->expression()->num()); } void InstructionPrinter::VisitCountOperation(CountOperation* expr) { if (expr->is_prefix()) { PrintF("%s@%d", Token::String(expr->op()), expr->expression()->num()); } else { PrintF("@%d%s", expr->expression()->num(), Token::String(expr->op())); } } void InstructionPrinter::VisitBinaryOperation(BinaryOperation* expr) { PrintSubexpression(expr->left()); PrintF(" %s ", Token::String(expr->op())); PrintSubexpression(expr->right()); } void InstructionPrinter::VisitCompareOperation(CompareOperation* expr) { PrintSubexpression(expr->left()); PrintF(" %s ", Token::String(expr->op())); PrintSubexpression(expr->right()); } void InstructionPrinter::VisitThisFunction(ThisFunction* expr) { UNREACHABLE(); } int BasicBlock::PrintAsText(int instruction_number) { // Print a label for all blocks except the entry. if (HasPredecessor()) { PrintF("L%d:", number()); } // Number and print the instructions. Since AST child nodes are visited // before their parents, the parent nodes can refer to them by number. InstructionPrinter printer; for (int i = 0; i < instructions_.length(); ++i) { PrintF("\n%d ", instruction_number); instructions_[i]->set_num(instruction_number++); instructions_[i]->Accept(&printer); } // If this is the exit, print "exit". If there is a single successor, // print "goto" successor on a separate line. If there are two // successors, print "goto" successor on the same line as the last // instruction in the block. There is a blank line between blocks (and // after the last one). if (left_successor_ == NULL) { PrintF("\nexit\n\n"); } else if (right_successor_ == NULL) { PrintF("\ngoto L%d\n\n", left_successor_->number()); } else { PrintF(", goto (L%d, L%d)\n\n", left_successor_->number(), right_successor_->number()); } return instruction_number; } void FlowGraph::PrintAsText(Handle name) { PrintF("\n==== name = \"%s\" ====\n", *name->ToCString()); // Print nodes in reverse postorder. Note that AST node numbers are used // during printing of instructions and thus their current values are // destroyed. int number = 0; for (int i = postorder_.length() - 1; i >= 0; --i) { number = postorder_[i]->PrintAsText(number); } } #endif // DEBUG } } // namespace v8::internal