compiler: permit inlining functions with labels and goto statements
authorIan Lance Taylor <ian@gcc.gnu.org>
Mon, 10 Jun 2019 21:02:20 +0000 (21:02 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Mon, 10 Jun 2019 21:02:20 +0000 (21:02 +0000)
    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.

    Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/181197

From-SVN: r272131

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/export.cc
gcc/go/gofrontend/export.h
gcc/go/gofrontend/import.cc
gcc/go/gofrontend/import.h
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h

index 05959f2c55a2a0645529a756a2ed4a59c3e289bd..9dfa0670c5a962623ecfc05fc7aafd348b4988cc 100644 (file)
@@ -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 0890b0961f718363dd24aa847c32369236901817..a48ca0767e62c3fef7f0f071ce8739fbd8437a33 100644 (file)
@@ -1326,3 +1326,26 @@ Export_function_body::temporary_index(const Temporary_statement* temp)
   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 92d0180dde6a05506f1c66fab7a57e9b5c345ac5..910b1db43cdc92957577affa12afac9ceed38d38 100644 (file)
@@ -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 String_dump
  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 String_dump
   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 String_dump
   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 70e92ab9d3b5d034e758978c87349b0bd685bd7e..02c1c48191b04b61e1c6edfe690ba28dfaa9d2bb 100644 (file)
@@ -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(Temporary_statement* temp,
                                       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_statement(unsigned int idx)
     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 4afeb4a29588f76aa055c5f28b29c4c800550290..db51f726bf956b89d17482073ab2fb64dd2ce485 100644 (file)
@@ -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 Import_expression
                       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 Import_expression
   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 Import_expression
   { 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 Import_expression
   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 f680a0a1c3e079e120e662804546e8dfbb984b14..9ab11727fe93851fac03ed5ed714015b0d2c7987 100644 (file)
@@ -129,10 +129,15 @@ Statement::import_statement(Import_function_body* ifb, Location loc)
 {
   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_function_body* ifb, Location loc)
     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* expr, bool is_ignored)
 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_function_body* efb, Block* block)
 // 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_dump_context*) const
 
 // 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_context* context)
   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(Translate_context* context)
   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(Translate_context* context)
   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(Translate_context* context)
   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_function_body* efb)
   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_body* ifb, Location loc)
       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_body* ifb, Location loc)
          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 3e85243b084febf0d0587de9e18e1adb6b5f50df..432da30f7e6bc9447051eefd6ed31f8007caf3b6 100644 (file)
@@ -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 Statement
   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 Statement
   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;