compiler: inline functions with assignments and return statements
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 28 Nov 2018 18:08:21 +0000 (18:08 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 28 Nov 2018 18:08:21 +0000 (18:08 +0000)
    Support inlining functions that contain only assignments and return
    statements, with expressions of either constants or parameters.
    Functions that contain other kinds of statements or expressions are
    not yet inlined.  With this change, about 100 functions in the
    standard library are inlinable.

    Reviewed-on: https://go-review.googlesource.com/c/150073

From-SVN: r266573

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/expressions.cc
gcc/go/gofrontend/expressions.h
gcc/go/gofrontend/statements.cc
gcc/go/gofrontend/statements.h

index 8e220f50bf6e4744ea271588b5bfa383f178d66a..74806a23cbeedee124d53c8c2b98c778a90841a1 100644 (file)
@@ -1,4 +1,4 @@
-b013405f2c66596c47cb9be493c798db1087c0f0
+a8f768d68760768da5e86a8e63ef1ad5691c3ae8
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 5f00eff5655f6955c901b628fa52b787855a6513..7c464ce7f54dae73ece5169a9e46882faeafd1bc 100644 (file)
@@ -786,6 +786,31 @@ Var_expression::do_address_taken(bool escapes)
     }
 }
 
+// The cost to inline a variable reference.  We currently only support
+// references to parameters.
+
+int
+Var_expression::do_inlining_cost() const
+{
+  if (this->variable_->is_variable())
+    {
+      if (this->variable_->var_value()->is_parameter())
+       return 1;
+    }
+  else if (this->variable_->is_result_variable())
+    return 1;
+
+  return 0x100000;
+}
+
+// Export a reference to a variable.
+
+void
+Var_expression::do_export(Export_function_body* efb) const
+{
+  efb->write_string(Gogo::unpack_hidden_name(this->variable_->name()));
+}
+
 // Get the backend representation for a reference to a variable.
 
 Bexpression*
@@ -1608,6 +1633,10 @@ class Boolean_expression : public Expression
   do_get_backend(Translate_context* context)
   { return context->backend()->boolean_constant_expression(this->val_); }
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body* efb) const
   { efb->write_c_string(this->val_ ? "$true" : "$false"); }
@@ -1997,6 +2026,10 @@ class Integer_expression : public Expression
                                        this->location());
   }
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body*) const;
 
@@ -2408,6 +2441,10 @@ class Float_expression : public Expression
   Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body*) const;
 
@@ -2617,6 +2654,10 @@ class Complex_expression : public Expression
   Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const
+  { return 2; }
+
   void
   do_export(Export_function_body*) const;
 
@@ -3204,6 +3245,10 @@ class Nil_expression : public Expression
   do_get_backend(Translate_context* context)
   { return context->backend()->nil_pointer_expression(); }
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body* efb) const
   { efb->write_c_string("$nil"); }
@@ -3654,6 +3699,25 @@ Type_conversion_expression::do_get_backend(Translate_context* context)
     }
 }
 
+// Cost of inlining a type conversion.
+
+int
+Type_conversion_expression::do_inlining_cost() const
+{
+  Type* type = this->type_;
+  Type* expr_type = this->expr_->type();
+  if (type->interface_type() != NULL || expr_type->interface_type() != NULL)
+    return 10;
+  else if (type->is_string_type() && expr_type->integer_type() != NULL)
+    return 10;
+  else if (type->is_string_type() && expr_type->is_slice_type())
+    return 10;
+  else if (type->is_slice_type() && expr_type->is_string_type())
+    return 10;
+  else
+    return 1;
+}
+
 // Output a type conversion in a constant expression.
 
 void
@@ -4677,7 +4741,11 @@ Unary_expression::do_export(Export_function_body* efb) const
       efb->write_c_string("^");
       break;
     case OPERATOR_AND:
+      efb->write_c_string("&");
+      break;
     case OPERATOR_MULT:
+      efb->write_c_string("*");
+      break;
     default:
       go_unreachable();
     }
@@ -4704,6 +4772,12 @@ Unary_expression::do_import(Import_expression* imp, Location loc)
     case '^':
       op = OPERATOR_XOR;
       break;
+    case '&':
+      op = OPERATOR_AND;
+      break;
+    case '*':
+      op = OPERATOR_MULT;
+      break;
     default:
       go_unreachable();
     }
@@ -16195,7 +16269,7 @@ Expression*
 Expression::import_expression(Import_expression* imp, Location loc)
 {
   int c = imp->peek_char();
-  if (c == '+' || c == '-' || c == '!' || c == '^')
+  if (c == '+' || c == '-' || c == '!' || c == '^' || c == '&' || c == '*')
     return Unary_expression::do_import(imp, loc);
   else if (c == '(')
     return Binary_expression::do_import(imp, loc);
@@ -16220,11 +16294,35 @@ Expression::import_expression(Import_expression* imp, Location loc)
           || (imp->version() < EXPORT_FORMAT_V3
               && imp->match_c_string("convert")))
     return Type_conversion_expression::do_import(imp, loc);
-  else
+
+  Import_function_body* ifb = imp->ifb();
+  if (ifb == NULL)
     {
       go_error_at(imp->location(), "import error: expected expression");
       return Expression::make_error(loc);
     }
+  if (ifb->saw_error())
+    return Expression::make_error(loc);
+  std::string id = ifb->read_identifier();
+  if (id.empty())
+    {
+      if (!ifb->saw_error())
+       go_error_at(imp->location(),
+                   "import error: expected identifier at %lu",
+                   static_cast<unsigned long>(ifb->off()));
+      ifb->set_saw_error();
+      return Expression::make_error(loc);
+    }
+  Named_object* var = ifb->block()->bindings()->lookup(id);
+  if (var == NULL)
+    {
+      if (!ifb->saw_error())
+       go_error_at(imp->location(), "import error: lookup of %qs failed",
+                   id.c_str());
+      ifb->set_saw_error();
+      return Expression::make_error(loc);
+    }
+  return Expression::make_var_reference(var, loc);
 }
 
 // Class Expression_list.
index 70616571922582e35ce86d99078db76aa023b6b3..a18322cfb91a4866e3dc14f5a7b16ec50e962a80 100644 (file)
@@ -941,7 +941,7 @@ class Expression
 
   // Return the cost of this statement for inlining purposes.
   int
-  inlining_cost()
+  inlining_cost() const
   { return this->do_inlining_cost(); }
 
   // Return whether the expression is addressable--something which may
@@ -1093,7 +1093,7 @@ class Expression
   // inlining.  The default cost is high, so we only need to define
   // this method for expressions that can be inlined.
   virtual int
-  do_inlining_cost()
+  do_inlining_cost() const
   { return 0x100000; }
 
   // Child class implements whether the expression is addressable.
@@ -1355,6 +1355,12 @@ class Var_expression : public Expression
   do_copy()
   { return this; }
 
+  int
+  do_inlining_cost() const;
+
+  void
+  do_export(Export_function_body*) const;
+
   bool
   do_is_addressable() const
   { return true; }
@@ -1602,6 +1608,12 @@ class String_expression : public Expression
   static void
   export_string(String_dump* exp, const String_expression* str);
 
+  // Set the inlining cost a bit high since inlining may cause
+  // duplicated string literals.
+  int
+  do_inlining_cost() const
+  { return 5; }
+
   void
   do_export(Export_function_body*) const;
 
@@ -1686,6 +1698,9 @@ class Type_conversion_expression : public Expression
   Bexpression*
   do_get_backend(Translate_context* context);
 
+  int
+  do_inlining_cost() const;
+
   void
   do_export(Export_function_body*) const;
 
@@ -1877,6 +1892,10 @@ class Unary_expression : public Expression
   Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body*) const;
 
@@ -2022,6 +2041,10 @@ class Binary_expression : public Expression
   Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body*) const;
 
index 60b7a701df24384abf7fa31e37490affcac0eb32..4d10f60e6c201f878ef00b5ca1f21f30ccb3d0ed 100644 (file)
@@ -12,6 +12,7 @@
 #include "expressions.h"
 #include "gogo.h"
 #include "export.h"
+#include "import.h"
 #include "runtime.h"
 #include "backend.h"
 #include "statements.h"
@@ -124,9 +125,41 @@ Statement::determine_types()
 // Read a statement from export data.
 
 Statement*
-Statement::import_statement(Import_function_body*, Location)
+Statement::import_statement(Import_function_body* ifb, Location loc)
 {
-  go_unreachable();
+  if (ifb->match_c_string("{"))
+    {
+      size_t nl = ifb->body().find('\n', ifb->off());
+      if (nl == std::string::npos)
+       {
+         if (!ifb->saw_error())
+           go_error_at(ifb->location(),
+                       "import error: no newline after { at %lu",
+                       static_cast<unsigned long>(ifb->off()));
+         ifb->set_saw_error();
+         return Statement::make_error_statement(loc);
+       }
+      ifb->set_off(nl + 1);
+      ifb->increment_indent();
+      Block* block = new Block(ifb->block(), loc);
+      bool ok = Block::import_block(block, ifb, loc);
+      ifb->decrement_indent();
+      if (!ok)
+       return Statement::make_error_statement(loc);
+      return Statement::make_block_statement(block, loc);
+    }
+  else if (ifb->match_c_string("return"))
+    {
+      // After lowering return statements have no expressions.  The
+      // return expressions are assigned to result parameters.
+      ifb->advance(6);
+      return Statement::make_return_statement(NULL, loc);
+    }
+
+  Expression* lhs = Expression::import_expression(ifb, loc);
+  ifb->require_c_string(" = ");
+  Expression* rhs = Expression::import_expression(ifb, loc);
+  return Statement::make_assignment(lhs, rhs, loc);
 }
 
 // If this is a thunk statement, return it.
@@ -834,6 +867,14 @@ Assignment_statement::do_check_types(Gogo*)
     this->set_is_error();
 }
 
+void
+Assignment_statement::do_export_statement(Export_function_body* efb)
+{
+  this->lhs_->export_expression(efb);
+  efb->write_c_string(" = ");
+  this->rhs_->export_expression(efb);
+}
+
 // Flatten an assignment statement.  We may need a temporary for
 // interface conversion.
 
@@ -2844,6 +2885,16 @@ Return_statement::do_get_backend(Translate_context* context)
                                              retvals, loc);
 }
 
+// Export a return statement.  At this point all the expressions have
+// been converted to assignments to the result variables, so this is
+// simple.
+
+void
+Return_statement::do_export_statement(Export_function_body* efb)
+{
+  efb->write_c_string("return");
+}
+
 // Dump the AST representation for a return statement.
 
 void
index 3b5c68a3f6cc9a37addb33fedc0c7b0be97011e9..621d301fb8229b96cdb81074f122f09503f4fffb 100644 (file)
@@ -631,6 +631,13 @@ class Assignment_statement : public Statement
   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*);
 
@@ -792,6 +799,13 @@ class Return_statement : public Statement
   do_may_fall_through() const
   { return false; }
 
+  int
+  do_inlining_cost()
+  { return 1; }
+
+  void
+  do_export_statement(Export_function_body*);
+
   Bstatement*
   do_get_backend(Translate_context*);