compiler: inline call expressions and function references
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 5 Jun 2019 21:05:38 +0000 (21:05 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 5 Jun 2019 21:05:38 +0000 (21:05 +0000)
    Scan inlinable methods for references to global variables and
    functions (forgot to do that earlier).

    Track all packages mentioned by exports (that should have been done
    earlier too).

    Record assembler name in export data, so that we can inline calls to
    non-Go functions.  Modify gccgoimporter code to skip assembler name.

    This increases the number of inlinable functions in the standard
    library from 215 to 439.

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

From-SVN: r271976

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/export.cc
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/import.cc
libgo/go/go/internal/gccgoimporter/parser.go

index e9072a85851780f54658571c4e4885aad7e4a381..d596f02f3fef66f91ce942618efbd86fbff13afe 100644 (file)
@@ -1,4 +1,4 @@
-949c3b7aa603bc09e650d62e82c600b3463802f0
+2609f9b8420e2341fbbe40d7cf6af42b0fba7293
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 108fdac22e57b3c8d2540f2c6ebe355189240900..824f821be73856e28e736a86dbec9e2c97da7b65 100644 (file)
@@ -133,6 +133,11 @@ Collect_references_from_inline::expression(Expression** pexpr)
   if (fe != NULL)
     {
       Named_object* no = fe->named_object();
+
+      if (no->is_function_declaration()
+         && no->func_declaration_value()->type()->is_builtin())
+       return TRAVERSE_CONTINUE;
+
       std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
        this->exports_->insert(no);
 
@@ -247,6 +252,22 @@ Export::export_globals(const std::string& package_name,
          if ((*p)->is_function()
              && (*p)->func_value()->export_for_inlining())
            check_inline_refs.push_back(*p);
+         else if ((*p)->is_type())
+           {
+             const Bindings* methods = (*p)->type_value()->local_methods();
+             if (methods != NULL)
+               {
+                 for (Bindings::const_definitions_iterator pm =
+                        methods->begin_definitions();
+                      pm != methods->end_definitions();
+                      ++pm)
+                   {
+                     Function* fn = (*pm)->func_value();
+                     if (fn->export_for_inlining())
+                       check_inline_refs.push_back(*pm);
+                   }
+               }
+           }
        }
     }
 
@@ -282,6 +303,9 @@ Export::export_globals(const std::string& package_name,
        }
     }
 
+  // Track all imported packages mentioned in export data.
+  Unordered_set(const Package*) all_imports;
+
   // Export the symbols in sorted order.  That will reduce cases where
   // irrelevant changes to the source code affect the exported
   // interface.
@@ -291,15 +315,20 @@ Export::export_globals(const std::string& package_name,
   for (Unordered_set(Named_object*)::const_iterator p = exports.begin();
        p != exports.end();
        ++p)
-    sorted_exports.push_back(*p);
+    {
+      sorted_exports.push_back(*p);
+
+      const Package* pkg = (*p)->package();
+      if (pkg != NULL)
+       all_imports.insert(pkg);
+    }
 
   std::sort(sorted_exports.begin(), sorted_exports.end(), Sort_bindings());
 
   // Assign indexes to all exported types and types referenced by
   // exported types, and collect all packages mentioned.
-  Unordered_set(const Package*) type_imports;
   int unexported_type_index = this->prepare_types(&sorted_exports,
-                                                 &type_imports);
+                                                 &all_imports);
 
   // Although the export data is readable, at least this version is,
   // it is conceptually a binary format.  Start with a four byte
@@ -327,7 +356,7 @@ Export::export_globals(const std::string& package_name,
 
   this->write_packages(packages);
 
-  this->write_imports(imports, type_imports);
+  this->write_imports(imports, all_imports);
 
   this->write_imported_init_fns(package_name, import_init_fn,
                                imported_init_fns);
@@ -693,7 +722,7 @@ import_compare(const std::pair<std::string, Package*>& a,
 
 void
 Export::write_imports(const std::map<std::string, Package*>& imports,
-                     const Unordered_set(const Package*)& type_imports)
+                     const Unordered_set(const Package*)& all_imports)
 {
   // Sort the imports for more consistent output.
   Unordered_set(const Package*) seen;
@@ -729,8 +758,8 @@ Export::write_imports(const std::map<std::string, Package*>& imports,
   // Write out a separate list of indirectly imported packages.
   std::vector<const Package*> indirect_imports;
   for (Unordered_set(const Package*)::const_iterator p =
-        type_imports.begin();
-       p != type_imports.end();
+        all_imports.begin();
+       p != all_imports.end();
        ++p)
     {
       if (seen.find(*p) == seen.end())
index d7bf4d75c95234c357aa95da7bacfb2961ba63c5..1493ddc675f7bb903d0f46703cfbf1fbf3f7a1fc 100644 (file)
@@ -1356,6 +1356,29 @@ Func_expression::do_get_backend(Translate_context* context)
   return gogo->backend()->convert_expression(btype, bexpr, this->location());
 }
 
+// The cost of inlining a function reference.
+
+int
+Func_expression::do_inlining_cost() const
+{
+  // FIXME: We don't inline references to nested functions.
+  if (this->closure_ != NULL)
+    return 0x100000;
+  if (this->function_->is_function()
+      && this->function_->func_value()->enclosing() != NULL)
+    return 0x100000;
+
+  return 1;
+}
+
+// Export a reference to a function.
+
+void
+Func_expression::do_export(Export_function_body* efb) const
+{
+  Expression::export_name(efb, this->function_);
+}
+
 // Ast dump for function.
 
 void
@@ -10088,38 +10111,82 @@ void
 Builtin_call_expression::do_export(Export_function_body* efb) const
 {
   Numeric_constant nc;
-  if (!this->numeric_constant_value(&nc))
+  if (this->numeric_constant_value(&nc))
     {
-      go_error_at(this->location(), "value is not constant");
-      return;
-    }
+      if (nc.is_int())
+       {
+         mpz_t val;
+         nc.get_int(&val);
+         Integer_expression::export_integer(efb, val);
+         mpz_clear(val);
+       }
+      else if (nc.is_float())
+       {
+         mpfr_t fval;
+         nc.get_float(&fval);
+         Float_expression::export_float(efb, fval);
+         mpfr_clear(fval);
+       }
+      else if (nc.is_complex())
+       {
+         mpc_t cval;
+         nc.get_complex(&cval);
+         Complex_expression::export_complex(efb, cval);
+         mpc_clear(cval);
+       }
+      else
+       go_unreachable();
 
-  if (nc.is_int())
-    {
-      mpz_t val;
-      nc.get_int(&val);
-      Integer_expression::export_integer(efb, val);
-      mpz_clear(val);
+      // A trailing space lets us reliably identify the end of the number.
+      efb->write_c_string(" ");
     }
-  else if (nc.is_float())
-    {
-      mpfr_t fval;
-      nc.get_float(&fval);
-      Float_expression::export_float(efb, fval);
-      mpfr_clear(fval);
-    }
-  else if (nc.is_complex())
+  else
     {
-      mpc_t cval;
-      nc.get_complex(&cval);
-      Complex_expression::export_complex(efb, cval);
-      mpc_clear(cval);
+      const char *s = NULL;
+      switch (this->code_)
+       {
+       default:
+         go_unreachable();
+       case BUILTIN_APPEND:
+         s = "append";
+         break;
+       case BUILTIN_COPY:
+         s = "copy";
+         break;
+       case BUILTIN_LEN:
+         s = "len";
+         break;
+       case BUILTIN_CAP:
+         s = "cap";
+         break;
+       case BUILTIN_PRINT:
+         s = "print";
+         break;
+       case BUILTIN_PRINTLN:
+         s = "println";
+         break;
+       case BUILTIN_PANIC:
+         s = "panic";
+         break;
+       case BUILTIN_RECOVER:
+         s = "recover";
+         break;
+       case BUILTIN_CLOSE:
+         s = "close";
+         break;
+       case BUILTIN_REAL:
+         s = "real";
+         break;
+       case BUILTIN_IMAG:
+         s = "imag";
+         break;
+       case BUILTIN_COMPLEX:
+         s = "complex";
+         break;
+       }
+      efb->write_c_string(s);
+      this->export_arguments(efb);
     }
-  else
-    go_unreachable();
-
-  // A trailing space lets us reliably identify the end of the number.
-  efb->write_c_string(" ");
 }
 
 // Class Call_expression.
@@ -11637,7 +11704,55 @@ Call_expression::do_get_backend(Translate_context* context)
   return this->call_;
 }
 
-// Dump ast representation for a call expressin.
+// The cost of inlining a call expression.
+
+int
+Call_expression::do_inlining_cost() const
+{
+  Func_expression* fn = this->fn_->func_expression();
+
+  // FIXME: We don't yet support all kinds of calls.
+  if (fn != NULL && fn->closure() != NULL)
+    return 0x100000;
+  if (this->fn_->interface_field_reference_expression())
+    return 0x100000;
+  if (this->get_function_type()->is_method())
+    return 0x100000;
+
+  return 5;
+}
+
+// Export a call expression.
+
+void
+Call_expression::do_export(Export_function_body* efb) const
+{
+  this->fn_->export_expression(efb);
+  this->export_arguments(efb);
+}
+
+// Export call expression arguments.
+
+void
+Call_expression::export_arguments(Export_function_body* efb) const
+{
+  efb->write_c_string("(");
+  if (this->args_ != NULL && !this->args_->empty())
+    {
+      Expression_list::const_iterator pa = this->args_->begin();
+      (*pa)->export_expression(efb);
+      for (pa++; pa != this->args_->end(); pa++)
+       {
+         efb->write_c_string(", ");
+         (*pa)->export_expression(efb);
+       }
+      if (this->is_varargs_)
+       efb->write_c_string("...");
+    }
+  efb->write_c_string(")");
+}
+
+// Dump ast representation for a call expression.
 
 void
 Call_expression::do_dump_expression(Ast_dump_context* ast_dump_context) const
@@ -17522,6 +17637,47 @@ Expression::make_backend(Bexpression* bexpr, Type* type, Location location)
 
 Expression*
 Expression::import_expression(Import_expression* imp, Location loc)
+{
+  Expression* expr = Expression::import_expression_without_suffix(imp, loc);
+  while (true)
+    {
+      if (imp->match_c_string("("))
+       {
+         imp->advance(1);
+         Expression_list* args = new Expression_list();
+         bool is_varargs = false;
+         while (!imp->match_c_string(")"))
+           {
+             Expression* arg = Expression::import_expression(imp, loc);
+             if (arg->is_error_expression())
+               return arg;
+             args->push_back(arg);
+             if (imp->match_c_string(")"))
+               break;
+             else if (imp->match_c_string("...)"))
+               {
+                 imp->advance(3);
+                 is_varargs = true;
+                 break;
+               }
+             imp->require_c_string(", ");
+           }
+         imp->require_c_string(")");
+         expr = Expression::make_call(expr, args, is_varargs, loc);
+       }
+      else
+       break;
+    }
+
+  return expr;
+}
+
+// Import an expression without considering a suffix (function
+// arguments, index operations, etc.).
+
+Expression*
+Expression::import_expression_without_suffix(Import_expression* imp,
+                                            Location loc)
 {
   int c = imp->peek_char();
   if (c == '+' || c == '-' || c == '!' || c == '^' || c == '&' || c == '*')
@@ -17608,7 +17764,21 @@ Expression::import_identifier(Import_function_body* ifb, Location loc)
       return Expression::make_error(loc);
     }
 
-  return Expression::make_var_reference(no, loc);
+  if (no->is_variable() || no->is_result_variable())
+    return Expression::make_var_reference(no, loc);
+  else if (no->is_function() || no->is_function_declaration())
+    return Expression::make_func_reference(no, NULL, loc);
+  else
+    {
+      if (!ifb->saw_error())
+       go_error_at(ifb->location(),
+                   ("import error for %qs: "
+                    "unexpected type of identifier %qs (%d)"),
+                   ifb->name().c_str(),
+                   id.c_str(), no->classification());
+      ifb->set_saw_error();
+      return Expression::make_error(loc);
+    }
 }
 
 // Class Expression_list.
index e17e8dc0c7f5723113cd0a71fa1b491dc94cebd6..fc1c5a80ff80d4b3c683c4c0f7677f047aeabe99 100644 (file)
@@ -1253,6 +1253,9 @@ class Expression
   static Expression*
   import_identifier(Import_function_body*, Location);
 
+  static Expression*
+  import_expression_without_suffix(Import_expression*, Location);
+
   // The expression classification.
   Expression_classification classification_;
   // The location in the input file.
@@ -2409,6 +2412,12 @@ class Call_expression : public Expression
   virtual Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const;
+
+  void
+  do_export(Export_function_body*) const;
+
   virtual bool
   do_is_recover_call() const;
 
@@ -2431,6 +2440,9 @@ class Call_expression : public Expression
   bool
   determining_types();
 
+  void
+  export_arguments(Export_function_body*) const;
+
   void
   do_dump_expression(Ast_dump_context*) const;
 
@@ -2571,6 +2583,10 @@ class Builtin_call_expression : public Call_expression
   Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body*) const;
 
@@ -2745,6 +2761,12 @@ class Func_expression : public Expression
   Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const;
+
+  void
+  do_export(Export_function_body*) const;
+
   void
   do_dump_expression(Ast_dump_context*) const;
 
index ce9bffbfda2a8d8eda86ab3cdc82f73152e1ca76..6e8ccbba226a3e611d22a5aa31d3e18947f6cc6d 100644 (file)
@@ -5059,6 +5059,9 @@ Mark_inline_candidates::type(Type* t)
 void
 Gogo::do_exports()
 {
+  if (saw_errors())
+    return;
+
   // Mark any functions whose body should be exported for inlining by
   // other packages.
   Mark_inline_candidates mic;
@@ -5690,7 +5693,7 @@ Function::export_func(Export* exp, const Named_object* no) const
     block = this->block_;
   Function::export_func_with_type(exp, no, this->type_, this->results_,
                                  this->is_method() && this->nointerface(),
-                                 block, this->location_);
+                                 this->asm_name(), block, this->location_);
 }
 
 // Export a function with a type.
@@ -5699,7 +5702,8 @@ void
 Function::export_func_with_type(Export* exp, const Named_object* no,
                                const Function_type* fntype,
                                Function::Results* result_vars,
-                               bool nointerface, Block* block, Location loc)
+                               bool nointerface, const std::string& asm_name,
+                               Block* block, Location loc)
 {
   exp->write_c_string("func ");
 
@@ -5709,6 +5713,13 @@ Function::export_func_with_type(Export* exp, const Named_object* no,
       exp->write_c_string("/*nointerface*/ ");
     }
 
+  if (!asm_name.empty())
+    {
+      exp->write_c_string("/*asm ");
+      exp->write_string(asm_name);
+      exp->write_c_string(" */ ");
+    }
+
   if (fntype->is_method())
     {
       exp->write_c_string("(");
@@ -5848,16 +5859,37 @@ Function::import_func(Import* imp, std::string* pname,
                      Typed_identifier_list** presults,
                      bool* is_varargs,
                      bool* nointerface,
+                     std::string* asm_name,
                      std::string* body)
 {
   imp->require_c_string("func ");
 
   *nointerface = false;
-  if (imp->match_c_string("/*"))
+  while (imp->match_c_string("/*"))
     {
-      imp->require_c_string("/*nointerface*/ ");
-      *nointerface = true;
+      imp->advance(2);
+      if (imp->match_c_string("nointerface"))
+       {
+         imp->require_c_string("nointerface*/ ");
+         *nointerface = true;
+       }
+      else if (imp->match_c_string("asm"))
+       {
+         imp->require_c_string("asm ");
+         *asm_name = imp->read_identifier();
+         imp->require_c_string(" */ ");
+       }
+      else
+       {
+         go_error_at(imp->location(),
+                     "import error at %d: unrecognized function comment",
+                     imp->pos());
+         return false;
+       }
+    }
 
+  if (*nointerface)
+    {
       // Only a method can be nointerface.
       go_assert(imp->peek_char() == '(');
     }
@@ -7158,6 +7190,8 @@ Function_declaration::import_function_body(Gogo* gogo, Named_object* no)
       Named_type* rtype = fntype->receiver()->type()->deref()->named_type();
       go_assert(rtype != NULL);
       no = rtype->add_method(no->name(), fn);
+      const Package* package = rtype->named_object()->package();
+      package->bindings()->add_method(no);
     }
 
   Import_function_body ifb(gogo, this->imp_, no, body, nl + 1, outer, indent);
index 91e3bdfd198015cbdbe638475378d632066b8f42..11974764c4ef63dee7c3b0469b6ab5824306dd37 100644 (file)
@@ -1573,7 +1573,7 @@ class Function
   static void
   export_func_with_type(Export*, const Named_object*,
                        const Function_type*, Results*, bool nointerface,
-                       Block* block, Location);
+                       const std::string& asm_name, Block* block, Location);
 
   // Import a function.  Reports whether the import succeeded.
   static bool
@@ -1581,7 +1581,7 @@ class Function
              bool* is_exported, Typed_identifier** receiver,
              Typed_identifier_list** pparameters,
              Typed_identifier_list** presults, bool* is_varargs,
-             bool* nointerface, std::string* body);
+             bool* nointerface, std::string* asm_name, std::string* body);
 
  private:
   // Type for mapping from label names to Label objects.
@@ -1805,7 +1805,7 @@ class Function_declaration
   {
     Function::export_func_with_type(exp, no, this->fntype_, NULL,
                                    this->is_method() && this->nointerface(),
-                                   NULL, this->location_);
+                                   this->asm_name_, NULL, this->location_);
   }
 
   // Check that the types used in this declaration's signature are defined.
index ff92b8248d6c02bbf1909094bcb81ea8dc0a34ce..1c3f4a498b5f5cdefaba21626e7a6a8aef7cedff 100644 (file)
@@ -757,10 +757,11 @@ Import::import_func(Package* package)
   Typed_identifier_list* results;
   bool is_varargs;
   bool nointerface;
+  std::string asm_name;
   std::string body;
   if (!Function::import_func(this, &name, &fpkg, &is_exported, &receiver,
                             &parameters, &results, &is_varargs, &nointerface,
-                            &body))
+                            &asm_name, &body))
     return;
   if (fpkg == NULL)
     fpkg = package;
@@ -802,12 +803,14 @@ Import::import_func(Package* package)
   else
     {
       no = fpkg->add_function_declaration(name, fntype, loc);
-      if (this->add_to_globals_)
+      if (this->add_to_globals_ && fpkg == package)
        this->gogo_->add_dot_import_object(no);
     }
 
   if (nointerface)
     no->func_declaration_value()->set_nointerface();
+  if (!asm_name.empty())
+    no->func_declaration_value()->set_asm_name(asm_name);
   if (!body.empty() && !no->func_declaration_value()->has_imported_body())
     no->func_declaration_value()->set_imported_body(this, body);
 }
@@ -1231,6 +1234,12 @@ Import::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code)
   this->builtin_types_[index] = named_object->type_value();
 }
 
+// Characters that stop read_identifier.  We base this on the
+// characters that stop an identifier, without worrying about
+// characters that are permitted in an identifier.  That lets us skip
+// UTF-8 parsing.
+static const char * const identifier_stop = " \n;,()[]";
+
 // Read an identifier from the stream.
 
 std::string
@@ -1242,8 +1251,14 @@ Import::read_identifier()
   while (true)
     {
       c = stream->peek_char();
-      if (c == -1 || c == ' ' || c == '\n' || c == ';' || c == ')')
+      if (c == -1 || strchr(identifier_stop, c) != NULL)
        break;
+
+      // FIXME: Probably we shouldn't accept '.', but that might break
+      // some existing imports.
+      if (c == '.' && stream->match_c_string("..."))
+       break;
+
       ret += c;
       stream->advance(1);
     }
@@ -1521,7 +1536,18 @@ Import_function_body::read_identifier()
   for (size_t i = start; i < this->body_.length(); i++)
     {
       int c = static_cast<unsigned char>(this->body_[i]);
-      if (c == ' ' || c == '\n' || c == ';' || c == ')')
+      if (strchr(identifier_stop, c) != NULL)
+       {
+         this->off_ = i;
+         return this->body_.substr(start, i - start);
+       }
+
+      // FIXME: Probably we shouldn't accept '.', but that might break
+      // some existing imports.
+      if (c == '.'
+         && i + 2 < this->body_.length()
+         && this->body_[i + 1] == '.'
+         && this->body_[i + 2] == '.')
        {
          this->off_ = i;
          return this->body_.substr(start, i - start);
index 42f43a19fb77873b77e56c6c6256850a70e279a3..956a9a85c10b13c5224a5a94b43938da351b149f 100644 (file)
@@ -539,10 +539,12 @@ func (p *parser) parseNamedType(nlist []int) types.Type {
                for p.tok == scanner.Ident {
                        p.expectKeyword("func")
                        if p.tok == '/' {
-                               // Skip a /*nointerface*/ comment.
+                               // Skip a /*nointerface*/ or /*asm ID */ comment.
                                p.expect('/')
                                p.expect('*')
-                               p.expect(scanner.Ident)
+                               if p.expect(scanner.Ident) == "asm" {
+                                       p.parseUnquotedString()
+                               }
                                p.expect('*')
                                p.expect('/')
                        }
@@ -727,6 +729,17 @@ func (p *parser) parseFunctionType(pkg *types.Package, nlist []int) *types.Signa
 
 // Func = Name FunctionType [InlineBody] .
 func (p *parser) parseFunc(pkg *types.Package) *types.Func {
+       if p.tok == '/' {
+               // Skip an /*asm ID */ comment.
+               p.expect('/')
+               p.expect('*')
+               if p.expect(scanner.Ident) == "asm" {
+                       p.parseUnquotedString()
+               }
+               p.expect('*')
+               p.expect('/')
+       }
+
        name := p.parseName()
        if strings.ContainsRune(name, '$') {
                // This is a Type$equal or Type$hash function, which we don't want to parse,