diff mbox

Go patch committed: Only make function descriptors if needed

Message ID mcr61x8miee.fsf@iant-glaptop.roam.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor June 21, 2013, 12:12 a.m. UTC
This is a follow up to the last big patch, adding function descriptors
to the Go frontend.  This patch adds another compilation pass to only
create the function descriptors if they are needed: if the function is
exported, or if it is referenced in some way other than calling it.
Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline.

Ian
diff mbox

Patch

diff -r a4bae46290c3 go/expressions.cc
--- a/go/expressions.cc	Thu Jun 20 13:21:55 2013 -0700
+++ b/go/expressions.cc	Thu Jun 20 17:04:56 2013 -0700
@@ -1385,62 +1385,24 @@ 
   return new Func_expression(function, closure, location);
 }
 
-// A function descriptor.  A function descriptor is a struct with a
-// single field pointing to the function code.  This is used for
-// functions without closures.
-
-class Func_descriptor_expression : public Expression
-{
- public:
-  Func_descriptor_expression(Named_object* fn, Named_object* dfn)
-    : Expression(EXPRESSION_FUNC_DESCRIPTOR, fn->location()),
-      fn_(fn), dfn_(dfn), dvar_(NULL)
-  {
-    go_assert(!fn->is_function() || !fn->func_value()->needs_closure());
-  }
-
-  // Make the function descriptor type, so that it can be converted.
-  static void
-  make_func_descriptor_type();
-
- protected:
-  int
-  do_traverse(Traverse*)
-  { return TRAVERSE_CONTINUE; }
-
-  Type*
-  do_type();
-
-  void
-  do_determine_type(const Type_context*)
-  { }
-
-  Expression*
-  do_copy()
-  { return Expression::make_func_descriptor(this->fn_, this->dfn_); }
-
-  bool
-  do_is_addressable() const
-  { return true; }
-
-  tree
-  do_get_tree(Translate_context*);
-
-  void
-  do_dump_expression(Ast_dump_context* context) const
-  { context->ostream() << "[descriptor " << this->fn_->name() << "]"; }
-
- private:
-  // The type of all function descriptors.
-  static Type* descriptor_type;
-
-  // The function for which this is the descriptor.
-  Named_object* fn_;
-  // The descriptor function.
-  Named_object* dfn_;
-  // The descriptor variable.
-  Bvariable* dvar_;
-};
+// Class Func_descriptor_expression.
+
+// Constructor.
+
+Func_descriptor_expression::Func_descriptor_expression(Named_object* fn)
+  : Expression(EXPRESSION_FUNC_DESCRIPTOR, fn->location()),
+    fn_(fn), dfn_(NULL), dvar_(NULL)
+{
+  go_assert(!fn->is_function() || !fn->func_value()->needs_closure());
+}
+
+// Traversal.
+
+int
+Func_descriptor_expression::do_traverse(Traverse*)
+{
+  return TRAVERSE_CONTINUE;
+}
 
 // All function descriptors have the same type.
 
@@ -1464,6 +1426,18 @@ 
   return Func_descriptor_expression::descriptor_type;
 }
 
+// Copy a Func_descriptor_expression;
+
+Expression*
+Func_descriptor_expression::do_copy()
+{
+  Func_descriptor_expression* fde =
+    Expression::make_func_descriptor(this->fn_);
+  if (this->dfn_ != NULL)
+    fde->set_descriptor_wrapper(this->dfn_);
+  return fde;
+}
+
 // The tree for a function descriptor.
 
 tree
@@ -1519,12 +1493,20 @@ 
   return var_to_tree(bvar);
 }
 
+// Print a function descriptor expression.
+
+void
+Func_descriptor_expression::do_dump_expression(Ast_dump_context* context) const
+{
+  context->ostream() << "[descriptor " << this->fn_->name() << "]";
+}
+
 // Make a function descriptor expression.
 
-Expression*
-Expression::make_func_descriptor(Named_object* fn, Named_object* dfn)
-{
-  return new Func_descriptor_expression(fn, dfn);
+Func_descriptor_expression*
+Expression::make_func_descriptor(Named_object* fn)
+{
+  return new Func_descriptor_expression(fn);
 }
 
 // Make the function descriptor type, so that it can be converted.
diff -r a4bae46290c3 go/expressions.h
--- a/go/expressions.h	Thu Jun 20 13:21:55 2013 -0700
+++ b/go/expressions.h	Thu Jun 20 17:04:56 2013 -0700
@@ -32,6 +32,7 @@ 
 class Binary_expression;
 class Call_expression;
 class Func_expression;
+class Func_descriptor_expression;
 class Unknown_expression;
 class Index_expression;
 class Map_index_expression;
@@ -161,10 +162,9 @@ 
   // Make a function descriptor, an immutable struct with a single
   // field that points to the function code.  This may only be used
   // with functions that do not have closures.  FN is the function for
-  // which we are making the descriptor.  DFN is the descriptor
-  // function wrapper.
-  static Expression*
-  make_func_descriptor(Named_object* fn, Named_object* dfn);
+  // which we are making the descriptor.
+  static Func_descriptor_expression*
+  make_func_descriptor(Named_object* fn);
 
   // Make a reference to the code of a function.  This is used to set
   // descriptor and closure fields.
@@ -1562,6 +1562,63 @@ 
   Expression* closure_;
 };
 
+// A function descriptor.  A function descriptor is a struct with a
+// single field pointing to the function code.  This is used for
+// functions without closures.
+
+class Func_descriptor_expression : public Expression
+{
+ public:
+  Func_descriptor_expression(Named_object* fn);
+
+  // Set the descriptor wrapper.
+  void
+  set_descriptor_wrapper(Named_object* dfn)
+  {
+    go_assert(this->dfn_ == NULL);
+    this->dfn_ = dfn;
+  }
+
+  // Make the function descriptor type, so that it can be converted.
+  static void
+  make_func_descriptor_type();
+
+ protected:
+  int
+  do_traverse(Traverse*);
+
+  Type*
+  do_type();
+
+  void
+  do_determine_type(const Type_context*)
+  { }
+
+  Expression*
+  do_copy();
+
+  bool
+  do_is_addressable() const
+  { return true; }
+
+  tree
+  do_get_tree(Translate_context*);
+
+  void
+  do_dump_expression(Ast_dump_context* context) const;
+
+ private:
+  // The type of all function descriptors.
+  static Type* descriptor_type;
+
+  // The function for which this is the descriptor.
+  Named_object* fn_;
+  // The descriptor function.
+  Named_object* dfn_;
+  // The descriptor variable.
+  Bvariable* dvar_;
+};
+
 // A reference to an unknown name.
 
 class Unknown_expression : public Parser_expression
diff -r a4bae46290c3 go/go.cc
--- a/go/go.cc	Thu Jun 20 13:21:55 2013 -0700
+++ b/go/go.cc	Thu Jun 20 17:04:56 2013 -0700
@@ -91,6 +91,9 @@ 
   // form which is easier to use.
   ::gogo->lower_parse_tree();
 
+  // Create function descriptors as needed.
+  ::gogo->create_function_descriptors();
+
   // Write out queued up functions for hash and comparison of types.
   ::gogo->write_specific_type_functions();
 
diff -r a4bae46290c3 go/gogo.cc
--- a/go/gogo.cc	Thu Jun 20 13:21:55 2013 -0700
+++ b/go/gogo.cc	Thu Jun 20 17:04:56 2013 -0700
@@ -1608,14 +1608,6 @@ 
 {
   no->func_value()->set_closure_type();
 
-  // Make sure that every externally visible function has a
-  // descriptor, so that packages that import this one can refer to
-  // it.
-  if (!Gogo::is_hidden_name(no->name())
-      && !no->func_value()->is_method()
-      && !no->func_value()->is_descriptor_wrapper())
-    no->func_value()->descriptor(this->gogo_, no);
-
   go_assert(this->function_ == NULL);
   this->function_ = no;
   int t = no->func_value()->traverse(this);
@@ -1703,10 +1695,136 @@ 
 void
 Gogo::lower_parse_tree()
 {
-  // Create a function descriptor for any function that is declared in
-  // this package.  This is so that we have a descriptor for functions
-  // written in assembly.  Gather the descriptors first so that we
-  // don't add declarations while looping over them.
+  Lower_parse_tree lower_parse_tree(this, NULL);
+  this->traverse(&lower_parse_tree);
+}
+
+// Lower a block.
+
+void
+Gogo::lower_block(Named_object* function, Block* block)
+{
+  Lower_parse_tree lower_parse_tree(this, function);
+  block->traverse(&lower_parse_tree);
+}
+
+// Lower an expression.  INSERTER may be NULL, in which case the
+// expression had better not need to create any temporaries.
+
+void
+Gogo::lower_expression(Named_object* function, Statement_inserter* inserter,
+		       Expression** pexpr)
+{
+  Lower_parse_tree lower_parse_tree(this, function);
+  if (inserter != NULL)
+    lower_parse_tree.set_inserter(inserter);
+  lower_parse_tree.expression(pexpr);
+}
+
+// Lower a constant.  This is called when lowering a reference to a
+// constant.  We have to make sure that the constant has already been
+// lowered.
+
+void
+Gogo::lower_constant(Named_object* no)
+{
+  go_assert(no->is_const());
+  Lower_parse_tree lower(this, NULL);
+  lower.constant(no, false);
+}
+
+// Traverse the tree to create function descriptors as needed.
+
+class Create_function_descriptors : public Traverse
+{
+ public:
+  Create_function_descriptors(Gogo* gogo)
+    : Traverse(traverse_functions | traverse_expressions),
+      gogo_(gogo)
+  { }
+
+  int
+  function(Named_object*);
+
+  int
+  expression(Expression**);
+
+ private:
+  Gogo* gogo_;
+};
+
+// Create a descriptor for every top-level exported function.
+
+int
+Create_function_descriptors::function(Named_object* no)
+{
+  if (no->is_function()
+      && no->func_value()->enclosing() == NULL
+      && !no->func_value()->is_method()
+      && !no->func_value()->is_descriptor_wrapper()
+      && !Gogo::is_hidden_name(no->name()))
+    no->func_value()->descriptor(this->gogo_, no);
+
+  return TRAVERSE_CONTINUE;
+}
+
+// If we see a function referenced in any way other than calling it,
+// create a descriptor for it.
+
+int
+Create_function_descriptors::expression(Expression** pexpr)
+{
+  Expression* expr = *pexpr;
+
+  Func_expression* fe = expr->func_expression();
+  if (fe != NULL)
+    {
+      // We would not get here for a call to this function, so this is
+      // a reference to a function other than calling it.  We need a
+      // descriptor.
+      if (fe->closure() != NULL)
+	return TRAVERSE_CONTINUE;
+      Named_object* no = fe->named_object();
+      if (no->is_function() && !no->func_value()->is_method())
+	no->func_value()->descriptor(this->gogo_, no);
+      else if (no->is_function_declaration()
+	       && !no->func_declaration_value()->type()->is_method()
+	       && !Linemap::is_predeclared_location(no->location()))
+	no->func_declaration_value()->descriptor(this->gogo_, no);
+      return TRAVERSE_CONTINUE;
+    }
+
+  Call_expression* ce = expr->call_expression();
+  if (ce != NULL)
+    {
+      Expression* fn = ce->fn();
+      if (fn->func_expression() != NULL)
+	{
+	  // Traverse the arguments but not the function.
+	  Expression_list* args = ce->args();
+	  if (args != NULL)
+	    {
+	      if (args->traverse(this) == TRAVERSE_EXIT)
+		return TRAVERSE_EXIT;
+	    }
+	  return TRAVERSE_SKIP_COMPONENTS;
+	}
+    }
+
+  return TRAVERSE_CONTINUE;
+}
+
+// Create function descriptors as needed.  We need a function
+// descriptor for all exported functions and for all functions that
+// are referenced without being called.
+
+void
+Gogo::create_function_descriptors()
+{
+  // Create a function descriptor for any exported function that is
+  // declared in this package.  This is so that we have a descriptor
+  // for functions written in assembly.  Gather the descriptors first
+  // so that we don't add declarations while looping over them.
   std::vector<Named_object*> fndecls;
   Bindings* b = this->package_->bindings();
   for (Bindings::const_declarations_iterator p = b->begin_declarations();
@@ -1716,7 +1834,8 @@ 
       Named_object* no = p->second;
       if (no->is_function_declaration()
 	  && !no->func_declaration_value()->type()->is_method()
-	  && !Linemap::is_predeclared_location(no->location()))
+	  && !Linemap::is_predeclared_location(no->location())
+	  && !Gogo::is_hidden_name(no->name()))
 	fndecls.push_back(no);
     }
   for (std::vector<Named_object*>::const_iterator p = fndecls.begin();
@@ -1725,42 +1844,8 @@ 
     (*p)->func_declaration_value()->descriptor(this, *p);
   fndecls.clear();
 
-  Lower_parse_tree lower_parse_tree(this, NULL);
-  this->traverse(&lower_parse_tree);
-}
-
-// Lower a block.
-
-void
-Gogo::lower_block(Named_object* function, Block* block)
-{
-  Lower_parse_tree lower_parse_tree(this, function);
-  block->traverse(&lower_parse_tree);
-}
-
-// Lower an expression.  INSERTER may be NULL, in which case the
-// expression had better not need to create any temporaries.
-
-void
-Gogo::lower_expression(Named_object* function, Statement_inserter* inserter,
-		       Expression** pexpr)
-{
-  Lower_parse_tree lower_parse_tree(this, function);
-  if (inserter != NULL)
-    lower_parse_tree.set_inserter(inserter);
-  lower_parse_tree.expression(pexpr);
-}
-
-// Lower a constant.  This is called when lowering a reference to a
-// constant.  We have to make sure that the constant has already been
-// lowered.
-
-void
-Gogo::lower_constant(Named_object* no)
-{
-  go_assert(no->is_const());
-  Lower_parse_tree lower(this, NULL);
-  lower.constant(no, false);
+  Create_function_descriptors cfd(this);
+  this->traverse(&cfd);
 }
 
 // Look for interface types to finalize methods of inherited
@@ -3559,7 +3644,9 @@ 
     }
 
   gogo->add_statement(s);
-  gogo->add_block(gogo->finish_block(loc), loc);
+  Block* b = gogo->finish_block(loc);
+  gogo->add_block(b, loc);
+  gogo->lower_block(dno, b);
   gogo->finish_function(loc);
 
   return dno;
@@ -3576,13 +3663,18 @@ 
   go_assert(!this->is_descriptor_wrapper_);
   if (this->descriptor_ == NULL)
     {
-      Named_object* dno;
-      if (no->package() != NULL
-	  || Linemap::is_predeclared_location(no->location()))
-	dno = NULL;
-      else
-	dno = Function::make_descriptor_wrapper(gogo, no, this->type_);
-      this->descriptor_ = Expression::make_func_descriptor(no, dno);
+      // Make and record the descriptor first, so that when we lower
+      // the descriptor wrapper we don't try to make it again.
+      Func_descriptor_expression* descriptor =
+	Expression::make_func_descriptor(no);
+      this->descriptor_ = descriptor;
+      if (no->package() == NULL
+	  && !Linemap::is_predeclared_location(no->location()))
+	{
+	  Named_object* dno = Function::make_descriptor_wrapper(gogo, no,
+								this->type_);
+	  descriptor->set_descriptor_wrapper(dno);
+	}
     }
   return this->descriptor_;
 }
@@ -4127,13 +4219,18 @@ 
   go_assert(!this->fntype_->is_method());
   if (this->descriptor_ == NULL)
     {
-      Named_object* dno;
-      if (no->package() != NULL
-	  || Linemap::is_predeclared_location(no->location()))
-	dno = NULL;
-      else
-	dno = Function::make_descriptor_wrapper(gogo, no, this->fntype_);
-      this->descriptor_ = Expression::make_func_descriptor(no, dno);
+      // Make and record the descriptor first, so that when we lower
+      // the descriptor wrapper we don't try to make it again.
+      Func_descriptor_expression* descriptor =
+	Expression::make_func_descriptor(no);
+      this->descriptor_ = descriptor;
+      if (no->package() == NULL
+	  && !Linemap::is_predeclared_location(no->location()))
+	{
+	  Named_object* dno = Function::make_descriptor_wrapper(gogo, no,
+								this->fntype_);
+	  descriptor->set_descriptor_wrapper(dno);
+	}
     }
   return this->descriptor_;
 }
diff -r a4bae46290c3 go/gogo.h
--- a/go/gogo.h	Thu Jun 20 13:21:55 2013 -0700
+++ b/go/gogo.h	Thu Jun 20 17:04:56 2013 -0700
@@ -476,6 +476,10 @@ 
   void
   lower_constant(Named_object*);
 
+  // Create all necessary function descriptors.
+  void
+  create_function_descriptors();
+
   // Finalize the method lists and build stub methods for named types.
   void
   finalize_methods();
@@ -1164,15 +1168,15 @@ 
   // is NULL unless we actually need a defer stack.
   Temporary_statement* defer_stack_;
   // True if the result variables are named.
-  bool results_are_named_;
+  bool results_are_named_ : 1;
   // True if this method should not be included in the type descriptor.
-  bool nointerface_;
+  bool nointerface_ : 1;
   // True if this function calls the predeclared recover function.
-  bool calls_recover_;
+  bool calls_recover_ : 1;
   // True if this a thunk built for a function which calls recover.
-  bool is_recover_thunk_;
+  bool is_recover_thunk_ : 1;
   // True if this function already has a recover thunk.
-  bool has_recover_thunk_;
+  bool has_recover_thunk_ : 1;
   // True if this function should be put in a unique section.  This is
   // turned on for field tracking.
   bool in_unique_section_ : 1;