This patch to the Go frontend adds a new option -fgo-c-header=FILE
that tells the Go frontend to create a C header file defining the
package scope constants and struct types.  This is a step toward
converting much of the runtime package from C to Go, in order to use
the new facilities in the master Go library, such as the concurrent
garbage collectors.  By having the Go compiler define these structs
and constants in C, we can ensure that the Go code and the C code are
operating on the same data structures during the transition from C to
Go.

This patch also adds a new option -fcompiling-runtime, to be used when
compiling the runtime package, although that option doesn't do
anything yet.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian


2016-08-29  Ian Lance Taylor  <i...@google.com>

* lang.opt (fgo-c-header, fgo-compiling-runtime): New options.
* go-c.h (struct go_create_gogo_args): Define.
(go_create_gogo): Change declaration to take struct pointer.
* go-lang.c (go_c_header): New static variable.
(go_langhook_init): Update call to go_create_gogo.
* gccgo.texi (Invoking gccgo): Document -fgo-c-header and
-fgo-compiling-runtime.
Index: gcc/go/gccgo.texi
===================================================================
--- gcc/go/gccgo.texi   (revision 239316)
+++ gcc/go/gccgo.texi   (working copy)
@@ -239,6 +239,17 @@ heap when possible.  In the future this
 Output escape analysis debugging information.  Larger values of
 @var{n} generate more information.
 
+@item -fgo-c-header=@var{file}
+@cindex @option{-fgo-c-header}
+Write top-level named Go struct definitions to @var{file} as C code.
+This is used when compiling the runtime package.
+
+@item -fgo-compiling-runtime
+@cindex @option{-fgo-compiling-runtime}
+Apply special rules for compiling the runtime package.  Implicit
+memory allocation is forbidden.  Some additional compiler directives
+are supported.
+
 @item -g
 @cindex @option{-g for gccgo}
 This is the standard @command{gcc} option (@pxref{Debugging Options, ,
Index: gcc/go/go-c.h
===================================================================
--- gcc/go/go-c.h       (revision 239002)
+++ gcc/go/go-c.h       (working copy)
@@ -31,11 +31,21 @@ extern int go_enable_optimize (const cha
 
 extern void go_add_search_path (const char*);
 
-extern void go_create_gogo (int int_type_size, int pointer_size,
-                           const char* pkgpath, const char *prefix,
-                           const char *relative_import_path,
-                           bool check_divide_zero, bool check_divide_overflow,
-                           int debug_escape_level);
+struct go_create_gogo_args
+{
+  int int_type_size;
+  int pointer_size;
+  const char* pkgpath;
+  const char *prefix;
+  const char *relative_import_path;
+  const char *c_header;
+  bool check_divide_by_zero;
+  bool check_divide_overflow;
+  bool compiling_runtime;
+  int debug_escape_level;
+};
+
+extern void go_create_gogo (const struct go_create_gogo_args*);
 
 extern void go_parse_input_files (const char**, unsigned int,
                                  bool only_check_syntax,
Index: gcc/go/go-lang.c
===================================================================
--- gcc/go/go-lang.c    (revision 239002)
+++ gcc/go/go-lang.c    (working copy)
@@ -83,6 +83,7 @@ struct GTY(()) language_function
 static const char *go_pkgpath = NULL;
 static const char *go_prefix = NULL;
 static const char *go_relative_import_path = NULL;
+static const char *go_c_header = NULL;
 
 /* Language hooks.  */
 
@@ -99,9 +100,18 @@ go_langhook_init (void)
      to, e.g., unsigned_char_type_node) but before calling
      build_common_builtin_nodes (because it calls, indirectly,
      go_type_for_size).  */
-  go_create_gogo (INT_TYPE_SIZE, POINTER_SIZE, go_pkgpath, go_prefix,
-                 go_relative_import_path, go_check_divide_zero,
-                 go_check_divide_overflow, go_debug_escape_level);
+  struct go_create_gogo_args args;
+  args.int_type_size = INT_TYPE_SIZE;
+  args.pointer_size = POINTER_SIZE;
+  args.pkgpath = go_pkgpath;
+  args.prefix = go_prefix;
+  args.relative_import_path = go_relative_import_path;
+  args.c_header = go_c_header;
+  args.check_divide_by_zero = go_check_divide_zero;
+  args.check_divide_overflow = go_check_divide_overflow;
+  args.compiling_runtime = go_compiling_runtime;
+  args.debug_escape_level = go_debug_escape_level;
+  go_create_gogo (&args);
 
   build_common_builtin_nodes ();
 
@@ -247,6 +257,10 @@ go_langhook_handle_option (
       go_relative_import_path = arg;
       break;
 
+    case OPT_fgo_c_header_:
+      go_c_header = arg;
+      break;
+
     default:
       /* Just return 1 to indicate that the option is valid.  */
       break;
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE     (revision 239708)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -1,4 +1,4 @@
-0e505f5d191182abd8beb9b4c8232174bc116f97
+9c91e7eeb404b5b639cd6e80e2a38da948bb35ec
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/go.cc
===================================================================
--- gcc/go/gofrontend/go.cc     (revision 239311)
+++ gcc/go/gofrontend/go.cc     (working copy)
@@ -20,27 +20,29 @@ static Gogo* gogo;
 
 GO_EXTERN_C
 void
-go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath,
-              const char *prefix, const char *relative_import_path,
-              bool check_divide_by_zero, bool check_divide_overflow,
-              int debug_escape_level)
+go_create_gogo(const struct go_create_gogo_args* args)
 {
   go_assert(::gogo == NULL);
   Linemap* linemap = go_get_linemap();
-  ::gogo = new Gogo(go_get_backend(), linemap, int_type_size, pointer_size);
+  ::gogo = new Gogo(go_get_backend(), linemap, args->int_type_size,
+                   args->pointer_size);
 
-  if (pkgpath != NULL)
-    ::gogo->set_pkgpath(pkgpath);
-  else if (prefix != NULL)
-    ::gogo->set_prefix(prefix);
+  if (args->pkgpath != NULL)
+    ::gogo->set_pkgpath(args->pkgpath);
+  else if (args->prefix != NULL)
+    ::gogo->set_prefix(args->prefix);
 
-  if (relative_import_path != NULL)
-    ::gogo->set_relative_import_path(relative_import_path);
-  if (check_divide_by_zero)
-    ::gogo->set_check_divide_by_zero(check_divide_by_zero);
-  if (check_divide_overflow)
-    ::gogo->set_check_divide_overflow(check_divide_overflow);
-  ::gogo->set_debug_escape_level(debug_escape_level);
+  if (args->relative_import_path != NULL)
+    ::gogo->set_relative_import_path(args->relative_import_path);
+  if (args->check_divide_by_zero)
+    ::gogo->set_check_divide_by_zero(args->check_divide_by_zero);
+  if (args->check_divide_overflow)
+    ::gogo->set_check_divide_overflow(args->check_divide_overflow);
+  if (args->compiling_runtime)
+    ::gogo->set_compiling_runtime(args->compiling_runtime);
+  if (args->c_header != NULL)
+    ::gogo->set_c_header(args->c_header);
+  ::gogo->set_debug_escape_level(args->debug_escape_level);
 }
 
 // Parse the input files.
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc   (revision 239708)
+++ gcc/go/gofrontend/gogo.cc   (working copy)
@@ -6,6 +6,8 @@
 
 #include "go-system.h"
 
+#include <fstream>
+
 #include "filenames.h"
 
 #include "go-c.h"
@@ -4429,6 +4431,131 @@ Gogo::do_exports()
                      : ""),
                     this->imported_init_fns_,
                     this->package_->bindings());
+
+  if (!this->c_header_.empty() && !saw_errors())
+    this->write_c_header();
+}
+
+// Write the top level named struct types in C format to a C header
+// file.  This is used when building the runtime package, to share
+// struct definitions between C and Go.
+
+void
+Gogo::write_c_header()
+{
+  std::ofstream out;
+  out.open(this->c_header_.c_str());
+  if (out.fail())
+    {
+      error("cannot open %s: %m", this->c_header_.c_str());
+      return;
+    }
+
+  std::list<Named_object*> types;
+  Bindings* top = this->package_->bindings();
+  for (Bindings::const_definitions_iterator p = top->begin_definitions();
+       p != top->end_definitions();
+       ++p)
+    {
+      Named_object* no = *p;
+      if (no->is_type() && no->type_value()->struct_type() != NULL)
+       types.push_back(no);
+      if (no->is_const() && no->const_value()->type()->integer_type() != NULL)
+       {
+         Numeric_constant nc;
+         unsigned long val;
+         if (no->const_value()->expr()->numeric_constant_value(&nc)
+             && nc.to_unsigned_long(&val) == Numeric_constant::NC_UL_VALID)
+           {
+             out << "#define " << no->message_name() << ' ' << val
+                 << std::endl;
+           }
+       }
+    }
+
+  std::vector<const Named_object*> written;
+  int loop = 0;
+  while (!types.empty())
+    {
+      Named_object* no = types.front();
+      types.pop_front();
+
+      std::vector<const Named_object*> requires;
+      std::vector<const Named_object*> declare;
+      if (!no->type_value()->struct_type()->can_write_to_c_header(&requires,
+                                                                 &declare))
+       continue;
+
+      bool ok = true;
+      for (std::vector<const Named_object*>::const_iterator pr
+            = requires.begin();
+          pr != requires.end() && ok;
+          ++pr)
+       {
+         for (std::list<Named_object*>::const_iterator pt = types.begin();
+              pt != types.end() && ok;
+              ++pt)
+           if (*pr == *pt)
+             ok = false;
+       }
+      if (!ok)
+       {
+         ++loop;
+         if (loop > 10000)
+           {
+             // This should be impossible since the code parsed and
+             // type checked.
+             go_unreachable();
+           }
+
+         types.push_back(no);
+         continue;
+       }
+
+      for (std::vector<const Named_object*>::const_iterator pd
+            = declare.begin();
+          pd != declare.end();
+          ++pd)
+       {
+         if (*pd == no)
+           continue;
+
+         std::vector<const Named_object*> drequires;
+         std::vector<const Named_object*> ddeclare;
+         if (!(*pd)->type_value()->struct_type()->
+             can_write_to_c_header(&drequires, &ddeclare))
+           continue;
+
+         bool done = false;
+         for (std::vector<const Named_object*>::const_iterator pw
+                = written.begin();
+              pw != written.end();
+              ++pw)
+           {
+             if (*pw == *pd)
+               {
+                 done = true;
+                 break;
+               }
+           }
+         if (!done)
+           {
+             out << std::endl;
+             out << "struct " << (*pd)->message_name() << ";" << std::endl;
+             written.push_back(*pd);
+           }
+       }
+
+      out << std::endl;
+      out << "struct " << no->message_name() << " {" << std::endl;
+      no->type_value()->struct_type()->write_to_c_header(out);
+      out << "};" << std::endl;
+      written.push_back(no);
+    }
+
+  out.close();
+  if (out.fail())
+    error("error writing to %s: %m", this->c_header_.c_str());
 }
 
 // Find the blocks in order to convert named types defined in blocks.
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h    (revision 239708)
+++ gcc/go/gofrontend/gogo.h    (working copy)
@@ -250,6 +250,12 @@ class Gogo
   set_relative_import_path(const std::string& s)
   { this->relative_import_path_ = s; }
 
+  // Set the C header file to write.  This is used for the runtime
+  // package.
+  void
+  set_c_header(const std::string& s)
+  { this->c_header_ = s; }
+
   // Return whether to check for division by zero in binary operations.
   bool
   check_divide_by_zero() const
@@ -270,6 +276,16 @@ class Gogo
   set_check_divide_overflow(bool b)
   { this->check_divide_overflow_ = b; }
 
+  // Return whether we are compiling the runtime package.
+  bool
+  compiling_runtime() const
+  { return this->compiling_runtime_; }
+
+  // Set whether we are compiling the runtime package.
+  void
+  set_compiling_runtime(bool b)
+  { this->compiling_runtime_ = b; }
+
   // Return the level of escape analysis debug information to emit.
   int
   debug_escape_level() const
@@ -731,6 +747,9 @@ class Gogo
   const Bindings*
   current_bindings() const;
 
+  void
+  write_c_header();
+
   // Get the decl for the magic initialization function.
   Named_object*
   initialization_function_decl();
@@ -841,12 +860,17 @@ class Gogo
   // The relative import path, from the -fgo-relative-import-path
   // option.
   std::string relative_import_path_;
+  // The C header file to write, from the -fgo-c-header option.
+  std::string c_header_;
   // Whether or not to check for division by zero, from the
   // -fgo-check-divide-zero option.
   bool check_divide_by_zero_;
   // Whether or not to check for division overflow, from the
   // -fgo-check-divide-overflow option.
   bool check_divide_overflow_;
+  // Whether we are compiling the runtime package, from the
+  // -fgo-compiling-runtime option.
+  bool compiling_runtime_;
   // The level of escape analysis debug information to emit, from the
   // -fgo-debug-escape option.
   int debug_escape_level_;
Index: gcc/go/gofrontend/types.cc
===================================================================
--- gcc/go/gofrontend/types.cc  (revision 238653)
+++ gcc/go/gofrontend/types.cc  (working copy)
@@ -6,6 +6,8 @@
 
 #include "go-system.h"
 
+#include <ostream>
+
 #include "go-c.h"
 #include "gogo.h"
 #include "operator.h"
@@ -5680,6 +5682,276 @@ Struct_type::do_import(Import* imp)
   return Type::make_struct_type(fields, imp->location());
 }
 
+// Whether we can write this struct type to a C header file.
+// We can't if any of the fields are structs defined in a different package.
+
+bool
+Struct_type::can_write_to_c_header(
+    std::vector<const Named_object*>* requires,
+    std::vector<const Named_object*>* declare) const
+{
+  const Struct_field_list* fields = this->fields_;
+  if (fields == NULL || fields->empty())
+    return false;
+  for (Struct_field_list::const_iterator p = fields->begin();
+       p != fields->end();
+       ++p)
+    {
+      if (p->is_anonymous())
+       return false;
+      if (!this->can_write_type_to_c_header(p->type(), requires, declare))
+       return false;
+    }
+  return true;
+}
+
+// Whether we can write the type T to a C header file.
+
+bool
+Struct_type::can_write_type_to_c_header(
+    const Type* t,
+    std::vector<const Named_object*>* requires,
+    std::vector<const Named_object*>* declare) const
+{
+  t = t->forwarded();
+  switch (t->classification())
+    {
+    case TYPE_ERROR:
+    case TYPE_FORWARD:
+      return false;
+
+    case TYPE_VOID:
+    case TYPE_BOOLEAN:
+    case TYPE_INTEGER:
+    case TYPE_FLOAT:
+    case TYPE_COMPLEX:
+    case TYPE_STRING:
+    case TYPE_FUNCTION:
+    case TYPE_MAP:
+    case TYPE_CHANNEL:
+    case TYPE_INTERFACE:
+      return true;
+
+    case TYPE_POINTER:
+      if (t->points_to()->named_type() != NULL
+         && t->points_to()->struct_type() != NULL)
+       declare->push_back(t->points_to()->named_type()->named_object());
+      return true;
+
+    case TYPE_STRUCT:
+      return t->struct_type()->can_write_to_c_header(requires, declare);
+
+    case TYPE_ARRAY:
+      if (t->is_slice_type())
+       return true;
+      return this->can_write_type_to_c_header(t->array_type()->element_type(),
+                                             requires, declare);
+
+    case TYPE_NAMED:
+      {
+       const Named_object* no = t->named_type()->named_object();
+       if (no->package() != NULL)
+         {
+           if (t->is_unsafe_pointer_type())
+             return true;
+           return false;
+         }
+       if (t->struct_type() != NULL)
+         {
+           requires->push_back(no);
+           return t->struct_type()->can_write_to_c_header(requires, declare);
+         }
+       return this->can_write_type_to_c_header(t->base(), requires, declare);
+      }
+
+    case TYPE_CALL_MULTIPLE_RESULT:
+    case TYPE_NIL:
+    case TYPE_SINK:
+    default:
+      go_unreachable();
+    }
+}
+
+// Write this struct to a C header file.
+
+void
+Struct_type::write_to_c_header(std::ostream& os) const
+{
+  const Struct_field_list* fields = this->fields_;
+  for (Struct_field_list::const_iterator p = fields->begin();
+       p != fields->end();
+       ++p)
+    {
+      os << '\t';
+      this->write_field_to_c_header(os, p->field_name(), p->type());
+      os << ';' << std::endl;
+    }
+}
+
+// Write the type of a struct field to a C header file.
+
+void
+Struct_type::write_field_to_c_header(std::ostream& os, const std::string& name,
+                                    const Type *t) const
+{
+  bool print_name = true;
+  t = t->forwarded();
+  switch (t->classification())
+    {
+    case TYPE_VOID:
+      os << "void";
+      break;
+
+    case TYPE_BOOLEAN:
+      os << "_Bool";
+      break;
+
+    case TYPE_INTEGER:
+      {
+       const Integer_type* it = t->integer_type();
+       if (it->is_unsigned())
+         os << 'u';
+       os << "int" << it->bits() << "_t";
+      }
+      break;
+
+    case TYPE_FLOAT:
+      switch (t->float_type()->bits())
+       {
+       case 32:
+         os << "float";
+         break;
+       case 64:
+         os << "double";
+         break;
+       default:
+         go_unreachable();
+       }
+      break;
+
+    case TYPE_COMPLEX:
+      switch (t->complex_type()->bits())
+       {
+       case 64:
+         os << "float _Complex";
+         break;
+       case 128:
+         os << "double _Complex";
+         break;
+       default:
+         go_unreachable();
+       }
+      break;
+
+    case TYPE_STRING:
+      os << "String";
+      break;
+
+    case TYPE_FUNCTION:
+      os << "FuncVal";
+      break;
+
+    case TYPE_POINTER:
+      {
+       std::vector<const Named_object*> requires;
+       std::vector<const Named_object*> declare;
+       if (!this->can_write_type_to_c_header(t->points_to(), &requires,
+                                             &declare))
+         os << "void*";
+       else
+         {
+           this->write_field_to_c_header(os, "", t->points_to());
+           os << '*';
+         }
+      }
+      break;
+
+    case TYPE_MAP:
+      os << "Map*";
+      break;
+
+    case TYPE_CHANNEL:
+      os << "Chan*";
+      break;
+
+    case TYPE_INTERFACE:
+      if (t->interface_type()->is_empty())
+       os << "Eface";
+      else
+       os << "Iface";
+      break;
+
+    case TYPE_STRUCT:
+      os << "struct {" << std::endl;
+      t->struct_type()->write_to_c_header(os);
+      os << "\t}";
+      break;
+
+    case TYPE_ARRAY:
+      if (t->is_slice_type())
+       os << "Slice";
+      else
+       {
+         const Type *ele = t;
+         std::vector<const Type*> array_types;
+         while (ele->array_type() != NULL && !ele->is_slice_type())
+           {
+             array_types.push_back(ele);
+             ele = ele->array_type()->element_type();
+           }
+         this->write_field_to_c_header(os, "", ele);
+         os << ' ' << Gogo::message_name(name);
+         print_name = false;
+         while (!array_types.empty())
+           {
+             ele = array_types.back();
+             array_types.pop_back();
+             os << '[';
+             Numeric_constant nc;
+             if (!ele->array_type()->length()->numeric_constant_value(&nc))
+               go_unreachable();
+             mpz_t val;
+             if (!nc.to_int(&val))
+               go_unreachable();
+             char* s = mpz_get_str(NULL, 10, val);
+             os << s;
+             free(s);
+             mpz_clear(val);
+             os << ']';
+           }
+       }
+      break;
+
+    case TYPE_NAMED:
+      {
+       const Named_object* no = t->named_type()->named_object();
+       if (t->struct_type() != NULL)
+         os << "struct " << no->message_name();
+       else if (t->is_unsafe_pointer_type())
+         os << "void*";
+       else if (t == Type::lookup_integer_type("uintptr"))
+         os << "uintptr_t";
+       else
+         {
+           this->write_field_to_c_header(os, name, t->base());
+           print_name = false;
+         }
+      }
+      break;
+
+    case TYPE_ERROR:
+    case TYPE_FORWARD:
+    case TYPE_CALL_MULTIPLE_RESULT:
+    case TYPE_NIL:
+    case TYPE_SINK:
+    default:
+      go_unreachable();
+    }
+
+  if (print_name && !name.empty())
+    os << ' ' << Gogo::message_name(name);
+}
+
 // Make a struct type.
 
 Struct_type*
Index: gcc/go/gofrontend/types.h
===================================================================
--- gcc/go/gofrontend/types.h   (revision 238653)
+++ gcc/go/gofrontend/types.h   (working copy)
@@ -7,6 +7,8 @@
 #ifndef GO_TYPES_H
 #define GO_TYPES_H
 
+#include <ostream>
+
 #include "go-linemap.h"
 #include "escape.h"
 
@@ -2329,6 +2331,16 @@ class Struct_type : public Type
   void
   write_equal_function(Gogo*, Named_type*);
 
+  // Whether we can write this type to a C header file, to implement
+  // -fgo-c-header.
+  bool
+  can_write_to_c_header(std::vector<const Named_object*>*,
+                       std::vector<const Named_object*>*) const;
+
+  // Write this type to a C header file, to implement -fgo-c-header.
+  void
+  write_to_c_header(std::ostream&) const;
+
  protected:
   int
   do_traverse(Traverse*);
@@ -2364,6 +2376,14 @@ class Struct_type : public Type
   do_export(Export*) const;
 
  private:
+  bool
+  can_write_type_to_c_header(const Type*,
+                            std::vector<const Named_object*>*,
+                            std::vector<const Named_object*>*) const;
+
+  void
+  write_field_to_c_header(std::ostream&, const std::string&, const Type*) 
const;
+
   // Used to merge method sets of identical unnamed structs.
   typedef Unordered_map_hash(Struct_type*, Struct_type*, Type_hash_identical,
                             Type_identical) Identical_structs;
Index: gcc/go/lang.opt
===================================================================
--- gcc/go/lang.opt     (revision 239002)
+++ gcc/go/lang.opt     (working copy)
@@ -37,6 +37,10 @@ Wall
 Go
 ; Documented in c.opt
 
+fgo-c-header=
+Go Joined RejectNegative
+-fgo-c-header=<file>   Write Go struct definitions to file as C code.
+
 fgo-check-divide-zero
 Go Var(go_check_divide_zero) Init(1)
 Add explicit checks for division by zero.
@@ -45,6 +49,10 @@ fgo-check-divide-overflow
 Go Var(go_check_divide_overflow) Init(1)
 Add explicit checks for division overflow in INT_MIN / -1.
 
+fgo-compiling-runtime
+Go Var(go_compiling_runtime) Init(0)
+Apply special rules for compiling runtime package.
+
 fgo-dump-
 Go Joined RejectNegative
 -fgo-dump-<type>       Dump Go frontend internal information.

Reply via email to