diff options
Diffstat (limited to 'V8Binding/v8/src/cfg.cc')
-rw-r--r-- | V8Binding/v8/src/cfg.cc | 741 |
1 files changed, 741 insertions, 0 deletions
diff --git a/V8Binding/v8/src/cfg.cc b/V8Binding/v8/src/cfg.cc new file mode 100644 index 0000000..32f614b --- /dev/null +++ b/V8Binding/v8/src/cfg.cc @@ -0,0 +1,741 @@ +// 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() > kPointerSize) { + BAILOUT("function has too many locals"); + } + if (fun->scope()->num_parameters() > kPointerSize - 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 MoveInstr::FastAllocate(TempLocation* temp) { + ASSERT(temp->where() == TempLocation::NOT_ALLOCATED); + if (temp == value()) { + temp->set_where(TempLocation::ACCUMULATOR); + } else { + temp->set_where(TempLocation::STACK); + } +} + + +void PropLoadInstr::FastAllocate(TempLocation* temp) { + ASSERT(temp->where() == TempLocation::NOT_ALLOCATED); + if (temp == object() || temp == key()) { + temp->set_where(TempLocation::ACCUMULATOR); + } else { + temp->set_where(TempLocation::STACK); + } +} + + +void BinaryOpInstr::FastAllocate(TempLocation* temp) { + ASSERT(temp->where() == TempLocation::NOT_ALLOCATED); + if (temp == left() || temp == right()) { + temp->set_where(TempLocation::ACCUMULATOR); + } else { + temp->set_where(TempLocation::STACK); + } +} + + +void ReturnInstr::FastAllocate(TempLocation* temp) { + ASSERT(temp->where() == TempLocation::NOT_ALLOCATED); + if (temp == value()) { + temp->set_where(TempLocation::ACCUMULATOR); + } else { + temp->set_where(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)"); + } + + 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 propref"); + } + key.Build(expr->key(), NULL); + if (key.graph() == NULL) { + BAILOUT("unsupported key subexpression in propref"); + } + + 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(); + PrintF(")"); +} + + +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 MoveInstr::Print() { + PrintF("Move("); + location()->Print(); + PrintF(", "); + value_->Print(); + PrintF(")\n"); +} + + +void PropLoadInstr::Print() { + PrintF("PropLoad("); + location()->Print(); + PrintF(", "); + object()->Print(); + PrintF(", "); + key()->Print(); + PrintF(")\n"); +} + + +void BinaryOpInstr::Print() { + PrintF("BinaryOp("); + location()->Print(); + PrintF(", %s, ", Token::Name(op())); + left()->Print(); + PrintF(", "); + right()->Print(); + PrintF(")\n"); +} + + +void ReturnInstr::Print() { + PrintF("Return("); + value_->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 |