Go patch committed: Permit inlining functions with labels and goto statements

Message ID CAOyqgcU_avEHdHerYjjUnZXG9XvahWhxQpsdY5-TNYTfGPBu=w@mail.gmail.com
State New
Headers show
Series
  • Go patch committed: Permit inlining functions with labels and goto statements
Related show

Commit Message

Ian Lance Taylor June 10, 2019, 9:02 p.m.
This patch to the Go fronted permits inlining functions with labels
and goto statements.  This permits inlining functions with for loops
and some switches, as they are lowered to if and goto statements
before exporting them.  This by itself only adds three new inlinable
functions in the standard library: sort.Search,
context.(*emptyCtx).String, and
cmd/go/internal/work.(*Builder).disableBuildID.  Bootstrapped and ran
Go testsuite on x86_64-pc-linux-gnu.  Committed to mainline.

Ian

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 272130)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-3f7dcb98df3ce1d4e02d0072fd21e70dc08351db
+11d96c36198b75b0485d16524d521e558cf03312
 
 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 272127)
+++ gcc/go/gofrontend/export.cc	(working copy)
@@ -1326,3 +1326,26 @@  Export_function_body::temporary_index(co
   go_assert(p != this->temporary_indexes_.end());
   return p->second;
 }
+
+// Return the index of an unnamed label.  If it doesn't already have
+// an index, give it one.
+
+unsigned int
+Export_function_body::unnamed_label_index(const Unnamed_label* label)
+{
+  unsigned int next = this->next_label_index_;
+  std::pair<const Unnamed_label*, unsigned int> val(label, next);
+  std::pair<Unordered_map(const Unnamed_label*, unsigned int)::iterator,
+	    bool> ins =
+    this->label_indexes_.insert(val);
+  if (!ins.second)
+    return ins.first->second;
+  else
+    {
+      if (next > 0x7fffffff)
+	go_error_at(label->location(),
+		    "too many unnamed labels in export data");
+      ++this->next_label_index_;
+      return next;
+    }
+}
Index: gcc/go/gofrontend/export.h
===================================================================
--- gcc/go/gofrontend/export.h	(revision 272127)
+++ gcc/go/gofrontend/export.h	(working copy)
@@ -21,6 +21,7 @@  class Package;
 class Import_init_set;
 class Backend;
 class Temporary_statement;
+class Unnamed_label;
 
 // Codes used for the builtin types.  These are all negative to make
 // them easily distinct from the codes assigned by Export::write_type.
@@ -309,7 +310,8 @@  class Export_function_body : public Stri
  public:
   Export_function_body(Export* exp, int indent)
     : exp_(exp), body_(), type_context_(NULL), next_temporary_index_(0),
-      temporary_indexes_(), indent_(indent)
+      temporary_indexes_(), next_label_index_(0), label_indexes_(),
+      indent_(indent)
   { }
 
   // Write a character to the body.
@@ -373,6 +375,11 @@  class Export_function_body : public Stri
   unsigned int
   temporary_index(const Temporary_statement*);
 
+  // Return the index of an unnamed label.  If it doesn't already have
+  // an index, give it one.
+  unsigned int
+  unnamed_label_index(const Unnamed_label*);
+
   // Return a reference to the completed body.
   const std::string&
   body() const
@@ -389,6 +396,10 @@  class Export_function_body : public Stri
   unsigned int next_temporary_index_;
   // Map temporary statements to indexes.
   Unordered_map(const Temporary_statement*, unsigned int) temporary_indexes_;
+  // Index to give to the next unnamed label.
+  unsigned int next_label_index_;
+  // Map unnamed labels to indexes.
+  Unordered_map(const Unnamed_label*, unsigned int) label_indexes_;
   // Current indentation level: the number of spaces before each statement.
   int indent_;
 };
Index: gcc/go/gofrontend/import.cc
===================================================================
--- gcc/go/gofrontend/import.cc	(revision 272127)
+++ gcc/go/gofrontend/import.cc	(working copy)
@@ -1612,6 +1612,19 @@  Import_function_body::read_type()
   return type;
 }
 
+// Return the next size to use for a vector mapping indexes to values.
+
+size_t
+Import_function_body::next_size(size_t have)
+{
+  if (have == 0)
+    return 8;
+  else if (have < 256)
+    return have * 2;
+  else
+    return have + 64;
+}
+
 // Record the index of a temporary statement.
 
 void
@@ -1619,16 +1632,11 @@  Import_function_body::record_temporary(T
 				       unsigned int idx)
 {
   size_t have = this->temporaries_.size();
-  if (static_cast<size_t>(idx) >= have)
+  while (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;
+      size_t want = Import_function_body::next_size(have);
       this->temporaries_.resize(want, NULL);
+      have = want;
     }
   this->temporaries_[idx] = temp;
 }
@@ -1642,3 +1650,25 @@  Import_function_body::temporary_statemen
     return NULL;
   return this->temporaries_[idx];
 }
+
+// Return an unnamed label given an index, defining the label if we
+// haven't seen it already.
+
+Unnamed_label*
+Import_function_body::unnamed_label(unsigned int idx, Location loc)
+{
+  size_t have = this->labels_.size();
+  while (static_cast<size_t>(idx) >= have)
+    {
+      size_t want = Import_function_body::next_size(have);
+      this->labels_.resize(want, NULL);
+      have = want;
+    }
+  Unnamed_label* label = this->labels_[idx];
+  if (label == NULL)
+    {
+      label = new Unnamed_label(loc);
+      this->labels_[idx] = label;
+    }
+  return label;
+}
Index: gcc/go/gofrontend/import.h
===================================================================
--- gcc/go/gofrontend/import.h	(revision 272127)
+++ gcc/go/gofrontend/import.h	(working copy)
@@ -18,6 +18,8 @@  class Named_object;
 class Named_type;
 class Expression;
 class Import_function_body;
+class Temporary_statement;
+class Unnamed_label;
 
 // Expressions can be imported either directly from import data (for
 // simple constant expressions that can appear in a const declaration
@@ -587,7 +589,7 @@  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), temporaries_(),
+      off_(off), block_(block), indent_(indent), temporaries_(), labels_(),
       saw_error_(false)
   { }
 
@@ -704,6 +706,11 @@  class Import_function_body : public Impo
   Temporary_statement*
   temporary_statement(unsigned int);
 
+  // Return an unnamed label given an index, defining the label if we
+  // haven't seen it already.
+  Unnamed_label*
+  unnamed_label(unsigned int, Location);
+
   // Implement Import_expression.
   Import_function_body*
   ifb()
@@ -730,6 +737,9 @@  class Import_function_body : public Impo
   { this->saw_error_ = true; }
 
  private:
+  static size_t
+  next_size(size_t);
+
   // The IR.
   Gogo* gogo_;
   // The importer.
@@ -747,6 +757,8 @@  class Import_function_body : public Impo
   int indent_;
   // Temporary statements by index.
   std::vector<Temporary_statement*> temporaries_;
+  // Unnamed labels by index.
+  std::vector<Unnamed_label*> labels_;
   // 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 272127)
+++ gcc/go/gofrontend/statements.cc	(working copy)
@@ -129,10 +129,15 @@  Statement::import_statement(Import_funct
 {
   if (ifb->match_c_string("{"))
     {
-      Block* block = Block_statement::do_import(ifb, loc);
+      bool is_lowered_for_statement;
+      Block* block = Block_statement::do_import(ifb, loc,
+						&is_lowered_for_statement);
       if (block == NULL)
 	return Statement::make_error_statement(loc);
-      return Statement::make_block_statement(block, loc);
+      Block_statement* s = Statement::make_block_statement(block, loc);
+      if (is_lowered_for_statement)
+	s->set_is_lowered_for_statement();
+      return s;
     }
   else if (ifb->match_c_string("return"))
     {
@@ -147,6 +152,10 @@  Statement::import_statement(Import_funct
     return Variable_declaration_statement::do_import(ifb, loc);
   else if (ifb->match_c_string("if "))
     return If_statement::do_import(ifb, loc);
+  else if (ifb->match_c_string(":"))
+    return Label_statement::do_import(ifb, loc);
+  else if (ifb->match_c_string("goto "))
+    return Goto_statement::do_import(ifb, loc);
 
   Expression* lhs = Expression::import_expression(ifb, loc);
   ifb->require_c_string(" = ");
@@ -2109,15 +2118,20 @@  Statement::make_statement(Expression* ex
 void
 Block_statement::do_export_statement(Export_function_body* efb)
 {
-  Block_statement::export_block(efb, this->block_);
+  Block_statement::export_block(efb, this->block_,
+				this->is_lowered_for_statement_);
 }
 
 void
-Block_statement::export_block(Export_function_body* efb, Block* block)
+Block_statement::export_block(Export_function_body* efb, Block* block,
+			      bool is_lowered_for_statement)
 {
   // We are already indented to the right position.
   char buf[50];
-  snprintf(buf, sizeof buf, "{ //%d\n",
+  efb->write_c_string("{");
+  if (is_lowered_for_statement)
+    efb->write_c_string(" /*for*/");
+  snprintf(buf, sizeof buf, " //%d\n",
 	   Linemap::location_to_line(block->start_location()));
   efb->write_c_string(buf);
 
@@ -2134,9 +2148,16 @@  Block_statement::export_block(Export_fun
 // Import a block statement, returning the block.
 
 Block*
-Block_statement::do_import(Import_function_body* ifb, Location loc)
+Block_statement::do_import(Import_function_body* ifb, Location loc,
+			   bool* is_lowered_for_statement)
 {
   go_assert(ifb->match_c_string("{"));
+  *is_lowered_for_statement = false;
+  if (ifb->match_c_string(" /*for*/"))
+    {
+      ifb->advance(8);
+      *is_lowered_for_statement = true;
+    }
   size_t nl = ifb->body().find('\n', ifb->off());
   if (nl == std::string::npos)
     {
@@ -2176,7 +2197,7 @@  Block_statement::do_dump_statement(Ast_d
 
 // Make a block statement.
 
-Statement*
+Block_statement*
 Statement::make_block_statement(Block* block, Location location)
 {
   return new Block_statement(block, location);
@@ -3344,6 +3365,61 @@  Goto_statement::do_get_backend(Translate
   return context->backend()->goto_statement(blabel, this->location());
 }
 
+// Export a goto statement.
+
+void
+Goto_statement::do_export_statement(Export_function_body *efb)
+{
+  efb->write_c_string("goto ");
+  efb->write_string(this->label_->name());
+}
+
+// Import a goto or goto unnamed statement.
+
+Statement*
+Goto_statement::do_import(Import_function_body* ifb, Location loc)
+{
+  ifb->require_c_string("goto ");
+  std::string id = ifb->read_identifier();
+  if (id[0] != '$')
+    {
+      Function* fn = ifb->function()->func_value();
+      Label* label = fn->add_label_reference(ifb->gogo(), id, loc, false);
+      return Statement::make_goto_statement(label, loc);
+    }
+  else
+    {
+      if (id[1] != 'l')
+	{
+	  if (!ifb->saw_error())
+	    go_error_at(loc,
+			("invalid export data for %qs: "
+			 "bad unnamed label at %lu"),
+			ifb->name().c_str(),
+			static_cast<unsigned long>(ifb->off()));
+	  ifb->set_saw_error();
+	  return Statement::make_error_statement(loc);
+	}
+      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 unnamed label index at %lu"),
+			ifb->name().c_str(),
+			static_cast<unsigned long>(ifb->off()));
+	  ifb->set_saw_error();
+	  return Statement::make_error_statement(loc);
+	}
+
+      Unnamed_label* label = ifb->unnamed_label(idx, loc);
+      return Statement::make_goto_unnamed_statement(label, loc);
+    }
+}
+
 // Dump the AST representation for a goto statement.
 
 void
@@ -3377,6 +3453,17 @@  Goto_unnamed_statement::do_get_backend(T
   return this->label_->get_goto(context, this->location());
 }
 
+// Export a goto unnamed statement.
+
+void
+Goto_unnamed_statement::do_export_statement(Export_function_body *efb)
+{
+  unsigned int index = efb->unnamed_label_index(this->label_);
+  char buf[100];
+  snprintf(buf, sizeof buf, "goto $l%u", index);
+  efb->write_c_string(buf);
+}
+
 // Dump the AST representation for an unnamed goto statement
 
 void
@@ -3424,6 +3511,64 @@  Label_statement::do_get_backend(Translat
   return context->backend()->label_definition_statement(blabel);
 }
 
+// Export a label.
+
+void
+Label_statement::do_export_statement(Export_function_body* efb)
+{
+  if (this->label_->is_dummy_label())
+    return;
+  // We use a leading colon, not a trailing one, to simplify import.
+  efb->write_c_string(":");
+  efb->write_string(this->label_->name());
+}
+
+// Import a label or an unnamed label.
+
+Statement*
+Label_statement::do_import(Import_function_body* ifb, Location loc)
+{
+  ifb->require_c_string(":");
+  std::string id = ifb->read_identifier();
+  if (id[0] != '$')
+    {
+      Function* fn = ifb->function()->func_value();
+      Label* label = fn->add_label_definition(ifb->gogo(), id, loc);
+      return Statement::make_label_statement(label, loc);
+    }
+  else
+    {
+      if (id[1] != 'l')
+	{
+	  if (!ifb->saw_error())
+	    go_error_at(loc,
+			("invalid export data for %qs: "
+			 "bad unnamed label at %lu"),
+			ifb->name().c_str(),
+			static_cast<unsigned long>(ifb->off()));
+	  ifb->set_saw_error();
+	  return Statement::make_error_statement(loc);
+	}
+      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 unnamed label index at %lu"),
+			ifb->name().c_str(),
+			static_cast<unsigned long>(ifb->off()));
+	  ifb->set_saw_error();
+	  return Statement::make_error_statement(loc);
+	}
+
+      Unnamed_label* label = ifb->unnamed_label(idx, loc);
+      return Statement::make_unnamed_label_statement(label);
+    }
+}
+
 // Dump the AST for a label definition statement.
 
 void
@@ -3462,6 +3607,18 @@  Unnamed_label_statement::do_get_backend(
   return this->label_->get_definition(context);
 }
 
+// Export an unnamed label.
+
+void
+Unnamed_label_statement::do_export_statement(Export_function_body* efb)
+{
+  unsigned int index = efb->unnamed_label_index(this->label_);
+  char buf[50];
+  // We use a leading colon, not a trailing one, to simplify import.
+  snprintf(buf, sizeof buf, ":$l%u", index);
+  efb->write_c_string(buf);
+}
+
 // Dump the AST representation for an unnamed label definition statement.
 
 void
@@ -3557,11 +3714,11 @@  If_statement::do_export_statement(Export
   efb->write_c_string("if ");
   this->cond_->export_expression(efb);
   efb->write_c_string(" ");
-  Block_statement::export_block(efb, this->then_block_);
+  Block_statement::export_block(efb, this->then_block_, false);
   if (this->else_block_ != NULL)
     {
       efb->write_c_string(" else ");
-      Block_statement::export_block(efb, this->else_block_);
+      Block_statement::export_block(efb, this->else_block_, false);
     }
 }
 
@@ -3588,9 +3745,22 @@  If_statement::do_import(Import_function_
       return Statement::make_error_statement(loc);
     }
 
-  Block* then_block = Block_statement::do_import(ifb, loc);
+  bool is_lowered_for_statement;
+  Block* then_block = Block_statement::do_import(ifb, loc,
+						 &is_lowered_for_statement);
   if (then_block == NULL)
     return Statement::make_error_statement(loc);
+  if (is_lowered_for_statement)
+    {
+      if (!ifb->saw_error())
+	go_error_at(ifb->location(),
+		    ("import error for %qs: "
+		     "unexpected lowered for in if statement at %lu"),
+		    ifb->name().c_str(),
+		    static_cast<unsigned long>(ifb->off()));
+      ifb->set_saw_error();
+      return Statement::make_error_statement(loc);
+    }
 
   Block* else_block = NULL;
   if (ifb->match_c_string(" else "))
@@ -3608,9 +3778,21 @@  If_statement::do_import(Import_function_
 	  return Statement::make_error_statement(loc);
 	}
 
-      else_block = Block_statement::do_import(ifb, loc);
+      else_block = Block_statement::do_import(ifb, loc,
+					      &is_lowered_for_statement);
       if (else_block == NULL)
 	return Statement::make_error_statement(loc);
+      if (is_lowered_for_statement)
+	{
+	  if (!ifb->saw_error())
+	    go_error_at(ifb->location(),
+			("import error for %qs: "
+			 "unexpected lowered for in if statement at %lu"),
+			ifb->name().c_str(),
+			static_cast<unsigned long>(ifb->off()));
+	  ifb->set_saw_error();
+	  return Statement::make_error_statement(loc);
+	}
     }
 
   return Statement::make_if_statement(cond, then_block, else_block, loc);
Index: gcc/go/gofrontend/statements.h
===================================================================
--- gcc/go/gofrontend/statements.h	(revision 272127)
+++ gcc/go/gofrontend/statements.h	(working copy)
@@ -187,7 +187,7 @@  class Statement
 
   // Make a block statement from a Block.  This is an embedded list of
   // statements which may also include variable definitions.
-  static Statement*
+  static Block_statement*
   make_block_statement(Block*, Location);
 
   // Make an increment statement.
@@ -956,11 +956,13 @@  class Block_statement : public Statement
 
   // Export a block for a block statement.
   static void
-  export_block(Export_function_body*, Block*);
+  export_block(Export_function_body*, Block*, bool is_lowered_for_statement);
 
   // Import a block statement, returning the block.
+  // *IS_LOWERED_FOR_STATEMENT reports whether this block statement
+  // was lowered from a for statement.
   static Block*
-  do_import(Import_function_body*, Location);
+  do_import(Import_function_body*, Location, bool* is_lowered_for_statement);
 
  protected:
   int
@@ -1409,6 +1411,10 @@  class Goto_statement : public Statement
   label() const
   { return this->label_; }
 
+  // Import a goto statement.
+  static Statement*
+  do_import(Import_function_body*, Location);
+
  protected:
   int
   do_traverse(Traverse*);
@@ -1423,6 +1429,13 @@  class Goto_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost()
+  { return 5; }
+
+  void
+  do_export_statement(Export_function_body*);
+
   void
   do_dump_statement(Ast_dump_context*) const;
 
@@ -1455,6 +1468,13 @@  class Goto_unnamed_statement : public St
   Bstatement*
   do_get_backend(Translate_context* context);
 
+  int
+  do_inlining_cost()
+  { return 5; }
+
+  void
+  do_export_statement(Export_function_body*);
+
   void
   do_dump_statement(Ast_dump_context*) const;
 
@@ -1477,6 +1497,10 @@  class Label_statement : public Statement
   label() const
   { return this->label_; }
 
+  // Import a label or unnamed label.
+  static Statement*
+  do_import(Import_function_body*, Location);
+
  protected:
   int
   do_traverse(Traverse*);
@@ -1484,6 +1508,13 @@  class Label_statement : public Statement
   Bstatement*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost()
+  { return 1; }
+
+  void
+  do_export_statement(Export_function_body*);
+
   void
   do_dump_statement(Ast_dump_context*) const;
 
@@ -1506,6 +1537,13 @@  class Unnamed_label_statement : public S
   Bstatement*
   do_get_backend(Translate_context* context);
 
+  int
+  do_inlining_cost()
+  { return 1; }
+
+  void
+  do_export_statement(Export_function_body*);
+
   void
   do_dump_statement(Ast_dump_context*) const;