// Copyright 2009 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 "v8.h" #include "bootstrapper.h" #include "cfg.h" #include "scopeinfo.h" #include "scopes.h" namespace v8 { namespace internal { CfgGlobals* CfgGlobals::top_ = NULL; CfgGlobals::CfgGlobals(FunctionLiteral* fun) : global_fun_(fun), global_exit_(new ExitNode()), nowhere_(new Nowhere()), #ifdef DEBUG node_counter_(0), temp_counter_(0), #endif previous_(top_) { top_ = this; } #define BAILOUT(reason) \ do { return NULL; } while (false) Cfg* Cfg::Build() { FunctionLiteral* fun = CfgGlobals::current()->fun(); if (fun->scope()->num_heap_slots() > 0) { BAILOUT("function has context slots"); } if (fun->scope()->num_stack_slots() > kBitsPerPointer) { BAILOUT("function has too many locals"); } if (fun->scope()->num_parameters() > kBitsPerPointer - 1) { BAILOUT("function has too many parameters"); } if (fun->scope()->arguments() != NULL) { BAILOUT("function uses .arguments"); } ZoneList<Statement*>* body = fun->body(); if (body->is_empty()) { BAILOUT("empty function body"); } StatementCfgBuilder builder; builder.VisitStatements(body); Cfg* graph = builder.graph(); if (graph == NULL) { BAILOUT("unsupported statement type"); } if (graph->is_empty()) { BAILOUT("function body produces empty cfg"); } if (graph->has_exit()) { BAILOUT("control path without explicit return"); } graph->PrependEntryNode(); return graph; } #undef BAILOUT void Cfg::PrependEntryNode() { ASSERT(!is_empty()); entry_ = new EntryNode(InstructionBlock::cast(entry())); } void Cfg::Append(Instruction* instr) { ASSERT(is_empty() || has_exit()); if (is_empty()) { entry_ = exit_ = new InstructionBlock(); } InstructionBlock::cast(exit_)->Append(instr); } void Cfg::AppendReturnInstruction(Value* value) { Append(new ReturnInstr(value)); ExitNode* global_exit = CfgGlobals::current()->exit(); InstructionBlock::cast(exit_)->set_successor(global_exit); exit_ = NULL; } void Cfg::Concatenate(Cfg* other) { ASSERT(is_empty() || has_exit()); if (other->is_empty()) return; if (is_empty()) { entry_ = other->entry(); exit_ = other->exit(); } else { // We have a pair of nonempty fragments and this has an available exit. // Destructively glue the fragments together. InstructionBlock* first = InstructionBlock::cast(exit_); InstructionBlock* second = InstructionBlock::cast(other->entry()); first->instructions()->AddAll(*second->instructions()); if (second->successor() != NULL) { first->set_successor(second->successor()); exit_ = other->exit(); } } } void InstructionBlock::Unmark() { if (is_marked_) { is_marked_ = false; successor_->Unmark(); } } void EntryNode::Unmark() { if (is_marked_) { is_marked_ = false; successor_->Unmark(); } } void ExitNode::Unmark() { is_marked_ = false; } Handle<Code> Cfg::Compile(Handle<Script> script) { const int kInitialBufferSize = 4 * KB; MacroAssembler* masm = new MacroAssembler(NULL, kInitialBufferSize); entry()->Compile(masm); entry()->Unmark(); CodeDesc desc; masm->GetCode(&desc); FunctionLiteral* fun = CfgGlobals::current()->fun(); ZoneScopeInfo info(fun->scope()); InLoopFlag in_loop = fun->loop_nesting() ? IN_LOOP : NOT_IN_LOOP; Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop); Handle<Code> code = Factory::NewCode(desc, &info, flags, masm->CodeObject()); // Add unresolved entries in the code to the fixup list. Bootstrapper::AddFixup(*code, masm); #ifdef ENABLE_DISASSEMBLER if (FLAG_print_code) { // Print the source code if available. if (!script->IsUndefined() && !script->source()->IsUndefined()) { PrintF("--- Raw source ---\n"); StringInputBuffer stream(String::cast(script->source())); stream.Seek(fun->start_position()); // fun->end_position() points to the last character in the // stream. We need to compensate by adding one to calculate the // length. int source_len = fun->end_position() - fun->start_position() + 1; for (int i = 0; i < source_len; i++) { if (stream.has_more()) PrintF("%c", stream.GetNext()); } PrintF("\n\n"); } PrintF("--- Code ---\n"); code->Disassemble(*fun->name()->ToCString()); } #endif return code; } void ZeroOperandInstruction::FastAllocate(TempLocation* temp) { temp->set_where(TempLocation::STACK); } void OneOperandInstruction::FastAllocate(TempLocation* temp) { temp->set_where((temp == value_) ? TempLocation::ACCUMULATOR : TempLocation::STACK); } void TwoOperandInstruction::FastAllocate(TempLocation* temp) { temp->set_where((temp == value0_ || temp == value1_) ? TempLocation::ACCUMULATOR : TempLocation::STACK); } void PositionInstr::Compile(MacroAssembler* masm) { if (FLAG_debug_info && pos_ != RelocInfo::kNoPosition) { masm->RecordStatementPosition(pos_); masm->RecordPosition(pos_); } } void MoveInstr::Compile(MacroAssembler* masm) { location()->Move(masm, value()); } // The expression builder should not be used for declarations or statements. void ExpressionCfgBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } #define DEFINE_VISIT(type) \ void ExpressionCfgBuilder::Visit##type(type* stmt) { UNREACHABLE(); } STATEMENT_NODE_LIST(DEFINE_VISIT) #undef DEFINE_VISIT // Macros (temporarily) handling unsupported expression types. #define BAILOUT(reason) \ do { \ graph_ = NULL; \ return; \ } while (false) void ExpressionCfgBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { BAILOUT("FunctionLiteral"); } void ExpressionCfgBuilder::VisitFunctionBoilerplateLiteral( FunctionBoilerplateLiteral* expr) { BAILOUT("FunctionBoilerplateLiteral"); } void ExpressionCfgBuilder::VisitConditional(Conditional* expr) { BAILOUT("Conditional"); } void ExpressionCfgBuilder::VisitSlot(Slot* expr) { BAILOUT("Slot"); } void ExpressionCfgBuilder::VisitVariableProxy(VariableProxy* expr) { Expression* rewrite = expr->var()->rewrite(); if (rewrite == NULL || rewrite->AsSlot() == NULL) { BAILOUT("unsupported variable (not a slot)"); } Slot* slot = rewrite->AsSlot(); if (slot->type() != Slot::PARAMETER && slot->type() != Slot::LOCAL) { BAILOUT("unsupported slot type (not a parameter or local)"); } // Ignore the passed destination. value_ = new SlotLocation(slot->type(), slot->index()); } void ExpressionCfgBuilder::VisitLiteral(Literal* expr) { // Ignore the passed destination. value_ = new Constant(expr->handle()); } void ExpressionCfgBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { BAILOUT("RegExpLiteral"); } void ExpressionCfgBuilder::VisitObjectLiteral(ObjectLiteral* expr) { BAILOUT("ObjectLiteral"); } void ExpressionCfgBuilder::VisitArrayLiteral(ArrayLiteral* expr) { BAILOUT("ArrayLiteral"); } void ExpressionCfgBuilder::VisitCatchExtensionObject( CatchExtensionObject* expr) { BAILOUT("CatchExtensionObject"); } void ExpressionCfgBuilder::VisitAssignment(Assignment* expr) { if (expr->op() != Token::ASSIGN && expr->op() != Token::INIT_VAR) { BAILOUT("unsupported compound assignment"); } Expression* lhs = expr->target(); if (lhs->AsProperty() != NULL) { BAILOUT("unsupported property assignment"); } Variable* var = lhs->AsVariableProxy()->AsVariable(); if (var == NULL) { BAILOUT("unsupported invalid left-hand side"); } if (var->is_global()) { BAILOUT("unsupported global variable"); } Slot* slot = var->slot(); ASSERT(slot != NULL); if (slot->type() != Slot::PARAMETER && slot->type() != Slot::LOCAL) { BAILOUT("unsupported slot lhs (not a parameter or local)"); } // Parameter and local slot assignments. ExpressionCfgBuilder builder; SlotLocation* loc = new SlotLocation(slot->type(), slot->index()); builder.Build(expr->value(), loc); if (builder.graph() == NULL) { BAILOUT("unsupported expression in assignment"); } // If the expression did not come back in the slot location, append // a move to the CFG. graph_ = builder.graph(); if (builder.value() != loc) { graph()->Append(new MoveInstr(loc, builder.value())); } // Record the assignment. assigned_vars_.AddElement(loc); // Ignore the destination passed to us. value_ = loc; } void ExpressionCfgBuilder::VisitThrow(Throw* expr) { BAILOUT("Throw"); } void ExpressionCfgBuilder::VisitProperty(Property* expr) { ExpressionCfgBuilder object, key; object.Build(expr->obj(), NULL); if (object.graph() == NULL) { BAILOUT("unsupported object subexpression in propload"); } key.Build(expr->key(), NULL); if (key.graph() == NULL) { BAILOUT("unsupported key subexpression in propload"); } if (destination_ == NULL) destination_ = new TempLocation(); graph_ = object.graph(); // Insert a move to a fresh temporary if the object value is in a slot // that's assigned in the key. Location* temp = NULL; if (object.value()->is_slot() && key.assigned_vars()->Contains(SlotLocation::cast(object.value()))) { temp = new TempLocation(); graph()->Append(new MoveInstr(temp, object.value())); } graph()->Concatenate(key.graph()); graph()->Append(new PropLoadInstr(destination_, temp == NULL ? object.value() : temp, key.value())); assigned_vars_ = *object.assigned_vars(); assigned_vars()->Union(key.assigned_vars()); value_ = destination_; } void ExpressionCfgBuilder::VisitCall(Call* expr) { BAILOUT("Call"); } void ExpressionCfgBuilder::VisitCallEval(CallEval* expr) { BAILOUT("CallEval"); } void ExpressionCfgBuilder::VisitCallNew(CallNew* expr) { BAILOUT("CallNew"); } void ExpressionCfgBuilder::VisitCallRuntime(CallRuntime* expr) { BAILOUT("CallRuntime"); } void ExpressionCfgBuilder::VisitUnaryOperation(UnaryOperation* expr) { BAILOUT("UnaryOperation"); } void ExpressionCfgBuilder::VisitCountOperation(CountOperation* expr) { BAILOUT("CountOperation"); } void ExpressionCfgBuilder::VisitBinaryOperation(BinaryOperation* expr) { Token::Value op = expr->op(); switch (op) { case Token::COMMA: case Token::OR: case Token::AND: BAILOUT("unsupported binary operation"); 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: { ExpressionCfgBuilder left, right; left.Build(expr->left(), NULL); if (left.graph() == NULL) { BAILOUT("unsupported left subexpression in binop"); } right.Build(expr->right(), NULL); if (right.graph() == NULL) { BAILOUT("unsupported right subexpression in binop"); } if (destination_ == NULL) destination_ = new TempLocation(); graph_ = left.graph(); // Insert a move to a fresh temporary if the left value is in a // slot that's assigned on the right. Location* temp = NULL; if (left.value()->is_slot() && right.assigned_vars()->Contains(SlotLocation::cast(left.value()))) { temp = new TempLocation(); graph()->Append(new MoveInstr(temp, left.value())); } graph()->Concatenate(right.graph()); graph()->Append(new BinaryOpInstr(destination_, op, temp == NULL ? left.value() : temp, right.value())); assigned_vars_ = *left.assigned_vars(); assigned_vars()->Union(right.assigned_vars()); value_ = destination_; return; } default: UNREACHABLE(); } } void ExpressionCfgBuilder::VisitCompareOperation(CompareOperation* expr) { BAILOUT("CompareOperation"); } void ExpressionCfgBuilder::VisitThisFunction(ThisFunction* expr) { BAILOUT("ThisFunction"); } #undef BAILOUT // Macros (temporarily) handling unsupported statement types. #define BAILOUT(reason) \ do { \ graph_ = NULL; \ return; \ } while (false) #define CHECK_BAILOUT() \ if (graph() == NULL) { return; } else {} void StatementCfgBuilder::VisitStatements(ZoneList<Statement*>* stmts) { for (int i = 0, len = stmts->length(); i < len; i++) { Visit(stmts->at(i)); CHECK_BAILOUT(); if (!graph()->has_exit()) return; } } // The statement builder should not be used for declarations or expressions. void StatementCfgBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } #define DEFINE_VISIT(type) \ void StatementCfgBuilder::Visit##type(type* expr) { UNREACHABLE(); } EXPRESSION_NODE_LIST(DEFINE_VISIT) #undef DEFINE_VISIT void StatementCfgBuilder::VisitBlock(Block* stmt) { VisitStatements(stmt->statements()); } void StatementCfgBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { ExpressionCfgBuilder builder; builder.Build(stmt->expression(), CfgGlobals::current()->nowhere()); if (builder.graph() == NULL) { BAILOUT("unsupported expression in expression statement"); } graph()->Append(new PositionInstr(stmt->statement_pos())); graph()->Concatenate(builder.graph()); } void StatementCfgBuilder::VisitEmptyStatement(EmptyStatement* stmt) { // Nothing to do. } void StatementCfgBuilder::VisitIfStatement(IfStatement* stmt) { BAILOUT("IfStatement"); } void StatementCfgBuilder::VisitContinueStatement(ContinueStatement* stmt) { BAILOUT("ContinueStatement"); } void StatementCfgBuilder::VisitBreakStatement(BreakStatement* stmt) { BAILOUT("BreakStatement"); } void StatementCfgBuilder::VisitReturnStatement(ReturnStatement* stmt) { ExpressionCfgBuilder builder; builder.Build(stmt->expression(), NULL); if (builder.graph() == NULL) { BAILOUT("unsupported expression in return statement"); } graph()->Append(new PositionInstr(stmt->statement_pos())); graph()->Concatenate(builder.graph()); graph()->AppendReturnInstruction(builder.value()); } void StatementCfgBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { BAILOUT("WithEnterStatement"); } void StatementCfgBuilder::VisitWithExitStatement(WithExitStatement* stmt) { BAILOUT("WithExitStatement"); } void StatementCfgBuilder::VisitSwitchStatement(SwitchStatement* stmt) { BAILOUT("SwitchStatement"); } void StatementCfgBuilder::VisitLoopStatement(LoopStatement* stmt) { BAILOUT("LoopStatement"); } void StatementCfgBuilder::VisitForInStatement(ForInStatement* stmt) { BAILOUT("ForInStatement"); } void StatementCfgBuilder::VisitTryCatch(TryCatch* stmt) { BAILOUT("TryCatch"); } void StatementCfgBuilder::VisitTryFinally(TryFinally* stmt) { BAILOUT("TryFinally"); } void StatementCfgBuilder::VisitDebuggerStatement(DebuggerStatement* stmt) { BAILOUT("DebuggerStatement"); } #ifdef DEBUG // CFG printing support (via depth-first, preorder block traversal). void Cfg::Print() { entry_->Print(); entry_->Unmark(); } void Constant::Print() { PrintF("Constant "); handle_->Print(); } void Nowhere::Print() { PrintF("Nowhere"); } void SlotLocation::Print() { PrintF("Slot "); switch (type_) { case Slot::PARAMETER: PrintF("(PARAMETER, %d)", index_); break; case Slot::LOCAL: PrintF("(LOCAL, %d)", index_); break; default: UNREACHABLE(); } } void TempLocation::Print() { PrintF("Temp %d", number()); } void OneOperandInstruction::Print() { PrintF("("); location()->Print(); PrintF(", "); value_->Print(); PrintF(")"); } void TwoOperandInstruction::Print() { PrintF("("); location()->Print(); PrintF(", "); value0_->Print(); PrintF(", "); value1_->Print(); PrintF(")"); } void MoveInstr::Print() { PrintF("Move "); OneOperandInstruction::Print(); PrintF("\n"); } void PropLoadInstr::Print() { PrintF("PropLoad "); TwoOperandInstruction::Print(); PrintF("\n"); } void BinaryOpInstr::Print() { switch (op()) { case Token::OR: // Two character operand. PrintF("BinaryOp[OR] "); break; case Token::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: // Three character operands. PrintF("BinaryOp[%s] ", Token::Name(op())); break; case Token::COMMA: // Five character operand. PrintF("BinaryOp[COMMA] "); break; case Token::BIT_OR: // Six character operand. PrintF("BinaryOp[BIT_OR] "); break; case Token::BIT_XOR: case Token::BIT_AND: // Seven character operands. PrintF("BinaryOp[%s] ", Token::Name(op())); break; default: UNREACHABLE(); } TwoOperandInstruction::Print(); PrintF("\n"); } void ReturnInstr::Print() { PrintF("Return "); OneOperandInstruction::Print(); PrintF("\n"); } void InstructionBlock::Print() { if (!is_marked_) { is_marked_ = true; PrintF("L%d:\n", number()); for (int i = 0, len = instructions_.length(); i < len; i++) { instructions_[i]->Print(); } PrintF("Goto L%d\n\n", successor_->number()); successor_->Print(); } } void EntryNode::Print() { if (!is_marked_) { is_marked_ = true; successor_->Print(); } } void ExitNode::Print() { if (!is_marked_) { is_marked_ = true; PrintF("L%d:\nExit\n\n", number()); } } #endif // DEBUG } } // namespace v8::internal