This patch to the Go frontend adds support for 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.
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 266536)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -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: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc    (revision 266534)
+++ gcc/go/gofrontend/expressions.cc    (working copy)
@@ -786,6 +786,31 @@ Var_expression::do_address_taken(bool es
     }
 }
 
+// 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 Expres
   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 Expres
                                        this->location());
   }
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body*) const;
 
@@ -2408,6 +2441,10 @@ class Float_expression : public Expressi
   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 Expres
   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_backe
     }
 }
 
+// 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_funct
       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_expre
     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_exp
           || (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: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h     (revision 266526)
+++ gcc/go/gofrontend/expressions.h     (working copy)
@@ -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 Express
   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 : publi
   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 Expressi
   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 Express
   Bexpression*
   do_get_backend(Translate_context*);
 
+  int
+  do_inlining_cost() const
+  { return 1; }
+
   void
   do_export(Export_function_body*) const;
 
Index: gcc/go/gofrontend/statements.cc
===================================================================
--- gcc/go/gofrontend/statements.cc     (revision 266517)
+++ gcc/go/gofrontend/statements.cc     (working copy)
@@ -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(Gog
     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(Transla
                                              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: gcc/go/gofrontend/statements.h
===================================================================
--- gcc/go/gofrontend/statements.h      (revision 266525)
+++ gcc/go/gofrontend/statements.h      (working copy)
@@ -631,6 +631,13 @@ class Assignment_statement : public Stat
   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 Statemen
   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*);
 

Reply via email to