This patch to the Go frontend permits inlining functions that use
temporary statements and references.  This increases the number of
inlinable functions from 439 to 455.  An example is math/bits.Mul32,
which uses temporaries to handle the
tuple assignment.  Bootstrapped and ran Go testsuite on
x86_64-pc-linux-gnu.  Committed to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE     (revision 271983)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -1,4 +1,4 @@
-bc7374913367fba9b10dc284af87eb539fb6c5b2
+015785baa74629baafe520367b9c71707366c6eb
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/export.cc
===================================================================
--- gcc/go/gofrontend/export.cc (revision 271976)
+++ gcc/go/gofrontend/export.cc (working copy)
@@ -6,15 +6,14 @@
 
 #include "go-system.h"
 
-#include "go-sha1.h"
 #include "go-c.h"
-
+#include "go-diagnostics.h"
+#include "go-sha1.h"
 #include "gogo.h"
 #include "types.h"
 #include "expressions.h"
 #include "statements.h"
 #include "export.h"
-
 #include "go-linemap.h"
 #include "backend.h"
 
@@ -1297,3 +1296,33 @@ Stream_to_section::do_write(const char*
 {
   this->backend_->write_export_data (bytes, length);
 }
+
+// Class Export_function_body.
+
+// Record a temporary statement.
+
+unsigned int
+Export_function_body::record_temporary(const Temporary_statement* temp)
+{
+  unsigned int ret = this->next_temporary_index_;
+  if (ret > 0x7fffffff)
+    go_error_at(temp->location(),
+               "too many temporary statements in export data");
+  ++this->next_temporary_index_;
+  std::pair<const Temporary_statement*, unsigned int> val(temp, ret);
+  std::pair<Unordered_map(const Temporary_statement*, unsigned int)::iterator,
+           bool> ins = this->temporary_indexes_.insert(val);
+  go_assert(ins.second);
+  return ret;
+}
+
+// Return the index of a temporary statement.
+
+unsigned int
+Export_function_body::temporary_index(const Temporary_statement* temp)
+{
+  Unordered_map(const Temporary_statement*, unsigned int)::const_iterator p =
+    this->temporary_indexes_.find(temp);
+  go_assert(p != this->temporary_indexes_.end());
+  return p->second;
+}
Index: gcc/go/gofrontend/export.h
===================================================================
--- gcc/go/gofrontend/export.h  (revision 271891)
+++ gcc/go/gofrontend/export.h  (working copy)
@@ -20,6 +20,7 @@ class Type;
 class Package;
 class Import_init_set;
 class Backend;
+class Temporary_statement;
 
 // Codes used for the builtin types.  These are all negative to make
 // them easily distinct from the codes assigned by Export::write_type.
@@ -307,7 +308,8 @@ class Export_function_body : public Stri
 {
  public:
   Export_function_body(Export* exp, int indent)
-    : exp_(exp), type_context_(NULL), indent_(indent)
+    : exp_(exp), body_(), type_context_(NULL), next_temporary_index_(0),
+      temporary_indexes_(), indent_(indent)
   { }
 
   // Write a character to the body.
@@ -363,6 +365,14 @@ class Export_function_body : public Stri
   package_index(const Package* p) const
   { return this->exp_->package_index(p); }
 
+  // Record a temporary statement and return its index.
+  unsigned int
+  record_temporary(const Temporary_statement*);
+
+  // Return the index of a temporary statement.
+  unsigned int
+  temporary_index(const Temporary_statement*);
+
   // Return a reference to the completed body.
   const std::string&
   body() const
@@ -375,6 +385,10 @@ class Export_function_body : public Stri
   std::string body_;
   // Current type context.  Used to avoid duplicate type conversions.
   Type* type_context_;
+  // Index to give to next temporary statement.
+  unsigned int next_temporary_index_;
+  // Map temporary statements to indexes.
+  Unordered_map(const Temporary_statement*, unsigned int) temporary_indexes_;
   // Current indentation level: the number of spaces before each statement.
   int indent_;
 };
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc    (revision 271983)
+++ gcc/go/gofrontend/expressions.cc    (working copy)
@@ -1025,6 +1025,57 @@ Temporary_reference_expression::do_addre
   this->statement_->set_is_address_taken();
 }
 
+// Export a reference to a temporary.
+
+void
+Temporary_reference_expression::do_export(Export_function_body* efb) const
+{
+  unsigned int idx = efb->temporary_index(this->statement_);
+  char buf[50];
+  snprintf(buf, sizeof buf, "$t%u", idx);
+  efb->write_c_string(buf);
+}
+
+// Import a reference to a temporary.
+
+Expression*
+Temporary_reference_expression::do_import(Import_function_body* ifb,
+                                         Location loc)
+{
+  std::string id = ifb->read_identifier();
+  go_assert(id[0] == '$' && id[1] == 't');
+  const char *p = id.c_str();
+  char *end;
+  long idx = strtol(p + 2, &end, 10);
+  if (*end != '\0' || idx > 0x7fffffff)
+    {
+      if (!ifb->saw_error())
+       go_error_at(loc,
+                   ("invalid export data for %qs: "
+                    "invalid temporary reference index at %lu"),
+                   ifb->name().c_str(),
+                   static_cast<unsigned long>(ifb->off()));
+      ifb->set_saw_error();
+      return Expression::make_error(loc);
+    }
+
+  Temporary_statement* temp =
+    ifb->temporary_statement(static_cast<unsigned int>(idx));
+  if (temp == NULL)
+    {
+      if (!ifb->saw_error())
+       go_error_at(loc,
+                   ("invalid export data for %qs: "
+                    "undefined temporary reference index at %lu"),
+                   ifb->name().c_str(),
+                   static_cast<unsigned long>(ifb->off()));
+      ifb->set_saw_error();
+      return Expression::make_error(loc);
+    }
+
+  return Expression::make_temporary_reference(temp, loc);
+}
+
 // Get a backend expression referring to the variable.
 
 Bexpression*
@@ -17819,6 +17870,10 @@ Expression::import_expression_without_su
     }
   if (ifb->saw_error())
     return Expression::make_error(loc);
+
+  if (ifb->match_c_string("$t"))
+    return Temporary_reference_expression::do_import(ifb, loc);
+
   return Expression::import_identifier(ifb, loc);
 }
 
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h     (revision 271976)
+++ gcc/go/gofrontend/expressions.h     (working copy)
@@ -1531,6 +1531,9 @@ class Temporary_reference_expression : p
   set_is_lvalue()
   { this->is_lvalue_ = true; }
 
+  static Expression*
+  do_import(Import_function_body*, Location);
+
  protected:
   Type*
   do_type();
@@ -1543,6 +1546,13 @@ class Temporary_reference_expression : p
   do_copy()
   { return make_temporary_reference(this->statement_, this->location()); }
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
+  void
+  do_export(Export_function_body*) const;
+
   bool
   do_is_addressable() const
   { return true; }
Index: gcc/go/gofrontend/import.cc
===================================================================
--- gcc/go/gofrontend/import.cc (revision 271976)
+++ gcc/go/gofrontend/import.cc (working copy)
@@ -1611,3 +1611,34 @@ Import_function_body::read_type()
 
   return type;
 }
+
+// Record the index of a temporary statement.
+
+void
+Import_function_body::record_temporary(Temporary_statement* temp,
+                                      unsigned int idx)
+{
+  size_t have = this->temporaries_.size();
+  if (static_cast<size_t>(idx) >= have)
+    {
+      size_t want;
+      if (have == 0)
+       want = 8;
+      else if (have < 256)
+       want = have * 2;
+      else
+       want = have + 64;
+      this->temporaries_.resize(want, NULL);
+    }
+  this->temporaries_[idx] = temp;
+}
+
+// Return a temporary statement given an index.
+
+Temporary_statement*
+Import_function_body::temporary_statement(unsigned int idx)
+{
+  if (static_cast<size_t>(idx) >= this->temporaries_.size())
+    return NULL;
+  return this->temporaries_[idx];
+}
Index: gcc/go/gofrontend/import.h
===================================================================
--- gcc/go/gofrontend/import.h  (revision 271891)
+++ gcc/go/gofrontend/import.h  (working copy)
@@ -587,7 +587,8 @@ class Import_function_body : public Impo
                       const std::string& body, size_t off, Block* block,
                       int indent)
     : gogo_(gogo), imp_(imp), named_object_(named_object), body_(body),
-      off_(off), block_(block), indent_(indent), saw_error_(false)
+      off_(off), block_(block), indent_(indent), temporaries_(),
+      saw_error_(false)
   { }
 
   // The IR.
@@ -695,6 +696,14 @@ class Import_function_body : public Impo
   version() const
   { return this->imp_->version(); }
 
+  // Record the index of a temporary statement.
+  void
+  record_temporary(Temporary_statement*, unsigned int);
+
+  // Return a temporary statement given an index.
+  Temporary_statement*
+  temporary_statement(unsigned int);
+
   // Implement Import_expression.
   Import_function_body*
   ifb()
@@ -736,6 +745,8 @@ class Import_function_body : public Impo
   Block* block_;
   // Current expected indentation level.
   int indent_;
+  // Temporary statements by index.
+  std::vector<Temporary_statement*> temporaries_;
   // Whether we've seen an error.  Used to avoid reporting excess
   // errors.
   bool saw_error_;
Index: gcc/go/gofrontend/statements.cc
===================================================================
--- gcc/go/gofrontend/statements.cc     (revision 271983)
+++ gcc/go/gofrontend/statements.cc     (working copy)
@@ -155,6 +155,8 @@ Statement::import_statement(Import_funct
       ifb->advance(6);
       return Statement::make_return_statement(NULL, loc);
     }
+  else if (ifb->match_c_string("var $t"))
+    return Temporary_statement::do_import(ifb, loc);
   else if (ifb->match_c_string("var "))
     return Variable_declaration_statement::do_import(ifb, loc);
 
@@ -693,6 +695,92 @@ Statement::make_temporary(Type* type, Ex
   return new Temporary_statement(type, init, location);
 }
 
+// Export a temporary statement.
+
+void
+Temporary_statement::do_export_statement(Export_function_body* efb)
+{
+  unsigned int idx = efb->record_temporary(this);
+  char buf[100];
+  snprintf(buf, sizeof buf, "var $t%u", idx);
+  efb->write_c_string(buf);
+  if (this->type_ != NULL)
+    {
+      efb->write_c_string(" ");
+      efb->write_type(this->type_);
+    }
+  if (this->init_ != NULL)
+    {
+      efb->write_c_string(" = ");
+
+      go_assert(efb->type_context() == NULL);
+      efb->set_type_context(this->type_);
+
+      this->init_->export_expression(efb);
+
+      efb->set_type_context(NULL);
+    }
+}
+
+// Import a temporary statement.
+
+Statement*
+Temporary_statement::do_import(Import_function_body* ifb, Location loc)
+{
+  ifb->require_c_string("var ");
+  std::string id = ifb->read_identifier();
+  go_assert(id[0] == '$' && id[1] == 't');
+  const char *p = id.c_str();
+  char *end;
+  long idx = strtol(p + 2, &end, 10);
+  if (*end != '\0' || idx > 0x7fffffff)
+    {
+      if (!ifb->saw_error())
+       go_error_at(loc,
+                   ("invalid export data for %qs: "
+                    "bad temporary statement index at %lu"),
+                   ifb->name().c_str(),
+                   static_cast<unsigned long>(ifb->off()));
+      ifb->set_saw_error();
+      return Statement::make_error_statement(loc);
+    }
+
+  Type* type = NULL;
+  if (!ifb->match_c_string(" = "))
+    {
+      ifb->require_c_string(" ");
+      type = ifb->read_type();
+    }
+  Expression* init = NULL;
+  if (ifb->match_c_string(" = "))
+    {
+      ifb->advance(3);
+      init = Expression::import_expression(ifb, loc);
+      if (type != NULL)
+       {
+         Type_context context(type, false);
+         init->determine_type(&context);
+       }
+    }
+  if (type == NULL && init == NULL)
+    {
+      if (!ifb->saw_error())
+       go_error_at(loc,
+                   ("invalid export data for %qs: "
+                    "temporary statement has neither type nor init at %lu"),
+                   ifb->name().c_str(),
+                   static_cast<unsigned long>(ifb->off()));
+      ifb->set_saw_error();
+      return Statement::make_error_statement(loc);
+    }
+
+  Temporary_statement* temp = Statement::make_temporary(type, init, loc);
+
+  ifb->record_temporary(temp, static_cast<unsigned int>(idx));
+
+  return temp;
+}
+
 // The Move_subexpressions class is used to move all top-level
 // subexpressions of an expression.  This is used for things like
 // index expressions in which we must evaluate the index value before
Index: gcc/go/gofrontend/statements.h
===================================================================
--- gcc/go/gofrontend/statements.h      (revision 271822)
+++ gcc/go/gofrontend/statements.h      (working copy)
@@ -739,6 +739,10 @@ class Temporary_statement : public State
   Bvariable*
   get_backend_variable(Translate_context*) const;
 
+  // Import the declaration of a temporary.
+  static Statement*
+  do_import(Import_function_body*, Location);
+
  protected:
   int
   do_traverse(Traverse*);
@@ -752,6 +756,13 @@ class Temporary_statement : public State
   void
   do_check_types(Gogo*);
 
+  int
+  do_inlining_cost()
+  { return 1; }
+
+  void
+  do_export_statement(Export_function_body*);
+
   Statement*
   do_flatten(Gogo*, Named_object*, Block*, Statement_inserter*);
 

Reply via email to