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]

[gccgo] Lower predeclared function calls to constants when possible


This gccgo patch lowers calls to predeclared functions to constants
when possible.  This is true for len or cap called on arrays or string
constants.  Doing this exposed some other issues regarding when
variable initialization and call result expressions are lowered.  The
bulk of this patch fixes those issues.  Committed to gccgo branch.

Ian

Index: gcc/go/gogo.cc
===================================================================
--- gcc/go/gogo.cc	(revision 156165)
+++ gcc/go/gogo.cc	(working copy)
@@ -1648,14 +1648,32 @@ Order_eval::statement(Block* block, size
   // anything.  We want to explicitly traverse the initialization
   // expression if there is one.
   Variable_declaration_statement* vds = s->variable_declaration_statement();
+  Expression* init = NULL;
+  Expression* orig_init = NULL;
   if (vds == NULL)
     s->traverse_contents(&find_eval_ordering);
   else
     {
-      Expression* init = vds->var()->var_value()->init();
+      init = vds->var()->var_value()->init();
       if (init == NULL)
 	return TRAVERSE_CONTINUE;
-      init->traverse_subexpressions(&find_eval_ordering);
+      orig_init = init;
+
+      // It might seem that this could be
+      // init->traverse_subexpressions.  Unfortunately that can fail
+      // in a case like
+      //   var err os.Error
+      //   newvar, err := call(arg())
+      // Here newvar will have an init of call result 0 of
+      // call(arg()).  If we only traverse subexpressions, we will
+      // only find arg(), and we won't bother to move anything out.
+      // Then we get to the assignment to err, we will traverse the
+      // whole statement, and this time we will find both call() and
+      // arg(), and so we will move them out.  This will cause them to
+      // be put into temporary variables before the assignment to err
+      // but after the declaration of newvar.  To avoid that problem,
+      // we traverse the entire expression here.
+      Expression::traverse(&init, &find_eval_ordering);
     }
 
   if (find_eval_ordering.size() <= 1)
@@ -1694,6 +1712,9 @@ Order_eval::statement(Block* block, size
       *pexpr = Expression::make_temporary_reference(ts, loc);
     }
 
+  if (init != orig_init)
+    vds->var()->var_value()->set_init(init);
+
   return TRAVERSE_CONTINUE;
 }
 
@@ -3579,6 +3600,8 @@ Traverse::~Traverse()
 {
   if (this->types_seen_ != NULL)
     delete this->types_seen_;
+  if (this->expressions_seen_ != NULL)
+    delete this->expressions_seen_;
 }
 
 // Record that we are looking at a type, and return true if we have
@@ -3599,6 +3622,21 @@ Traverse::remember_type(const Type* type
   return !ins.second;
 }
 
+// Record that we are looking at an expression, and return true if we
+// have already seen it.
+
+bool
+Traverse::remember_expression(const Expression* expression)
+{
+  gcc_assert((this->traverse_mask() & traverse_types) != 0
+	     || (this->traverse_mask() & traverse_expressions) != 0);
+  if (this->expressions_seen_ == NULL)
+    this->expressions_seen_ = new Expressions_seen();
+  std::pair<Expressions_seen::iterator, bool> ins =
+    this->expressions_seen_->insert(expression);
+  return !ins.second;
+}
+
 // The default versions of these functions should never be called: the
 // traversal mask indicates which functions may be called.
 
Index: gcc/go/gogo.h
===================================================================
--- gcc/go/gogo.h	(revision 156165)
+++ gcc/go/gogo.h	(working copy)
@@ -2206,7 +2206,7 @@ class Traverse
   static const unsigned int traverse_types =       0x40;
 
   Traverse(unsigned int traverse_mask)
-    : traverse_mask_(traverse_mask), types_seen_(NULL)
+    : traverse_mask_(traverse_mask), types_seen_(NULL), expressions_seen_(NULL)
   { }
 
   virtual ~Traverse();
@@ -2223,6 +2223,13 @@ class Traverse
   bool
   remember_type(const Type*);
 
+  // Record that we are going to see an expression.  This returns true
+  // if the expression has already been seen in this traversal.  This
+  // is only needed for cases where multiple expressions can point to
+  // a single one.
+  bool
+  remember_expression(const Expression*);
+
   // These functions return one of the TRAVERSE codes defined above.
 
   // If traverse_variables is set in the mask, this is called for
@@ -2265,10 +2272,14 @@ class Traverse
   typedef std::tr1::unordered_set<const Type*, Type_hash,
 				  Type_identical> Types_seen;
 
+  typedef std::tr1::unordered_set<const Expression*> Expressions_seen;
+
   // Bitmask of what sort of objects to traverse.
   unsigned int traverse_mask_;
   // Types which have been seen in this traversal.
   Types_seen* types_seen_;
+  // Expressions which have been seen in this traversal.
+  Expressions_seen* expressions_seen_;
 };
 
 // When translating the gogo IR into trees, this is the context we
Index: gcc/go/expressions.cc
===================================================================
--- gcc/go/expressions.cc	(revision 156165)
+++ gcc/go/expressions.cc	(working copy)
@@ -5126,7 +5126,7 @@ Builtin_call_expression::Builtin_call_ex
 }
 
 // Lower a builtin call expression.  This turns new and make into
-// specific expressions.
+// specific expressions.  We also convert to a constant if we can.
 
 Expression*
 Builtin_call_expression::do_lower(Gogo*, int)
@@ -5181,6 +5181,20 @@ Builtin_call_expression::do_lower(Gogo*,
 	    }
 	}
     }
+  else if (this->is_constant())
+    {
+      mpz_t ival;
+      mpz_init(ival);
+      Type* type;
+      if (this->integer_constant_value(true, ival, &type))
+	{
+	  Expression* ret = Expression::make_integer(&ival, type,
+						     this->location());
+	  mpz_clear(ival);
+	  return ret;
+	}
+      mpz_clear(ival);
+    }
 
   return this;
 }
@@ -6756,14 +6770,16 @@ class Call_result_expression : public Ex
   bool is_being_copied_;
 };
 
-// Traverse a call result.  We only traverse the call expression for
-// index 0, to avoid traversing it multiple times.
+// Traverse a call result.
 
 int
 Call_result_expression::do_traverse(Traverse* traverse)
 {
-  if (this->index_ > 0)
-    return TRAVERSE_CONTINUE;
+  if (traverse->remember_expression(this->call_))
+    {
+      // We have already traversed the call expression.
+      return TRAVERSE_CONTINUE;
+    }
   return Expression::traverse(&this->call_, traverse);
 }
 

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