compiler: add -fgo-c-header=FILE to create a C header
authorIan Lance Taylor <iant@google.com>
Tue, 30 Aug 2016 03:27:43 +0000 (03:27 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 30 Aug 2016 03:27:43 +0000 (03:27 +0000)
    The new -fgo-c-header=FILE option will write a C header file defining
    all the struct types and numeric const values in package scope.  This
    will be used when building the Go runtime package (libgo/go/runtime) to
    generate a C header file that may be included by the C code in the C
    runtime package (libgo/runtime).

    This will ensure that the Go code and C code are working with the same
    data structures as we convert the runtime from C to Go to upgrade to the
    current GC runtime, notably the concurrent garbage collector.

    Reviewed-on: https://go-review.googlesource.com/28000

* 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.

From-SVN: r239852

gcc/go/ChangeLog
gcc/go/gccgo.texi
gcc/go/go-c.h
gcc/go/go-lang.c
gcc/go/gofrontend/MERGE
gcc/go/gofrontend/go.cc
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/types.cc
gcc/go/gofrontend/types.h
gcc/go/lang.opt

index dbe3943c2e73849462146453ffa22c7b3585be20..2052a977029f260e25eed447c17cdc27837c116b 100644 (file)
@@ -1,3 +1,13 @@
+2016-08-29  Ian Lance Taylor  <iant@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.
+
 2016-08-09  Ian Lance Taylor  <iant@google.com>
 
        * gccgo.texi (Invoking gccgo): Document -fgo-optimize-allocs and
index 6544b7981a53480def480f810972a999d6d1d67c..04ce3e996c737e8164efa50fabff2b9d3f4ce0e1 100644 (file)
@@ -239,6 +239,17 @@ heap when possible.  In the future this may be the default.
 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 690d6db7ab96bf132f6a48fbccfa01b8c3c8dcc5..a96812d3cb5cb08ef3b27c30675109f9859419e2 100644 (file)
@@ -31,11 +31,21 @@ extern int go_enable_optimize (const char*);
 
 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 570f5e06e8d8ddeed113af026750269f185f1819..88667e0c9b4aa6d74d4719f9b78172ee2755d65b 100644 (file)
@@ -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 f73d5dd51ac1071b1e823b2b5dc56969d7fa6791..e92aee31835ee3f5729e9387ae1d2d4fea25f32a 100644 (file)
@@ -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 ee87141efd20f08c2c46dfd698f2520654e90a7f..d0f523bdfe8764fd13791c4ceb4b280046884566 100644 (file)
@@ -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);
-
-  if (pkgpath != NULL)
-    ::gogo->set_pkgpath(pkgpath);
-  else if (prefix != NULL)
-    ::gogo->set_prefix(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);
+  ::gogo = new Gogo(go_get_backend(), linemap, args->int_type_size,
+                   args->pointer_size);
+
+  if (args->pkgpath != NULL)
+    ::gogo->set_pkgpath(args->pkgpath);
+  else if (args->prefix != NULL)
+    ::gogo->set_prefix(args->prefix);
+
+  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 e1ebd6524d77537a59204be1e1bcbef0b0cb1f0e..3b7ecd3491fbe335c8ff05b45aab64c5557335c8 100644 (file)
@@ -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 0c45443d3f1c7ff7ddda73875651bf8d5c207ceb..fe9322c0be681a99bda057ab2d3e8b8d5ef0ba36 100644 (file)
@@ -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 1b764c0d2512f7dd639855ebfaaf715cb2a9709e..d91180a04971947bc9957e56fd050ce50aad73a2 100644 (file)
@@ -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 682f61145d2d4a5d03d6064d8e7815192f17a495..5de49ae3534e5388b3cef2e4d1d01bf9845b125a 100644 (file)
@@ -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 7de9386f92a5617fa607fd0e27811e52dd1c0ba3..503fc7dfd238297a00957d0e93b676edf169f44d 100644 (file)
@@ -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.