Index: src/cfg.cc |
=================================================================== |
--- src/cfg.cc (revision 0) |
+++ src/cfg.cc (revision 0) |
@@ -0,0 +1,462 @@ |
+// 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 { |
+ |
+ |
+ExitNode* Cfg::global_exit_ = NULL; |
+ |
+ |
+void CfgNode::Reset() { |
+#ifdef DEBUG |
+ node_counter_ = 0; |
+#endif |
+} |
+ |
+ |
+void Cfg::Reset(FunctionLiteral* fun) { |
+ global_exit_ = new ExitNode(fun); |
+ CfgNode::Reset(); |
+} |
+ |
+ |
+#define BAILOUT(reason) \ |
+ do { return NULL; } while (false) |
+ |
+Cfg* Cfg::Build(FunctionLiteral* fun) { |
+ ZoneList<Statement*>* body = fun->body(); |
+ if (body->is_empty()) { |
+ BAILOUT("empty function body"); |
+ } |
+ |
+ Cfg::Reset(fun); |
+ StatementBuilder builder; |
+ builder.VisitStatements(body); |
+ Cfg* cfg = builder.cfg(); |
+ if (cfg == NULL) { |
+ BAILOUT("unsupported statement type"); |
+ } |
+ |
+ ASSERT(!cfg->has_exit()); // Return on all paths. |
+ cfg->PrependEntryNode(fun); |
+ return cfg; |
+} |
+ |
+#undef BAILOUT |
+ |
+ |
+void Cfg::PrependEntryNode(FunctionLiteral* fun) { |
+ ASSERT(!is_empty()); |
+ entry_ = new EntryNode(fun, InstructionBlock::cast(entry())); |
+} |
+ |
+ |
+void Cfg::Append(Instruction* instr) { |
+ ASSERT(has_exit()); |
+ ASSERT(!is_empty()); |
+ InstructionBlock::cast(exit_)->Append(instr); |
+} |
+ |
+ |
+void Cfg::AppendReturnInstruction(Value* value) { |
+ Append(new ReturnInstr(value)); |
+ InstructionBlock::cast(exit_)->set_successor(global_exit_); |
+ exit_ = NULL; |
+} |
+ |
+ |
+EntryNode::EntryNode(FunctionLiteral* fun, InstructionBlock* succ) |
+ : successor_(succ), local_count_(fun->scope()->num_stack_slots()) { |
+} |
+ |
+ |
+ExitNode::ExitNode(FunctionLiteral* fun) |
+ : parameter_count_(fun->scope()->num_parameters()) { |
+} |
+ |
+ |
+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() {} |
+ |
+ |
+Handle<Code> Cfg::Compile(FunctionLiteral* fun, Handle<Script> script) { |
+ const int kInitialBufferSize = 4 * KB; |
+ MacroAssembler* masm = new MacroAssembler(NULL, kInitialBufferSize); |
+ entry()->Compile(masm); |
+ entry()->Unmark(); |
+ CodeDesc desc; |
+ masm->GetCode(&desc); |
+ 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; |
+} |
+ |
+ |
+// The expression builder should not be used for declarations or statements. |
+void ExpressionBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } |
+ |
+#define DEFINE_VISIT(type) \ |
+ void ExpressionBuilder::Visit##type(type* stmt) { UNREACHABLE(); } |
+STATEMENT_NODE_LIST(DEFINE_VISIT) |
+#undef DEFINE_VISIT |
+ |
+ |
+// Macros (temporarily) handling unsupported expression types. |
+#define BAILOUT(reason) \ |
+ do { \ |
+ value_ = NULL; \ |
+ return; \ |
+ } while (false) |
+ |
+#define CHECK_BAILOUT() \ |
+ if (value_ == NULL) { return; } else {} |
+ |
+void ExpressionBuilder::VisitFunctionLiteral(FunctionLiteral* expr) { |
+ BAILOUT("FunctionLiteral"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitFunctionBoilerplateLiteral( |
+ FunctionBoilerplateLiteral* expr) { |
+ BAILOUT("FunctionBoilerplateLiteral"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitConditional(Conditional* expr) { |
+ BAILOUT("Conditional"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitSlot(Slot* expr) { |
+ BAILOUT("Slot"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitVariableProxy(VariableProxy* expr) { |
+ BAILOUT("VariableProxy"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitLiteral(Literal* expr) { |
+ value_ = new Constant(expr->handle()); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitRegExpLiteral(RegExpLiteral* expr) { |
+ BAILOUT("RegExpLiteral"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitObjectLiteral(ObjectLiteral* expr) { |
+ BAILOUT("ObjectLiteral"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitArrayLiteral(ArrayLiteral* expr) { |
+ BAILOUT("ArrayLiteral"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { |
+ BAILOUT("CatchExtensionObject"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitAssignment(Assignment* expr) { |
+ BAILOUT("Assignment"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitThrow(Throw* expr) { |
+ BAILOUT("Throw"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitProperty(Property* expr) { |
+ BAILOUT("Property"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitCall(Call* expr) { |
+ BAILOUT("Call"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitCallEval(CallEval* expr) { |
+ BAILOUT("CallEval"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitCallNew(CallNew* expr) { |
+ BAILOUT("CallNew"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitCallRuntime(CallRuntime* expr) { |
+ BAILOUT("CallRuntime"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitUnaryOperation(UnaryOperation* expr) { |
+ BAILOUT("UnaryOperation"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitCountOperation(CountOperation* expr) { |
+ BAILOUT("CountOperation"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitBinaryOperation(BinaryOperation* expr) { |
+ BAILOUT("BinaryOperation"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitCompareOperation(CompareOperation* expr) { |
+ BAILOUT("CompareOperation"); |
+} |
+ |
+ |
+void ExpressionBuilder::VisitThisFunction(ThisFunction* expr) { |
+ BAILOUT("ThisFunction"); |
+} |
+ |
+#undef BAILOUT |
+#undef CHECK_BAILOUT |
+ |
+ |
+// Macros (temporarily) handling unsupported statement types. |
+#define BAILOUT(reason) \ |
+ do { \ |
+ cfg_ = NULL; \ |
+ return; \ |
+ } while (false) |
+ |
+#define CHECK_BAILOUT() \ |
+ if (cfg_ == NULL) { return; } else {} |
+ |
+void StatementBuilder::VisitStatements(ZoneList<Statement*>* stmts) { |
+ for (int i = 0, len = stmts->length(); i < len; i++) { |
+ Visit(stmts->at(i)); |
+ CHECK_BAILOUT(); |
+ if (!cfg_->has_exit()) return; |
+ } |
+} |
+ |
+ |
+// The statement builder should not be used for declarations or expressions. |
+void StatementBuilder::VisitDeclaration(Declaration* decl) { UNREACHABLE(); } |
+ |
+#define DEFINE_VISIT(type) \ |
+ void StatementBuilder::Visit##type(type* expr) { UNREACHABLE(); } |
+EXPRESSION_NODE_LIST(DEFINE_VISIT) |
+#undef DEFINE_VISIT |
+ |
+ |
+void StatementBuilder::VisitBlock(Block* stmt) { |
+ VisitStatements(stmt->statements()); |
+} |
+ |
+ |
+void StatementBuilder::VisitExpressionStatement(ExpressionStatement* stmt) { |
+ BAILOUT("ExpressionStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitEmptyStatement(EmptyStatement* stmt) { |
+ // Nothing to do. |
+} |
+ |
+ |
+void StatementBuilder::VisitIfStatement(IfStatement* stmt) { |
+ BAILOUT("IfStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitContinueStatement(ContinueStatement* stmt) { |
+ BAILOUT("ContinueStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitBreakStatement(BreakStatement* stmt) { |
+ BAILOUT("BreakStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitReturnStatement(ReturnStatement* stmt) { |
+ ExpressionBuilder builder; |
+ builder.Visit(stmt->expression()); |
+ Value* value = builder.value(); |
+ if (value == NULL) BAILOUT("unsupported expression type"); |
+ cfg_->AppendReturnInstruction(value); |
+} |
+ |
+ |
+void StatementBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { |
+ BAILOUT("WithEnterStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitWithExitStatement(WithExitStatement* stmt) { |
+ BAILOUT("WithExitStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitSwitchStatement(SwitchStatement* stmt) { |
+ BAILOUT("SwitchStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitLoopStatement(LoopStatement* stmt) { |
+ BAILOUT("LoopStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitForInStatement(ForInStatement* stmt) { |
+ BAILOUT("ForInStatement"); |
+} |
+ |
+ |
+void StatementBuilder::VisitTryCatch(TryCatch* stmt) { |
+ BAILOUT("TryCatch"); |
+} |
+ |
+ |
+void StatementBuilder::VisitTryFinally(TryFinally* stmt) { |
+ BAILOUT("TryFinally"); |
+} |
+ |
+ |
+void StatementBuilder::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() { |
+ handle_->Print(); |
+} |
+ |
+ |
+void ReturnInstr::Print() { |
+ PrintF("Return "); |
+ value_->Print(); |
+ PrintF("\n"); |
+} |
+ |
+ |
+int CfgNode::node_counter_ = 0; |
+ |
+ |
+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 |