This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Go patch committed: Add flattening pass


This patch from Chris Manghane adds a flattening pass to the Go
frontend.  This is a step toward moving more types of expressions into
the backend interface.  Bootstrapped and ran Go testsuite on
x86_64-unknown-linux-gnu.  Committed to mainline.

Ian

diff -r 447683c37ddf go/expressions.h
--- a/go/expressions.h	Thu Jan 09 15:16:28 2014 -0800
+++ b/go/expressions.h	Thu Jan 09 15:18:09 2014 -0800
@@ -575,6 +575,18 @@
 	int iota_value)
   { return this->do_lower(gogo, function, inserter, iota_value); }
 
+  // Flatten an expression. This is called after order_evaluation.
+  // FUNCTION is the function we are in; it will be NULL for an
+  // expression initializing a global variable.  INSERTER may be used
+  // to insert statements before the statement or initializer
+  // containing this expression; it is normally used to create
+  // temporary variables. This function must resolve expressions
+  // which could not be fully parsed into their final form.  It
+  // returns the same Expression or a new one.
+  Expression*
+  flatten(Gogo* gogo, Named_object* function, Statement_inserter* inserter)
+  { return this->do_flatten(gogo, function, inserter); }
+
   // Determine the real type of an expression with abstract integer,
   // floating point, or complex type.  TYPE_CONTEXT describes the
   // expected type.
@@ -698,6 +710,12 @@
   do_lower(Gogo*, Named_object*, Statement_inserter*, int)
   { return this; }
 
+  // Return a flattened expression.
+  virtual Expression*
+  do_flatten(Gogo*, Named_object*, Statement_inserter*)
+  { return this; }
+
+
   // Return whether this is a constant expression.
   virtual bool
   do_is_constant() const
diff -r 447683c37ddf go/go.cc
--- a/go/go.cc	Thu Jan 09 15:16:28 2014 -0800
+++ b/go/go.cc	Thu Jan 09 15:18:09 2014 -0800
@@ -119,12 +119,15 @@
   // Use temporary variables to force order of evaluation.
   ::gogo->order_evaluations();
 
+  // Flatten the parse tree.
+  ::gogo->flatten();
+
   // Build thunks for functions which call recover.
   ::gogo->build_recover_thunks();
 
   // Convert complicated go and defer statements into simpler ones.
   ::gogo->simplify_thunk_statements();
-  
+
   // Dump ast, use filename[0] as the base name
   ::gogo->dump_ast(filenames[0]);
 }
diff -r 447683c37ddf go/gogo.cc
--- a/go/gogo.cc	Thu Jan 09 15:16:28 2014 -0800
+++ b/go/gogo.cc	Thu Jan 09 15:18:09 2014 -0800
@@ -2703,6 +2703,169 @@
   this->traverse(&order_eval);
 }
 
+// Traversal to flatten parse tree after order of evaluation rules are applied.
+
+class Flatten : public Traverse
+{
+ public:
+  Flatten(Gogo* gogo, Named_object* function)
+    : Traverse(traverse_variables
+	       | traverse_functions
+	       | traverse_statements
+	       | traverse_expressions),
+      gogo_(gogo), function_(function), inserter_()
+  { }
+
+  void
+  set_inserter(const Statement_inserter* inserter)
+  { this->inserter_ = *inserter; }
+
+  int
+  variable(Named_object*);
+
+  int
+  function(Named_object*);
+
+  int
+  statement(Block*, size_t* pindex, Statement*);
+
+  int
+  expression(Expression**);
+
+ private:
+  // General IR.
+  Gogo* gogo_;
+  // The function we are traversing.
+  Named_object* function_;
+  // Current statement inserter for use by expressions.
+  Statement_inserter inserter_;
+};
+
+// Flatten variables.
+
+int
+Flatten::variable(Named_object* no)
+{
+  if (!no->is_variable())
+    return TRAVERSE_CONTINUE;
+
+  if (no->is_variable() && no->var_value()->is_global())
+    {
+      // Global variables can have loops in their initialization
+      // expressions.  This is handled in flatten_init_expression.
+      no->var_value()->flatten_init_expression(this->gogo_, this->function_,
+                                               &this->inserter_);
+      return TRAVERSE_CONTINUE;
+    }
+
+  go_assert(!no->var_value()->has_pre_init());
+
+  return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Flatten the body of a function.  Record the function while flattening it,
+// so that we can pass it down when flattening an expression.
+
+int
+Flatten::function(Named_object* no)
+{
+  go_assert(this->function_ == NULL);
+  this->function_ = no;
+  int t = no->func_value()->traverse(this);
+  this->function_ = NULL;
+
+  if (t == TRAVERSE_EXIT)
+    return t;
+  return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Flatten statement parse trees.
+
+int
+Flatten::statement(Block* block, size_t* pindex, Statement* sorig)
+{
+  // Because we explicitly traverse the statement's contents
+  // ourselves, we want to skip block statements here.  There is
+  // nothing to flatten in a block statement.
+  if (sorig->is_block_statement())
+    return TRAVERSE_CONTINUE;
+
+  Statement_inserter hold_inserter(this->inserter_);
+  this->inserter_ = Statement_inserter(block, pindex);
+
+  // Flatten the expressions first.
+  int t = sorig->traverse_contents(this);
+  if (t == TRAVERSE_EXIT)
+    {
+      this->inserter_ = hold_inserter;
+      return t;
+    }
+
+  // Keep flattening until nothing changes.
+  Statement* s = sorig;
+  while (true)
+    {
+      Statement* snew = s->flatten(this->gogo_, this->function_, block,
+                                   &this->inserter_);
+      if (snew == s)
+	break;
+      s = snew;
+      t = s->traverse_contents(this);
+      if (t == TRAVERSE_EXIT)
+	{
+	  this->inserter_ = hold_inserter;
+	  return t;
+	}
+    }
+
+  if (s != sorig)
+    block->replace_statement(*pindex, s);
+
+  this->inserter_ = hold_inserter;
+  return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Flatten expression parse trees.
+
+int
+Flatten::expression(Expression** pexpr)
+{
+  // Keep flattening until nothing changes.
+  while (true)
+    {
+      Expression* e = *pexpr;
+      if (e->traverse_subexpressions(this) == TRAVERSE_EXIT)
+        return TRAVERSE_EXIT;
+
+      Expression* enew = e->flatten(this->gogo_, this->function_,
+                                    &this->inserter_);
+      if (enew == e)
+	break;
+      *pexpr = enew;
+    }
+  return TRAVERSE_SKIP_COMPONENTS;
+}
+
+// Flatten an expression.  INSERTER may be NULL, in which case the
+// expression had better not need to create any temporaries.
+
+void
+Gogo::flatten_expression(Named_object* function, Statement_inserter* inserter,
+                         Expression** pexpr)
+{
+  Flatten flatten(this, function);
+  if (inserter != NULL)
+    flatten.set_inserter(inserter);
+  flatten.expression(pexpr);
+}
+
+void
+Gogo::flatten()
+{
+  Flatten flatten(this, NULL);
+  this->traverse(&flatten);
+}
+
 // Traversal to convert calls to the predeclared recover function to
 // pass in an argument indicating whether it can recover from a panic
 // or not.
@@ -4286,10 +4449,11 @@
     backend_(NULL), is_global_(is_global), is_parameter_(is_parameter),
     is_receiver_(is_receiver), is_varargs_parameter_(false), is_used_(false),
     is_address_taken_(false), is_non_escaping_address_taken_(false),
-    seen_(false), init_is_lowered_(false), type_from_init_tuple_(false),
-    type_from_range_index_(false), type_from_range_value_(false),
-    type_from_chan_element_(false), is_type_switch_var_(false),
-    determined_type_(false), in_unique_section_(false)
+    seen_(false), init_is_lowered_(false), init_is_flattened_(false),
+    type_from_init_tuple_(false), type_from_range_index_(false),
+    type_from_range_value_(false), type_from_chan_element_(false),
+    is_type_switch_var_(false), determined_type_(false),
+    in_unique_section_(false)
 {
   go_assert(type != NULL || init != NULL);
   go_assert(!is_parameter || init == NULL);
@@ -4351,6 +4515,40 @@
     }
 }
 
+// Flatten the initialization expression after ordering evaluations.
+
+void
+Variable::flatten_init_expression(Gogo* gogo, Named_object* function,
+                                  Statement_inserter* inserter)
+{
+  Named_object* dep = gogo->var_depends_on(this);
+  if (dep != NULL && dep->is_variable())
+    dep->var_value()->flatten_init_expression(gogo, function, inserter);
+
+  if (this->init_ != NULL && !this->init_is_flattened_)
+    {
+      if (this->seen_)
+	{
+	  // We will give an error elsewhere, this is just to prevent
+	  // an infinite loop.
+	  return;
+	}
+      this->seen_ = true;
+
+      Statement_inserter global_inserter;
+      if (this->is_global_)
+	{
+	  global_inserter = Statement_inserter(gogo, this);
+	  inserter = &global_inserter;
+	}
+
+      gogo->flatten_expression(function, inserter, &this->init_);
+
+      this->seen_ = false;
+      this->init_is_flattened_ = true;
+    }
+}
+
 // Get the preinit block.
 
 Block*
diff -r 447683c37ddf go/gogo.h
--- a/go/gogo.h	Thu Jan 09 15:16:28 2014 -0800
+++ b/go/gogo.h	Thu Jan 09 15:18:09 2014 -0800
@@ -487,6 +487,10 @@
   void
   lower_constant(Named_object*);
 
+  // Flatten an expression.
+  void
+  flatten_expression(Named_object* function, Statement_inserter*, Expression**);
+
   // Create all necessary function descriptors.
   void
   create_function_descriptors();
@@ -531,6 +535,10 @@
   void
   order_evaluations();
 
+  // Flatten parse tree.
+  void
+  flatten();
+
   // Build thunks for functions which call recover.
   void
   build_recover_thunks();
@@ -1447,6 +1455,10 @@
   void
   lower_init_expression(Gogo*, Named_object*, Statement_inserter*);
 
+  // Flatten the initialization expression after ordering evaluations.
+  void
+  flatten_init_expression(Gogo*, Named_object*, Statement_inserter*);
+
   // A special case: the init value is used only to determine the
   // type.  This is used if the variable is defined using := with the
   // comma-ok form of a map index or a receive expression.  The init
@@ -1580,6 +1592,8 @@
   bool seen_ : 1;
   // True if we have lowered the initialization expression.
   bool init_is_lowered_ : 1;
+  // True if we have flattened the initialization expression.
+  bool init_is_flattened_ : 1;
   // True if init is a tuple used to set the type.
   bool type_from_init_tuple_ : 1;
   // True if init is a range clause and the type is the index type.
diff -r 447683c37ddf go/statements.cc
--- a/go/statements.cc	Thu Jan 09 15:16:28 2014 -0800
+++ b/go/statements.cc	Thu Jan 09 15:18:09 2014 -0800
@@ -246,6 +246,16 @@
   return this;
 }
 
+// Flatten the variable's initialization expression.
+
+Statement*
+Variable_declaration_statement::do_flatten(Gogo* gogo, Named_object* function,
+                                           Block*, Statement_inserter* inserter)
+{
+  this->var_->var_value()->flatten_init_expression(gogo, function, inserter);
+  return this;
+}
+
 // Convert a variable declaration to the backend representation.
 
 Bstatement*
diff -r 447683c37ddf go/statements.h
--- a/go/statements.h	Thu Jan 09 15:16:28 2014 -0800
+++ b/go/statements.h	Thu Jan 09 15:18:09 2014 -0800
@@ -306,6 +306,16 @@
 	Statement_inserter* inserter)
   { return this->do_lower(gogo, function, block, inserter); }
 
+  // Flatten a statement.  This is called immediately after the order of
+  // evaluation rules are applied to statements.  It returns the same
+  // Statement or a new one.  FUNCTION is the function containing this
+  // statement.  BLOCK is the block containing this statement.
+  // INSERTER can be used to insert new statements before this one.
+  Statement*
+  flatten(Gogo* gogo, Named_object* function, Block* block,
+          Statement_inserter* inserter)
+  { return this->do_flatten(gogo, function, block, inserter); }
+
   // Set type information for unnamed constants.
   void
   determine_types();
@@ -412,6 +422,12 @@
   do_lower(Gogo*, Named_object*, Block*, Statement_inserter*)
   { return this; }
 
+  // Implemented by the child class: lower this statement to a simpler
+  // one.
+  virtual Statement*
+  do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*)
+  { return this; }
+
   // Implemented by child class: set type information for unnamed
   // constants.  Any statement which includes an expression needs to
   // implement this.
@@ -583,6 +599,9 @@
   Statement*
   do_lower(Gogo*, Named_object*, Block*, Statement_inserter*);
 
+  Statement*
+  do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
+
   Bstatement*
   do_get_backend(Translate_context*);
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]