Revision: 4087
Author: [email protected]
Date: Wed Mar 10 09:19:22 2010
Log: Add an assigned variables analysis.

This change adds a pass over the AST that computes the
set of assigned variables for locals and parameters for each expression.

The result of this analysis is used to for two purposes:
1. Recognize variables that are trivial subexpressions. A left sub-expression
   of a binary operation is trivial if it is a local variable or a parameter
   and it is not assigned in the right sub-expression. In the case of a
   trivial left sub-expression we evaluate the right first.
   Currently only binary operations and compare operations are considered
   when finding trivial left sub-expressions.

2. Recogize certain simple for-loops with a constant trip count where the loop
   variable is always within smi range. If the loop count variable is not
   assigned in the body of the loop (except in the update expression the
   for-loop). This allows omitting smi checks on operation using the loop
   count variable.

Review URL: http://codereview.chromium.org/669155
http://code.google.com/p/v8/source/detail?r=4087

Modified:
 /branches/bleeding_edge/src/ast.cc
 /branches/bleeding_edge/src/ast.h
 /branches/bleeding_edge/src/compiler.cc
 /branches/bleeding_edge/src/data-flow.cc
 /branches/bleeding_edge/src/data-flow.h
 /branches/bleeding_edge/src/ia32/codegen-ia32.cc
 /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc
 /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h
 /branches/bleeding_edge/src/ia32/virtual-frame-ia32.cc
 /branches/bleeding_edge/src/ia32/virtual-frame-ia32.h
 /branches/bleeding_edge/src/scopes.h
 /branches/bleeding_edge/src/virtual-frame-inl.h

=======================================
--- /branches/bleeding_edge/src/ast.cc  Mon Mar  8 05:01:24 2010
+++ /branches/bleeding_edge/src/ast.cc  Wed Mar 10 09:19:22 2010
@@ -58,13 +58,27 @@
// ----------------------------------------------------------------------------
 // Implementation of other node functionality.

+Assignment* ExpressionStatement::StatementAsSimpleAssignment() {
+  return (expression()->AsAssignment() != NULL &&
+          !expression()->AsAssignment()->is_compound())
+      ? expression()->AsAssignment()
+      : NULL;
+}
+
+
+CountOperation* ExpressionStatement::StatementAsCountOperation() {
+  return expression()->AsCountOperation();
+}
+
+
 VariableProxy::VariableProxy(Handle<String> name,
                              bool is_this,
                              bool inside_with)
   : name_(name),
     var_(NULL),
     is_this_(is_this),
-    inside_with_(inside_with) {
+    inside_with_(inside_with),
+    is_trivial_(false) {
   // names must be canonicalized for fast equality checks
   ASSERT(name->IsSymbol());
 }
=======================================
--- /branches/bleeding_edge/src/ast.h   Tue Mar  9 02:39:18 2010
+++ /branches/bleeding_edge/src/ast.h   Wed Mar 10 09:19:22 2010
@@ -137,6 +137,7 @@
   virtual BreakableStatement* AsBreakableStatement() { return NULL; }
   virtual IterationStatement* AsIterationStatement() { return NULL; }
   virtual UnaryOperation* AsUnaryOperation() { return NULL; }
+  virtual CountOperation* AsCountOperation() { return NULL; }
   virtual BinaryOperation* AsBinaryOperation() { return NULL; }
   virtual Assignment* AsAssignment() { return NULL; }
   virtual FunctionLiteral* AsFunctionLiteral() { return NULL; }
@@ -160,6 +161,9 @@

   virtual Statement* AsStatement()  { return this; }
   virtual ReturnStatement* AsReturnStatement() { return NULL; }
+
+  virtual Assignment* StatementAsSimpleAssignment() { return NULL; }
+  virtual CountOperation* StatementAsCountOperation() { return NULL; }

   bool IsEmpty() { return AsEmptyStatement() != NULL; }

@@ -321,6 +325,16 @@

   virtual void Accept(AstVisitor* v);

+  virtual Assignment* StatementAsSimpleAssignment() {
+    if (statements_.length() != 1) return NULL;
+    return statements_[0]->StatementAsSimpleAssignment();
+  }
+
+  virtual CountOperation* StatementAsCountOperation() {
+    if (statements_.length() != 1) return NULL;
+    return statements_[0]->StatementAsCountOperation();
+  }
+
   void AddStatement(Statement* statement) { statements_.Add(statement); }

   ZoneList<Statement*>* statements() { return &statements_; }
@@ -442,8 +456,8 @@
         init_(NULL),
         cond_(NULL),
         next_(NULL),
-        may_have_function_literal_(true) {
-  }
+        may_have_function_literal_(true),
+        loop_variable_(NULL) {}

   void Initialize(Statement* init,
                   Expression* cond,
@@ -463,6 +477,10 @@
   bool may_have_function_literal() const {
     return may_have_function_literal_;
   }
+
+  bool is_fast_smi_loop() { return loop_variable_ != NULL; }
+  Variable* loop_variable() { return loop_variable_; }
+  void set_loop_variable(Variable* var) { loop_variable_ = var; }

  private:
   Statement* init_;
@@ -470,6 +488,7 @@
   Statement* next_;
   // True if there is a function literal subexpression in the condition.
   bool may_have_function_literal_;
+  Variable* loop_variable_;

   friend class AstOptimizer;
 };
@@ -506,6 +525,9 @@

   // Type testing & conversion.
   virtual ExpressionStatement* AsExpressionStatement() { return this; }
+
+  virtual Assignment* StatementAsSimpleAssignment();
+  virtual CountOperation* StatementAsCountOperation();

   void set_expression(Expression* e) { expression_ = e; }
   Expression* expression() { return expression_; }
@@ -964,7 +986,7 @@

   // Reading from a mutable variable is a side effect, but 'this' is
   // immutable.
-  virtual bool IsTrivial() { return is_this(); }
+  virtual bool IsTrivial() { return is_trivial_; }

   bool IsVariable(Handle<String> n) {
     return !is_this() && name().is_identical_to(n);
@@ -979,6 +1001,8 @@
   Variable* var() const  { return var_; }
   bool is_this() const  { return is_this_; }
   bool inside_with() const  { return inside_with_; }
+  bool is_trivial() { return is_trivial_; }
+  void set_is_trivial(bool b) { is_trivial_ = b; }

   // Bind this proxy to the variable var.
   void BindTo(Variable* var);
@@ -988,6 +1012,7 @@
   Variable* var_;  // resolved variable, or NULL
   bool is_this_;
   bool inside_with_;
+  bool is_trivial_;

   VariableProxy(Handle<String> name, bool is_this, bool inside_with);
   explicit VariableProxy(bool is_this);
@@ -1245,6 +1270,8 @@
   }

   virtual void Accept(AstVisitor* v);
+
+  virtual CountOperation* AsCountOperation() { return this; }

   bool is_prefix() const { return is_prefix_; }
   bool is_postfix() const { return !is_prefix_; }
@@ -1323,6 +1350,8 @@

   virtual void Accept(AstVisitor* v);
   virtual Assignment* AsAssignment() { return this; }
+
+  Assignment* AsSimpleAssignment() { return !is_compound() ? this : NULL; }

   Token::Value binary_op() const;

=======================================
--- /branches/bleeding_edge/src/compiler.cc     Wed Mar 10 09:02:25 2010
+++ /branches/bleeding_edge/src/compiler.cc     Wed Mar 10 09:19:22 2010
@@ -78,6 +78,15 @@
     // overflow exception will be thrown by the caller.
     return Handle<Code>::null();
   }
+
+  if (function->scope()->num_parameters() > 0 ||
+      function->scope()->num_stack_slots()) {
+    AssignedVariablesAnalyzer ava(function);
+    ava.Analyze();
+    if (ava.HasStackOverflow()) {
+      return Handle<Code>::null();
+    }
+  }

   if (FLAG_use_flow_graph) {
     FlowGraphBuilder builder;
@@ -462,6 +471,15 @@
     if (!Rewriter::Optimize(literal)) {
       return Handle<JSFunction>::null();
     }
+
+    if (literal->scope()->num_parameters() > 0 ||
+        literal->scope()->num_stack_slots()) {
+      AssignedVariablesAnalyzer ava(literal);
+      ava.Analyze();
+      if (ava.HasStackOverflow()) {
+        return Handle<JSFunction>::null();
+      }
+    }

     if (FLAG_use_flow_graph) {
       FlowGraphBuilder builder;
=======================================
--- /branches/bleeding_edge/src/data-flow.cc    Wed Mar 10 09:02:25 2010
+++ /branches/bleeding_edge/src/data-flow.cc    Wed Mar 10 09:19:22 2010
@@ -28,6 +28,7 @@
 #include "v8.h"

 #include "data-flow.h"
+#include "scopes.h"

 namespace v8 {
 namespace internal {
@@ -1033,6 +1034,445 @@
 void LivenessAnalyzer::VisitThisFunction(ThisFunction* expr) {
   UNREACHABLE();
 }
+
+
+AssignedVariablesAnalyzer::AssignedVariablesAnalyzer(FunctionLiteral* fun)
+    : fun_(fun),
+ av_(fun->scope()->num_parameters() + fun->scope()->num_stack_slots()) {}
+
+
+void AssignedVariablesAnalyzer::Analyze() {
+  ASSERT(av_.length() > 0);
+  VisitStatements(fun_->body());
+}
+
+
+Variable* AssignedVariablesAnalyzer::FindSmiLoopVariable(ForStatement* stmt) {
+  // The loop must have all necessary parts.
+ if (stmt->init() == NULL || stmt->cond() == NULL || stmt->next() == NULL) {
+    return NULL;
+  }
+  // The initialization statement has to be a simple assignment.
+  Assignment* init = stmt->init()->StatementAsSimpleAssignment();
+  if (init == NULL) return NULL;
+
+  // We only deal with local variables.
+  Variable* loop_var = init->target()->AsVariableProxy()->AsVariable();
+  if (!loop_var->IsStackAllocated()) return NULL;
+
+  // The initial value has to be a smi.
+  Literal* init_lit = init->value()->AsLiteral();
+  if (init_lit == NULL || !init_lit->handle()->IsSmi()) return NULL;
+  int init_value = Smi::cast(*init_lit->handle())->value();
+
+  // The condition must be a compare of variable with <, <=, >, or >=.
+  CompareOperation* cond = stmt->cond()->AsCompareOperation();
+  if (cond == NULL) return NULL;
+  if (cond->op() != Token::LT
+      && cond->op() != Token::LTE
+      && cond->op() != Token::GT
+      && cond->op() != Token::GTE) return NULL;
+
+  // The lhs must be the same variable as in the init expression.
+ if (cond->left()->AsVariableProxy()->AsVariable() != loop_var) return NULL;
+
+  // The rhs must be a smi.
+  Literal* term_lit = cond->right()->AsLiteral();
+  if (term_lit == NULL || !term_lit->handle()->IsSmi()) return NULL;
+  int term_value = Smi::cast(*term_lit->handle())->value();
+
+ // The count operation updates the same variable as in the init expression.
+  CountOperation* update = stmt->next()->StatementAsCountOperation();
+  if (update == NULL) return NULL;
+  if (update->expression()->AsVariableProxy()->AsVariable() != loop_var) {
+    return NULL;
+  }
+
+ // The direction of the count operation must agree with the start and the end + // value. We currently do not allow the initial value to be the same as the + // terminal value. This _would_ be ok as long as the loop body never executes
+  // or executes exactly one time.
+  if (init_value == term_value) return NULL;
+  if (init_value < term_value && update->op() != Token::INC) return NULL;
+  if (init_value > term_value && update->op() != Token::DEC) return NULL;
+
+  // Found a smi loop variable.
+  return loop_var;
+}
+
+int AssignedVariablesAnalyzer::BitIndex(Variable* var) {
+  ASSERT(var != NULL);
+  ASSERT(var->IsStackAllocated());
+  Slot* slot = var->slot();
+  if (slot->type() == Slot::PARAMETER) {
+    return slot->index();
+  } else {
+    return fun_->scope()->num_parameters() + slot->index();
+  }
+}
+
+
+void AssignedVariablesAnalyzer::RecordAssignedVar(Variable* var) {
+  ASSERT(var != NULL);
+  if (var->IsStackAllocated()) {
+    av_.Add(BitIndex(var));
+  }
+}
+
+
+void AssignedVariablesAnalyzer::MarkIfTrivial(Expression* expr) {
+  Variable* var = expr->AsVariableProxy()->AsVariable();
+  if (var != NULL &&
+      var->IsStackAllocated() &&
+      (var->is_this() || !av_.Contains(BitIndex(var)))) {
+    expr->AsVariableProxy()->set_is_trivial(true);
+  }
+}
+
+
+void AssignedVariablesAnalyzer::ProcessExpression(Expression* expr) {
+  BitVector saved_av(av_);
+  av_.Clear();
+  Visit(expr);
+  av_.Union(saved_av);
+}
+
+void AssignedVariablesAnalyzer::VisitBlock(Block* stmt) {
+  VisitStatements(stmt->statements());
+}
+
+
+void AssignedVariablesAnalyzer::VisitExpressionStatement(
+    ExpressionStatement* stmt) {
+  ProcessExpression(stmt->expression());
+}
+
+
+void AssignedVariablesAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) {
+  // Do nothing.
+}
+
+
+void AssignedVariablesAnalyzer::VisitIfStatement(IfStatement* stmt) {
+  ProcessExpression(stmt->condition());
+  Visit(stmt->then_statement());
+  Visit(stmt->else_statement());
+}
+
+
+void AssignedVariablesAnalyzer::VisitContinueStatement(
+    ContinueStatement* stmt) {
+  // Nothing to do.
+}
+
+
+void AssignedVariablesAnalyzer::VisitBreakStatement(BreakStatement* stmt) {
+  // Nothing to do.
+}
+
+
+void AssignedVariablesAnalyzer::VisitReturnStatement(ReturnStatement* stmt) {
+  ProcessExpression(stmt->expression());
+}
+
+
+void AssignedVariablesAnalyzer::VisitWithEnterStatement(
+    WithEnterStatement* stmt) {
+  ProcessExpression(stmt->expression());
+}
+
+
+void AssignedVariablesAnalyzer::VisitWithExitStatement(
+    WithExitStatement* stmt) {
+  // Nothing to do.
+}
+
+
+void AssignedVariablesAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) {
+  BitVector result(av_);
+  av_.Clear();
+  Visit(stmt->tag());
+  result.Union(av_);
+  for (int i = 0; i < stmt->cases()->length(); i++) {
+    CaseClause* clause = stmt->cases()->at(i);
+    if (!clause->is_default()) {
+      av_.Clear();
+      Visit(clause->label());
+      result.Union(av_);
+    }
+    VisitStatements(clause->statements());
+  }
+  av_.Union(result);
+}
+
+
+void AssignedVariablesAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) {
+  ProcessExpression(stmt->cond());
+  Visit(stmt->body());
+}
+
+
+void AssignedVariablesAnalyzer::VisitWhileStatement(WhileStatement* stmt) {
+  ProcessExpression(stmt->cond());
+  Visit(stmt->body());
+}
+
+
+void AssignedVariablesAnalyzer::VisitForStatement(ForStatement* stmt) {
+  if (stmt->init() != NULL) Visit(stmt->init());
+
+  if (stmt->cond() != NULL) ProcessExpression(stmt->cond());
+
+  if (stmt->next() != NULL) Visit(stmt->next());
+
+  // Process loop body. After visiting the loop body av_ contains
+  // the assigned variables of the loop body.
+  BitVector saved_av(av_);
+  av_.Clear();
+  Visit(stmt->body());
+
+  Variable* var = FindSmiLoopVariable(stmt);
+  if (var != NULL && !av_.Contains(BitIndex(var))) {
+    stmt->set_loop_variable(var);
+  }
+
+  av_.Union(saved_av);
+}
+
+
+void AssignedVariablesAnalyzer::VisitForInStatement(ForInStatement* stmt) {
+  ProcessExpression(stmt->each());
+  ProcessExpression(stmt->enumerable());
+  Visit(stmt->body());
+}
+
+
+void AssignedVariablesAnalyzer::VisitTryCatchStatement(
+    TryCatchStatement* stmt) {
+  Visit(stmt->try_block());
+  Visit(stmt->catch_block());
+}
+
+
+void AssignedVariablesAnalyzer::VisitTryFinallyStatement(
+    TryFinallyStatement* stmt) {
+  Visit(stmt->try_block());
+  Visit(stmt->finally_block());
+}
+
+
+void AssignedVariablesAnalyzer::VisitDebuggerStatement(
+    DebuggerStatement* stmt) {
+  // Nothing to do.
+}
+
+
+void AssignedVariablesAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) {
+  // Nothing to do.
+  ASSERT(av_.IsEmpty());
+}
+
+
+void AssignedVariablesAnalyzer::VisitFunctionBoilerplateLiteral(
+    FunctionBoilerplateLiteral* expr) {
+  // Nothing to do.
+  ASSERT(av_.IsEmpty());
+}
+
+
+void AssignedVariablesAnalyzer::VisitConditional(Conditional* expr) {
+  ASSERT(av_.IsEmpty());
+
+  Visit(expr->condition());
+
+  BitVector result(av_);
+  av_.Clear();
+  Visit(expr->then_expression());
+  result.Union(av_);
+
+  av_.Clear();
+  Visit(expr->else_expression());
+  av_.Union(result);
+}
+
+
+void AssignedVariablesAnalyzer::VisitSlot(Slot* expr) {
+  UNREACHABLE();
+}
+
+
+void AssignedVariablesAnalyzer::VisitVariableProxy(VariableProxy* expr) {
+  // Nothing to do.
+  ASSERT(av_.IsEmpty());
+}
+
+
+void AssignedVariablesAnalyzer::VisitLiteral(Literal* expr) {
+  // Nothing to do.
+  ASSERT(av_.IsEmpty());
+}
+
+
+void AssignedVariablesAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) {
+  // Nothing to do.
+  ASSERT(av_.IsEmpty());
+}
+
+
+void AssignedVariablesAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) {
+  ASSERT(av_.IsEmpty());
+  BitVector result(av_.length());
+  for (int i = 0; i < expr->properties()->length(); i++) {
+    Visit(expr->properties()->at(i)->value());
+    result.Union(av_);
+    av_.Clear();
+  }
+  av_.CopyFrom(result);
+}
+
+
+void AssignedVariablesAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) {
+  ASSERT(av_.IsEmpty());
+  BitVector result(av_.length());
+  for (int i = 0; i < expr->values()->length(); i++) {
+    Visit(expr->values()->at(i));
+    result.Union(av_);
+    av_.Clear();
+  }
+  av_.CopyFrom(result);
+}
+
+
+void AssignedVariablesAnalyzer::VisitCatchExtensionObject(
+    CatchExtensionObject* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->key());
+  ProcessExpression(expr->value());
+}
+
+
+void AssignedVariablesAnalyzer::VisitAssignment(Assignment* expr) {
+  ASSERT(av_.IsEmpty());
+
+  Visit(expr->target());
+
+  ProcessExpression(expr->value());
+
+  Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+  if (var != NULL) RecordAssignedVar(var);
+
+  // If we have a variable as a receiver in a property store, check if
+  // we can mark it as trivial.
+  if (expr->target()->AsProperty() != NULL) {
+    MarkIfTrivial(expr->target()->AsProperty()->obj());
+  }
+}
+
+
+void AssignedVariablesAnalyzer::VisitThrow(Throw* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->exception());
+}
+
+
+void AssignedVariablesAnalyzer::VisitProperty(Property* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->obj());
+  ProcessExpression(expr->key());
+
+  // In case we have a variable as a receiver, check if we can mark
+  // it as trivial.
+  MarkIfTrivial(expr->obj());
+}
+
+
+void AssignedVariablesAnalyzer::VisitCall(Call* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->expression());
+  BitVector result(av_);
+  for (int i = 0; i < expr->arguments()->length(); i++) {
+    av_.Clear();
+    Visit(expr->arguments()->at(i));
+    result.Union(av_);
+  }
+  av_.CopyFrom(result);
+}
+
+
+void AssignedVariablesAnalyzer::VisitCallNew(CallNew* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->expression());
+  BitVector result(av_);
+  for (int i = 0; i < expr->arguments()->length(); i++) {
+    av_.Clear();
+    Visit(expr->arguments()->at(i));
+    result.Union(av_);
+  }
+  av_.CopyFrom(result);
+}
+
+
+void AssignedVariablesAnalyzer::VisitCallRuntime(CallRuntime* expr) {
+  ASSERT(av_.IsEmpty());
+  BitVector result(av_);
+  for (int i = 0; i < expr->arguments()->length(); i++) {
+    av_.Clear();
+    Visit(expr->arguments()->at(i));
+    result.Union(av_);
+  }
+  av_.CopyFrom(result);
+}
+
+
+void AssignedVariablesAnalyzer::VisitUnaryOperation(UnaryOperation* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->expression());
+}
+
+
+void AssignedVariablesAnalyzer::VisitCountOperation(CountOperation* expr) {
+  ASSERT(av_.IsEmpty());
+
+  Visit(expr->expression());
+
+  Variable* var = expr->expression()->AsVariableProxy()->AsVariable();
+  if (var != NULL) RecordAssignedVar(var);
+}
+
+
+void AssignedVariablesAnalyzer::VisitBinaryOperation(BinaryOperation* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->left());
+
+  ProcessExpression(expr->right());
+
+  // In case we have a variable on the left side, check if we can mark
+  // it as trivial.
+  MarkIfTrivial(expr->left());
+}
+
+
+void AssignedVariablesAnalyzer::VisitCompareOperation(CompareOperation* expr) {
+  ASSERT(av_.IsEmpty());
+  Visit(expr->left());
+
+  ProcessExpression(expr->right());
+
+  // In case we have a variable on the left side, check if we can mark
+  // it as trivial.
+  MarkIfTrivial(expr->left());
+}
+
+
+void AssignedVariablesAnalyzer::VisitThisFunction(ThisFunction* expr) {
+  // Nothing to do.
+  ASSERT(av_.IsEmpty());
+}
+
+
+void AssignedVariablesAnalyzer::VisitDeclaration(Declaration* decl) {
+  UNREACHABLE();
+}


 #ifdef DEBUG
=======================================
--- /branches/bleeding_edge/src/data-flow.h     Wed Mar 10 09:02:25 2010
+++ /branches/bleeding_edge/src/data-flow.h     Wed Mar 10 09:19:22 2010
@@ -43,7 +43,6 @@
       : length_(length),
         data_length_(SizeFor(length)),
         data_(Zone::NewArray<uint32_t>(data_length_)) {
-    ASSERT(length > 0);
     Clear();
   }

@@ -486,6 +485,41 @@
 };


+// Computes the set of assigned variables and annotates variables proxies
+// that are trivial sub-expressions and for-loops where the loop variable
+// is guaranteed to be a smi.
+class AssignedVariablesAnalyzer : public AstVisitor {
+ public:
+  explicit AssignedVariablesAnalyzer(FunctionLiteral* fun);
+
+  void Analyze();
+
+ private:
+  Variable* FindSmiLoopVariable(ForStatement* stmt);
+
+  int BitIndex(Variable* var);
+
+  void RecordAssignedVar(Variable* var);
+
+  void MarkIfTrivial(Expression* expr);
+
+  // Visits an expression saving the accumulator before, clearing
+  // it before visting and restoring it after visiting.
+  void ProcessExpression(Expression* expr);
+
+  // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+  AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+  FunctionLiteral* fun_;
+
+  // Accumulator for assigned variables set.
+  BitVector av_;
+
+  DISALLOW_COPY_AND_ASSIGN(AssignedVariablesAnalyzer);
+};
+
 } }  // namespace v8::internal


=======================================
--- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Mar 9 01:40:35 2010 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Wed Mar 10 09:19:22 2010
@@ -2286,61 +2286,69 @@
       // a jump target and branching to duplicate the virtual frame at
       // the first split.  We manually handle the off-frame references
       // by reconstituting them on the non-fall-through path.
-      JumpTarget is_smi;
-      __ test(left_side.reg(), Immediate(kSmiTagMask));
-      is_smi.Branch(zero, taken);
-
-      bool is_for_loop_compare = (node->AsCompareOperation() != NULL)
-          && node->AsCompareOperation()->is_for_loop_condition();
-      if (!is_for_loop_compare
-          && CpuFeatures::IsSupported(SSE2)
-          && right_val->IsSmi()) {
-        // Right side is a constant smi and left side has been checked
-        // not to be a smi.
-        CpuFeatures::Scope use_sse2(SSE2);
-        JumpTarget not_number;
-        __ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
-               Immediate(Factory::heap_number_map()));
-        not_number.Branch(not_equal, &left_side);
-        __ movdbl(xmm1,
-                  FieldOperand(left_reg, HeapNumber::kValueOffset));
-        int value = Smi::cast(*right_val)->value();
-        if (value == 0) {
-          __ xorpd(xmm0, xmm0);
-        } else {
-          Result temp = allocator()->Allocate();
-          __ mov(temp.reg(), Immediate(value));
-          __ cvtsi2sd(xmm0, Operand(temp.reg()));
-          temp.Unuse();
-        }
-        __ comisd(xmm1, xmm0);
-        // Jump to builtin for NaN.
-        not_number.Branch(parity_even, &left_side);
-        left_side.Unuse();
-        Condition double_cc = cc;
-        switch (cc) {
-          case less:          double_cc = below;       break;
-          case equal:         double_cc = equal;       break;
-          case less_equal:    double_cc = below_equal; break;
-          case greater:       double_cc = above;       break;
-          case greater_equal: double_cc = above_equal; break;
-          default: UNREACHABLE();
-        }
-        dest->true_target()->Branch(double_cc);
+
+      if (left_side.is_smi()) {
+        if (FLAG_debug_code) {
+          __ AbortIfNotSmi(left_side.reg(), "Argument not a smi");
+        }
+      } else {
+        JumpTarget is_smi;
+        __ test(left_side.reg(), Immediate(kSmiTagMask));
+        is_smi.Branch(zero, taken);
+
+        bool is_for_loop_compare = (node->AsCompareOperation() != NULL)
+            && node->AsCompareOperation()->is_for_loop_condition();
+        if (!is_for_loop_compare
+            && CpuFeatures::IsSupported(SSE2)
+            && right_val->IsSmi()) {
+          // Right side is a constant smi and left side has been checked
+          // not to be a smi.
+          CpuFeatures::Scope use_sse2(SSE2);
+          JumpTarget not_number;
+          __ cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
+                 Immediate(Factory::heap_number_map()));
+          not_number.Branch(not_equal, &left_side);
+          __ movdbl(xmm1,
+                    FieldOperand(left_reg, HeapNumber::kValueOffset));
+          int value = Smi::cast(*right_val)->value();
+          if (value == 0) {
+            __ xorpd(xmm0, xmm0);
+          } else {
+            Result temp = allocator()->Allocate();
+            __ mov(temp.reg(), Immediate(value));
+            __ cvtsi2sd(xmm0, Operand(temp.reg()));
+            temp.Unuse();
+          }
+          __ comisd(xmm1, xmm0);
+          // Jump to builtin for NaN.
+          not_number.Branch(parity_even, &left_side);
+          left_side.Unuse();
+          Condition double_cc = cc;
+          switch (cc) {
+            case less:          double_cc = below;       break;
+            case equal:         double_cc = equal;       break;
+            case less_equal:    double_cc = below_equal; break;
+            case greater:       double_cc = above;       break;
+            case greater_equal: double_cc = above_equal; break;
+            default: UNREACHABLE();
+          }
+          dest->true_target()->Branch(double_cc);
+          dest->false_target()->Jump();
+          not_number.Bind(&left_side);
+        }
+
+        // Setup and call the compare stub.
+        CompareStub stub(cc, strict, kCantBothBeNaN);
+        Result result = frame_->CallStub(&stub, &left_side, &right_side);
+        result.ToRegister();
+        __ cmp(result.reg(), 0);
+        result.Unuse();
+        dest->true_target()->Branch(cc);
         dest->false_target()->Jump();
-        not_number.Bind(&left_side);
+
+        is_smi.Bind();
       }

-      // Setup and call the compare stub.
-      CompareStub stub(cc, strict, kCantBothBeNaN);
-      Result result = frame_->CallStub(&stub, &left_side, &right_side);
-      result.ToRegister();
-      __ cmp(result.reg(), 0);
-      result.Unuse();
-      dest->true_target()->Branch(cc);
-      dest->false_target()->Jump();
-
-      is_smi.Bind();
       left_side = Result(left_reg);
       right_side = Result(right_val);
       // Test smi equality and comparison by signed int comparison.
@@ -3579,6 +3587,24 @@
   }

   CheckStack();  // TODO(1222600): ignore if body contains calls.
+
+  // If we have (a) a loop with a compile-time constant trip count
+  // and (b) the loop induction variable is not assignend inside the
+  // loop we update the number type of the induction variable to be smi.
+
+  if (node->is_fast_smi_loop()) {
+    // Set number type of the loop variable to smi.
+    Slot* slot = node->loop_variable()->slot();
+    ASSERT(slot->type() == Slot::LOCAL);
+    frame_->SetTypeForLocalAt(slot->index(), NumberInfo::Smi());
+    if (FLAG_debug_code) {
+      frame_->PushLocalAt(slot->index());
+      Result var = frame_->Pop();
+      var.ToRegister();
+      __ AbortIfNotSmi(var.reg(), "Loop variable not a smi.");
+    }
+  }
+
   Visit(node->body());

   // If there is an update expression, compile it if necessary.
@@ -6624,15 +6650,6 @@
       __ Set(tmp.reg(), Immediate(0));
     }

-    DeferredCode* deferred = NULL;
-    if (is_postfix) {
-      deferred = new DeferredPostfixCountOperation(new_value.reg(),
-                                                   old_value.reg(),
-                                                   is_increment);
-    } else {
-      deferred = new DeferredPrefixCountOperation(new_value.reg(),
-                                                  is_increment);
-    }

     if (is_increment) {
       __ add(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
@@ -6640,24 +6657,41 @@
       __ sub(Operand(new_value.reg()), Immediate(Smi::FromInt(1)));
     }

-    // If the count operation didn't overflow and the result is a valid
-    // smi, we're done. Otherwise, we jump to the deferred slow-case
-    // code.
-    if (tmp.is_valid()) {
-      // We combine the overflow and the smi tag check if we could
-      // successfully allocate a temporary byte register.
-      __ setcc(overflow, tmp.reg());
-      __ or_(Operand(tmp.reg()), new_value.reg());
-      __ test(tmp.reg(), Immediate(kSmiTagMask));
-      tmp.Unuse();
-      deferred->Branch(not_zero);
+    if (new_value.is_smi()) {
+      if (FLAG_debug_code) {
+        __ AbortIfNotSmi(new_value.reg(), "Argument not a smi");
+      }
+      if (tmp.is_valid()) tmp.Unuse();
     } else {
-      // Otherwise we test separately for overflow and smi tag.
-      deferred->Branch(overflow);
-      __ test(new_value.reg(), Immediate(kSmiTagMask));
-      deferred->Branch(not_zero);
-    }
-    deferred->BindExit();
+      DeferredCode* deferred = NULL;
+      if (is_postfix) {
+        deferred = new DeferredPostfixCountOperation(new_value.reg(),
+                                                     old_value.reg(),
+                                                     is_increment);
+      } else {
+        deferred = new DeferredPrefixCountOperation(new_value.reg(),
+                                                    is_increment);
+      }
+
+      // If the count operation didn't overflow and the result is a valid
+      // smi, we're done. Otherwise, we jump to the deferred slow-case
+      // code.
+      if (tmp.is_valid()) {
+        // We combine the overflow and the smi tag check if we could
+        // successfully allocate a temporary byte register.
+        __ setcc(overflow, tmp.reg());
+        __ or_(Operand(tmp.reg()), new_value.reg());
+        __ test(tmp.reg(), Immediate(kSmiTagMask));
+        tmp.Unuse();
+        deferred->Branch(not_zero);
+      } else {
+        // Otherwise we test separately for overflow and smi tag.
+        deferred->Branch(overflow);
+        __ test(new_value.reg(), Immediate(kSmiTagMask));
+        deferred->Branch(not_zero);
+      }
+      deferred->BindExit();
+    }

     // Postfix: store the old value in the allocated slot under the
     // reference.
@@ -6823,8 +6857,15 @@
       overwrite_mode = OVERWRITE_RIGHT;
     }

-    Load(node->left());
-    Load(node->right());
+    if (node->left()->IsTrivial()) {
+      Load(node->right());
+      Result right = frame_->Pop();
+      frame_->Push(node->left());
+      frame_->Push(&right);
+    } else {
+      Load(node->left());
+      Load(node->right());
+    }
     GenericBinaryOperation(node->op(), node->type(), overwrite_mode);
   }
 }
@@ -7024,8 +7065,20 @@
     default:
       UNREACHABLE();
   }
-  if (!left_already_loaded) Load(left);
-  Load(right);
+
+  if (left->IsTrivial()) {
+    if (!left_already_loaded) {
+      Load(right);
+      Result right_result = frame_->Pop();
+      frame_->Push(left);
+      frame_->Push(&right_result);
+    } else {
+      Load(right);
+    }
+  } else {
+    if (!left_already_loaded) Load(left);
+    Load(right);
+  }
   Comparison(node, cc, strict, destination());
 }

=======================================
--- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Tue Mar 2 03:54:11 2010 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.cc Wed Mar 10 09:19:22 2010
@@ -395,6 +395,12 @@
   Assert(equal, msg);
   bind(&ok);
 }
+
+
+void MacroAssembler::AbortIfNotSmi(Register object, const char* msg) {
+  test(object, Immediate(kSmiTagMask));
+  Assert(equal, msg);
+}


 void MacroAssembler::EnterFrame(StackFrame::Type type) {
=======================================
--- /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Thu Feb 25 04:18:55 2010 +++ /branches/bleeding_edge/src/ia32/macro-assembler-ia32.h Wed Mar 10 09:19:22 2010
@@ -179,6 +179,9 @@
   // Abort execution if argument is not a number. Used in debug code.
   void AbortIfNotNumber(Register object, const char* msg);

+  // Abort execution if argument is not a smi. Used in debug code.
+  void AbortIfNotSmi(Register object, const char* msg);
+
// ---------------------------------------------------------------------------
   // Exception handling

=======================================
--- /branches/bleeding_edge/src/ia32/virtual-frame-ia32.cc Fri Mar 5 15:54:13 2010 +++ /branches/bleeding_edge/src/ia32/virtual-frame-ia32.cc Wed Mar 10 09:19:22 2010
@@ -1171,11 +1171,17 @@
   }

   VariableProxy* proxy = expr->AsVariableProxy();
-  if (proxy != NULL && proxy->is_this()) {
-    PushParameterAt(-1);
-    return;
-  }
-
+  if (proxy != NULL) {
+    Slot* slot = proxy->var()->slot();
+    if (slot->type() == Slot::LOCAL) {
+      PushLocalAt(slot->index());
+      return;
+    }
+    if (slot->type() == Slot::PARAMETER) {
+      PushParameterAt(slot->index());
+      return;
+    }
+  }
   UNREACHABLE();
 }

=======================================
--- /branches/bleeding_edge/src/ia32/virtual-frame-ia32.h Fri Mar 5 15:54:13 2010 +++ /branches/bleeding_edge/src/ia32/virtual-frame-ia32.h Wed Mar 10 09:19:22 2010
@@ -422,6 +422,9 @@
   // the frame.  Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
   inline void Nip(int num_dropped);

+ // Update the type information of a local variable frame element directly.
+  inline void SetTypeForLocalAt(int index, NumberInfo info);
+
  private:
   static const int kLocal0Offset = JavaScriptFrameConstants::kLocal0Offset;
static const int kFunctionOffset = JavaScriptFrameConstants::kFunctionOffset;
=======================================
--- /branches/bleeding_edge/src/scopes.h        Tue Dec  8 01:43:51 2009
+++ /branches/bleeding_edge/src/scopes.h        Wed Mar 10 09:19:22 2010
@@ -277,7 +277,6 @@
   // The number of contexts between this and scope; zero if this == scope.
   int ContextChainLength(Scope* scope);

-
// ---------------------------------------------------------------------------
   // Debugging.

=======================================
--- /branches/bleeding_edge/src/virtual-frame-inl.h     Fri Mar  5 15:54:13 2010
+++ /branches/bleeding_edge/src/virtual-frame-inl.h     Wed Mar 10 09:19:22 2010
@@ -118,6 +118,12 @@

   return true;
 }
+
+
+void VirtualFrame::SetTypeForLocalAt(int index, NumberInfo info) {
+  elements_[local0_index() + index].set_number_info(info);
+}
+

 } }  // namespace v8::internal

--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev

Reply via email to