| // 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 |