Add last use data flow information to the fast code generator.
authorfschneider@chromium.org <fschneider@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 12 Feb 2010 10:16:30 +0000 (10:16 +0000)
committerfschneider@chromium.org <fschneider@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 12 Feb 2010 10:16:30 +0000 (10:16 +0000)
This change add simple local live variable information to
the fast code generator.  It supports only AST nodes that
are accepted by the syntax checker.

Each variable use points to a variable definition structure
which contains the last use of the definition.

To determine whether a variable is live after a certain point
we can check whether its last use occurs later in the evaluation
order defined by the AST labeling number.

The new information is currently only printed out together with
the IR and not yet used for code generation.

Review URL: http://codereview.chromium.org/603004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3839 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/ast.h
src/data-flow.cc
src/data-flow.h
src/fast-codegen.cc
test/mjsunit/compiler/simple-binary-op.js [new file with mode: 0644]

index 2b38b18..8e717a6 100644 (file)
--- a/src/ast.h
+++ b/src/ast.h
@@ -102,6 +102,7 @@ namespace internal {
 // Forward declarations
 class TargetCollector;
 class MaterializedLiteral;
+class DefinitionInfo;
 
 #define DEF_FORWARD_DECLARATION(type) class type;
 AST_NODE_LIST(DEF_FORWARD_DECLARATION)
@@ -182,7 +183,7 @@ class Expression: public AstNode {
 
   static const int kNoLabel = -1;
 
-  Expression() : num_(kNoLabel) {}
+  Expression() : num_(kNoLabel), def_(NULL), defined_vars_(NULL) {}
 
   virtual Expression* AsExpression()  { return this; }
 
@@ -211,9 +212,20 @@ class Expression: public AstNode {
   // AST node numbering ordered by evaluation order.
   void set_num(int n) { num_ = n; }
 
+  // Data flow information.
+  DefinitionInfo* var_def() { return def_; }
+  void set_var_def(DefinitionInfo* def) { def_ = def; }
+
+  ZoneList<DefinitionInfo*>* defined_vars() { return defined_vars_; }
+  void set_defined_vars(ZoneList<DefinitionInfo*>* defined_vars) {
+    defined_vars_ = defined_vars;
+  }
+
  private:
   StaticType type_;
   int num_;
+  DefinitionInfo* def_;
+  ZoneList<DefinitionInfo*>* defined_vars_;
 };
 
 
index d3601a6..5e9d217 100644 (file)
@@ -270,4 +270,292 @@ void AstLabeler::VisitDeclaration(Declaration* decl) {
   UNREACHABLE();
 }
 
+
+ZoneList<Expression*>* VarUseMap::Lookup(Variable* var) {
+  HashMap::Entry* entry = HashMap::Lookup(var, var->name()->Hash(), true);
+  if (entry->value == NULL) {
+    entry->value = new ZoneList<Expression*>(1);
+  }
+  return reinterpret_cast<ZoneList<Expression*>*>(entry->value);
+}
+
+
+void LivenessAnalyzer::Analyze(FunctionLiteral* fun) {
+  // Process the function body.
+  VisitStatements(fun->body());
+
+  // All variables are implicitly defined at the function start.
+  // Record a definition of all variables live at function entry.
+  for (HashMap::Entry* p = live_vars_.Start();
+       p != NULL;
+       p = live_vars_.Next(p)) {
+    Variable* var = reinterpret_cast<Variable*>(p->key);
+    RecordDef(var, fun);
+  }
+}
+
+
+void LivenessAnalyzer::VisitStatements(ZoneList<Statement*>* stmts) {
+  // Visit statements right-to-left.
+  for (int i = stmts->length() - 1; i >= 0; i--) {
+    Visit(stmts->at(i));
+  }
+}
+
+
+void LivenessAnalyzer::RecordUse(Variable* var, Expression* expr) {
+  ASSERT(var->is_global() || var->is_this());
+  ZoneList<Expression*>* uses = live_vars_.Lookup(var);
+  uses->Add(expr);
+}
+
+
+void LivenessAnalyzer::RecordDef(Variable* var, Expression* expr) {
+  ASSERT(var->is_global() || var->is_this());
+
+  // We do not support other expressions that can define variables.
+  ASSERT(expr->AsFunctionLiteral() != NULL);
+
+  // Add the variable to the list of defined variables.
+  if (expr->defined_vars() == NULL) {
+    expr->set_defined_vars(new ZoneList<DefinitionInfo*>(1));
+  }
+  DefinitionInfo* def = new DefinitionInfo();
+  expr->AsFunctionLiteral()->defined_vars()->Add(def);
+
+  // Compute the last use of the definition. The variable uses are
+  // inserted in reversed evaluation order. The first element
+  // in the list of live uses is the last use.
+  ZoneList<Expression*>* uses = live_vars_.Lookup(var);
+  while (uses->length() > 0) {
+    Expression* use_site = uses->RemoveLast();
+    use_site->set_var_def(def);
+    if (uses->length() == 0) {
+      def->set_last_use(use_site);
+    }
+  }
+}
+
+
+// Visitor functions for live variable analysis.
+void LivenessAnalyzer::VisitDeclaration(Declaration* decl) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitBlock(Block* stmt) {
+  VisitStatements(stmt->statements());
+}
+
+
+void LivenessAnalyzer::VisitExpressionStatement(
+    ExpressionStatement* stmt) {
+  Visit(stmt->expression());
+}
+
+
+void LivenessAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) {
+  // Do nothing.
+}
+
+
+void LivenessAnalyzer::VisitIfStatement(IfStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitContinueStatement(ContinueStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitBreakStatement(BreakStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitReturnStatement(ReturnStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitWithEnterStatement(
+    WithEnterStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitWithExitStatement(WithExitStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitWhileStatement(WhileStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitForStatement(ForStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitForInStatement(ForInStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitTryCatchStatement(TryCatchStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitTryFinallyStatement(
+    TryFinallyStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitDebuggerStatement(
+    DebuggerStatement* stmt) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitFunctionBoilerplateLiteral(
+    FunctionBoilerplateLiteral* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitConditional(Conditional* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitSlot(Slot* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitVariableProxy(VariableProxy* expr) {
+  Variable* var = expr->var();
+  ASSERT(var->is_global());
+  ASSERT(!var->is_this());
+  RecordUse(var, expr);
+}
+
+
+void LivenessAnalyzer::VisitLiteral(Literal* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCatchExtensionObject(
+    CatchExtensionObject* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitAssignment(Assignment* expr) {
+  Property* prop = expr->target()->AsProperty();
+  ASSERT(prop != NULL);
+  ASSERT(prop->key()->IsPropertyName());
+  VariableProxy* proxy = prop->obj()->AsVariableProxy();
+  ASSERT(proxy != NULL && proxy->var()->is_this());
+
+  // Record use of this at the assignment node. Assignments to
+  // this-properties are treated like unary operations.
+  RecordUse(proxy->var(), expr);
+
+  // Visit right-hand side.
+  Visit(expr->value());
+}
+
+
+void LivenessAnalyzer::VisitThrow(Throw* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitProperty(Property* expr) {
+  ASSERT(expr->key()->IsPropertyName());
+  VariableProxy* proxy = expr->obj()->AsVariableProxy();
+  ASSERT(proxy != NULL && proxy->var()->is_this());
+  RecordUse(proxy->var(), expr);
+}
+
+
+void LivenessAnalyzer::VisitCall(Call* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCallNew(CallNew* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCallRuntime(CallRuntime* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitUnaryOperation(UnaryOperation* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCountOperation(CountOperation* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitBinaryOperation(BinaryOperation* expr) {
+  // Visit child nodes in reverse evaluation order.
+  Visit(expr->right());
+  Visit(expr->left());
+}
+
+
+void LivenessAnalyzer::VisitCompareOperation(CompareOperation* expr) {
+  UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitThisFunction(ThisFunction* expr) {
+  UNREACHABLE();
+}
+
+
 } }  // namespace v8::internal
index 7c16d5d..2331944 100644 (file)
@@ -62,6 +62,56 @@ class AstLabeler: public AstVisitor {
 };
 
 
+class VarUseMap : public HashMap {
+ public:
+  VarUseMap() : HashMap(VarMatch) {}
+
+  ZoneList<Expression*>* Lookup(Variable* var);
+
+ private:
+  static bool VarMatch(void* key1, void* key2) { return key1 == key2; }
+};
+
+
+class DefinitionInfo : public ZoneObject {
+ public:
+  explicit DefinitionInfo() : last_use_(NULL) {}
+
+  Expression* last_use() { return last_use_; }
+  void set_last_use(Expression* expr) { last_use_ = expr; }
+
+ private:
+  Expression* last_use_;
+  Register location_;
+};
+
+
+class LivenessAnalyzer : public AstVisitor {
+ public:
+  LivenessAnalyzer() {}
+
+  void Analyze(FunctionLiteral* fun);
+
+ private:
+  void VisitStatements(ZoneList<Statement*>* stmts);
+
+  void RecordUse(Variable* var, Expression* expr);
+  void RecordDef(Variable* var, Expression* expr);
+
+
+  // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+  AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+  // Map for tracking the live variables.
+  VarUseMap live_vars_;
+
+  DISALLOW_COPY_AND_ASSIGN(LivenessAnalyzer);
+};
+
+
 } }  // namespace v8::internal
 
+
 #endif  // V8_DATAFLOW_H_
index 3d288de..1b0672a 100644 (file)
@@ -436,6 +436,9 @@ Handle<Code> FastCodeGenerator::MakeCode(CompilationInfo* info) {
   AstLabeler labeler;
   labeler.Label(info);
 
+  LivenessAnalyzer analyzer;
+  analyzer.Analyze(info->function());
+
   CodeGenerator::MakeCodePrologue(info);
 
   const int kInitialBufferSize = 4 * KB;
@@ -594,7 +597,8 @@ void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
     Comment cmnt(masm(), ";; Global");
     if (FLAG_print_ir) {
       SmartPointer<char> name = expr->name()->ToCString();
-      PrintF("%d: t%d = Global(%s)\n", expr->num(), expr->num(), *name);
+      PrintF("%d: t%d = Global(%s)  // last_use = %d\n", expr->num(),
+             expr->num(), *name, expr->var_def()->last_use()->num());
     }
     EmitGlobalVariableLoad(cell);
   }
@@ -648,7 +652,9 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
     SmartPointer<char> name_string = name->ToCString();
     PrintF("%d: ", expr->num());
     if (!destination().is(no_reg)) PrintF("t%d = ", expr->num());
-    PrintF("Store(this, \"%s\", t%d)\n", *name_string, expr->value()->num());
+    PrintF("Store(this, \"%s\", t%d)  // last_use(this) = %d\n", *name_string,
+           expr->value()->num(),
+           expr->var_def()->last_use()->num());
   }
 
   EmitThisPropertyStore(name);
@@ -671,8 +677,9 @@ void FastCodeGenerator::VisitProperty(Property* expr) {
     Comment cmnt(masm(), ";; Load from this");
     if (FLAG_print_ir) {
       SmartPointer<char> name_string = name->ToCString();
-      PrintF("%d: t%d = Load(this, \"%s\")\n",
-             expr->num(), expr->num(), *name_string);
+      PrintF("%d: t%d = Load(this, \"%s\")  // last_use(this) = %d\n",
+             expr->num(), expr->num(), *name_string,
+             expr->var_def()->last_use()->num());
     }
     EmitThisPropertyLoad(name);
   }
diff --git a/test/mjsunit/compiler/simple-binary-op.js b/test/mjsunit/compiler/simple-binary-op.js
new file mode 100644 (file)
index 0000000..15e1a55
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2010 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.
+
+// Flags: --fast-compiler
+
+var a = 1;
+var b = 2;
+var c = 4;
+
+function f() { this.x = this.x | a | b | c | a | c; }
+
+var o = {x:0, g:f}
+
+o.g();
+
+assertEquals(7, o.x);