Index: src/rewriter.cc |
=================================================================== |
--- src/rewriter.cc (revision 629) |
+++ src/rewriter.cc (working copy) |
@@ -34,6 +34,441 @@ |
namespace v8 { namespace internal { |
+class AstOptimizer: public Visitor { |
+ public: |
+ explicit AstOptimizer() { |
+ } |
+ |
+ void Optimize(ZoneList<Statement*>* statements); |
+ |
+ private: |
+ // Helpers |
+ void OptimizeArguments(ZoneList<Expression*>* arguments); |
+ |
+ // Node visitors. |
+#define DEF_VISIT(type) \ |
+ virtual void Visit##type(type* node); |
+ NODE_LIST(DEF_VISIT) |
+#undef DEF_VISIT |
+ |
+ DISALLOW_COPY_AND_ASSIGN(AstOptimizer); |
+}; |
+ |
+ |
+void AstOptimizer::Optimize(ZoneList<Statement*>* statements) { |
+ int len = statements->length(); |
+ for (int i = 0; i < len; i++) { |
+ Visit(statements->at(i)); |
+ } |
+} |
+ |
+ |
+void AstOptimizer::OptimizeArguments(ZoneList<Expression*>* arguments) { |
+ for (int i = 0; i < arguments->length(); i++) { |
+ Visit(arguments->at(i)); |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitBlock(Block* node) { |
+ Optimize(node->statements()); |
+} |
+ |
+ |
+void AstOptimizer::VisitExpressionStatement(ExpressionStatement* node) { |
+ Visit(node->expression()); |
+} |
+ |
+ |
+void AstOptimizer::VisitIfStatement(IfStatement* node) { |
+ Visit(node->condition()); |
+ Visit(node->then_statement()); |
+ if (node->HasElseStatement()) { |
+ Visit(node->else_statement()); |
+ } |
+} |
+ |
+ |
+ |
+ |
+void AstOptimizer::VisitLoopStatement(LoopStatement* node) { |
+ if (node->init() != NULL) { |
+ Visit(node->init()); |
+ } |
+ if (node->cond() != NULL) { |
+ Visit(node->cond()); |
+ } |
+ if (node->body() != NULL) { |
+ Visit(node->body()); |
+ } |
+ if (node->next() != NULL) { |
+ Visit(node->next()); |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitForInStatement(ForInStatement* node) { |
+ Visit(node->each()); |
+ Visit(node->enumerable()); |
+ Visit(node->body()); |
+} |
+ |
+ |
+void AstOptimizer::VisitTryCatch(TryCatch* node) { |
+ Visit(node->try_block()); |
+ Visit(node->catch_var()); |
+ Visit(node->catch_block()); |
+} |
+ |
+ |
+void AstOptimizer::VisitTryFinally(TryFinally* node) { |
+ Visit(node->try_block()); |
+ Visit(node->finally_block()); |
+} |
+ |
+ |
+void AstOptimizer::VisitSwitchStatement(SwitchStatement* node) { |
+ Visit(node->tag()); |
+ for (int i = 0; i < node->cases()->length(); i++) { |
+ CaseClause* clause = node->cases()->at(i); |
+ if (!clause->is_default()) { |
+ Visit(clause->label()); |
+ } |
+ Optimize(clause->statements()); |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitContinueStatement(ContinueStatement* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitBreakStatement(BreakStatement* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitDeclaration(Declaration* node) { |
+ // Will not be reached by the current optimizations. |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitEmptyStatement(EmptyStatement* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitReturnStatement(ReturnStatement* node) { |
+ Visit(node->expression()); |
+} |
+ |
+ |
+void AstOptimizer::VisitWithEnterStatement(WithEnterStatement* node) { |
+ Visit(node->expression()); |
+} |
+ |
+ |
+void AstOptimizer::VisitWithExitStatement(WithExitStatement* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitDebuggerStatement(DebuggerStatement* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitFunctionLiteral(FunctionLiteral* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitFunctionBoilerplateLiteral( |
+ FunctionBoilerplateLiteral* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitConditional(Conditional* node) { |
+ Visit(node->condition()); |
+ Visit(node->then_expression()); |
+ Visit(node->else_expression()); |
+} |
+ |
+ |
+void AstOptimizer::VisitSlot(Slot* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitVariableProxy(VariableProxy* node) { |
+ Variable* var = node->AsVariable(); |
+ if (var != NULL) { |
+ if (var->type()->IsKnown()) { |
+ node->type()->CopyFrom(var->type()); |
+ } else if (node->type()->IsLikelySmi()) { |
+ var->type()->SetAsLikelySmi(); |
+ } |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitLiteral(Literal* node) { |
+ Handle<Object> literal = node->handle(); |
+ if (literal->IsSmi()) { |
+ node->type()->SetAsLikelySmi(); |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitRegExpLiteral(RegExpLiteral* node) { |
+ USE(node); |
+} |
+ |
+ |
+void AstOptimizer::VisitArrayLiteral(ArrayLiteral* node) { |
+ for (int i = 0; i < node->values()->length(); i++) { |
+ Visit(node->values()->at(i)); |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitObjectLiteral(ObjectLiteral* node) { |
+ for (int i = 0; i < node->properties()->length(); i++) { |
+ Visit(node->properties()->at(i)->key()); |
+ Visit(node->properties()->at(i)->value()); |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitAssignment(Assignment* node) { |
+ switch (node->op()) { |
+ case Token::INIT_VAR: |
+ case Token::INIT_CONST: |
+ case Token::ASSIGN: |
+ // No type can be infered from the general assignment. |
+ break; |
+ case Token::ASSIGN_BIT_OR: |
+ case Token::ASSIGN_BIT_XOR: |
+ case Token::ASSIGN_BIT_AND: |
+ case Token::ASSIGN_SHL: |
+ case Token::ASSIGN_SAR: |
+ case Token::ASSIGN_SHR: |
+ node->type()->SetAsLikelySmiIfUnknown(); |
+ node->target()->type()->SetAsLikelySmiIfUnknown(); |
+ node->value()->type()->SetAsLikelySmiIfUnknown(); |
+ break; |
+ case Token::ASSIGN_ADD: |
+ case Token::ASSIGN_SUB: |
+ case Token::ASSIGN_MUL: |
+ case Token::ASSIGN_DIV: |
+ case Token::ASSIGN_MOD: |
+ if (node->type()->IsLikelySmi()) { |
+ node->target()->type()->SetAsLikelySmiIfUnknown(); |
+ node->value()->type()->SetAsLikelySmiIfUnknown(); |
+ } |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
+ |
+ Visit(node->target()); |
+ Visit(node->value()); |
+ |
+ switch (node->op()) { |
+ case Token::INIT_VAR: |
+ case Token::INIT_CONST: |
+ case Token::ASSIGN: |
+ // Pure assigment copies the type from the value. |
+ node->type()->CopyFrom(node->value()->type()); |
+ break; |
+ case Token::ASSIGN_BIT_OR: |
+ case Token::ASSIGN_BIT_XOR: |
+ case Token::ASSIGN_BIT_AND: |
+ case Token::ASSIGN_SHL: |
+ case Token::ASSIGN_SAR: |
+ case Token::ASSIGN_SHR: |
+ // Should have been setup above already. |
+ break; |
+ case Token::ASSIGN_ADD: |
+ case Token::ASSIGN_SUB: |
+ case Token::ASSIGN_MUL: |
+ case Token::ASSIGN_DIV: |
+ case Token::ASSIGN_MOD: |
+ if (node->type()->IsUnknown()) { |
+ if (node->target()->type()->IsLikelySmi() || |
+ node->value()->type()->IsLikelySmi()) { |
+ node->type()->SetAsLikelySmi(); |
+ } |
+ } |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
+ |
+ // Since this is an assignment. We have to propagate this node's type to the |
+ // variable. |
+ VariableProxy* proxy = node->target()->AsVariableProxy(); |
+ if (proxy != NULL) { |
+ Variable* var = proxy->AsVariable(); |
+ if (var != NULL) { |
+ StaticType* var_type = var->type(); |
+ if (var_type->IsUnknown()) { |
+ var_type->CopyFrom(node->type()); |
+ } else if (var_type->IsLikelySmi()) { |
+ // We do not reset likely types to Unknown. |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitThrow(Throw* node) { |
+ Visit(node->exception()); |
+} |
+ |
+ |
+void AstOptimizer::VisitProperty(Property* node) { |
+ Visit(node->obj()); |
+ Visit(node->key()); |
+} |
+ |
+ |
+void AstOptimizer::VisitCall(Call* node) { |
+ Visit(node->expression()); |
+ OptimizeArguments(node->arguments()); |
+} |
+ |
+ |
+void AstOptimizer::VisitCallNew(CallNew* node) { |
+ Visit(node->expression()); |
+ OptimizeArguments(node->arguments()); |
+} |
+ |
+ |
+void AstOptimizer::VisitCallRuntime(CallRuntime* node) { |
+ OptimizeArguments(node->arguments()); |
+} |
+ |
+ |
+void AstOptimizer::VisitUnaryOperation(UnaryOperation* node) { |
+ Visit(node->expression()); |
+} |
+ |
+ |
+void AstOptimizer::VisitCountOperation(CountOperation* node) { |
+ // Count operations assume that they work on Smis. |
+ node->type()->SetAsLikelySmiIfUnknown(); |
+ node->expression()->type()->SetAsLikelySmiIfUnknown(); |
+ Visit(node->expression()); |
+} |
+ |
+ |
+void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) { |
+ // Depending on the operation we can propagate this node's type down the |
+ // AST nodes. |
+ switch (node->op()) { |
+ case Token::COMMA: |
+ case Token::OR: |
+ case Token::AND: |
+ break; |
+ case Token::BIT_OR: |
+ case Token::BIT_XOR: |
+ case Token::BIT_AND: |
+ case Token::SHL: |
+ case Token::SAR: |
+ case Token::SHR: |
+ node->type()->SetAsLikelySmiIfUnknown(); |
+ node->left()->type()->SetAsLikelySmiIfUnknown(); |
+ node->right()->type()->SetAsLikelySmiIfUnknown(); |
+ break; |
+ case Token::ADD: |
+ case Token::SUB: |
+ case Token::MUL: |
+ case Token::DIV: |
+ case Token::MOD: |
+ if (node->type()->IsLikelySmi()) { |
+ node->left()->type()->SetAsLikelySmiIfUnknown(); |
+ node->right()->type()->SetAsLikelySmiIfUnknown(); |
+ } |
+ break; |
+ default: |
+ UNREACHABLE(); |
+ break; |
+ } |
+ |
+ Visit(node->left()); |
+ Visit(node->right()); |
+ |
+ // After visiting the operand nodes we have to check if this node's type |
+ // can be updated. If it does, then we can push that information down |
+ // towards the leafs again if the new information is an upgrade over the |
+ // previous type of the operand nodes. |
+ if (node->type()->IsUnknown()) { |
+ if (node->left()->type()->IsLikelySmi() || |
+ node->right()->type()->IsLikelySmi()) { |
+ node->type()->SetAsLikelySmi(); |
+ } |
+ if (node->type()->IsLikelySmi()) { |
+ // The type of this node changed to LIKELY_SMI. Propagate this knowlege |
+ // down through the nodes. |
+ if (node->left()->type()->IsUnknown()) { |
+ node->left()->type()->SetAsLikelySmi(); |
+ Visit(node->left()); |
+ } |
+ if (node->right()->type()->IsUnknown()) { |
+ node->right()->type()->SetAsLikelySmi(); |
+ Visit(node->right()); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitCompareOperation(CompareOperation* node) { |
+ if (node->type()->IsKnown()) { |
+ // Propagate useful information down towards the leafs. |
+ node->left()->type()->SetAsLikelySmiIfUnknown(); |
+ node->right()->type()->SetAsLikelySmiIfUnknown(); |
+ } |
+ |
+ Visit(node->left()); |
+ Visit(node->right()); |
+ |
+ // After visiting the operand nodes we have to check if this node's type |
+ // can be updated. If it does, then we can push that information down |
+ // towards the leafs again if the new information is an upgrade over the |
+ // previous type of the operand nodes. |
+ if (node->type()->IsUnknown()) { |
+ if (node->left()->type()->IsLikelySmi() || |
+ node->right()->type()->IsLikelySmi()) { |
+ node->type()->SetAsLikelySmi(); |
+ } |
+ if (node->type()->IsLikelySmi()) { |
+ // The type of this node changed to LIKELY_SMI. Propagate this knowlege |
+ // down through the nodes. |
+ if (node->left()->type()->IsUnknown()) { |
+ node->left()->type()->SetAsLikelySmi(); |
+ Visit(node->left()); |
+ } |
+ if (node->right()->type()->IsUnknown()) { |
+ node->right()->type()->SetAsLikelySmi(); |
+ Visit(node->right()); |
+ } |
+ } |
+ } |
+} |
+ |
+ |
+void AstOptimizer::VisitThisFunction(ThisFunction* node) { |
+ USE(node); |
+} |
+ |
+ |
class Processor: public Visitor { |
public: |
explicit Processor(VariableProxy* result) |
@@ -326,4 +761,18 @@ |
} |
+void Rewriter::Optimize(FunctionLiteral* function) { |
+ ZoneList<Statement*>* body = function->body(); |
+ if (body->is_empty()) return; |
+ |
+ if (FLAG_optimize_ast) { |
+ Scope* scope = function->scope(); |
+ if (!scope->is_global_scope()) { |
+ AstOptimizer optimizer; |
+ optimizer.Optimize(body); |
+ } |
+ } |
+} |
+ |
+ |
} } // namespace v8::internal |