aboutsummaryrefslogtreecommitdiff
path: root/gcc/go/gofrontend
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@google.com>2016-08-30 03:27:43 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2016-08-30 03:27:43 +0000
commit2adb671d186febfe9610f8d8ac8ba296b79d2c90 (patch)
tree4e9dd1801c8fffce5ca94c71063a0a5a3acd0d30 /gcc/go/gofrontend
parent0b390d608949864df9ce128a5090b1788dc07b16 (diff)
downloadgcc-2adb671d186febfe9610f8d8ac8ba296b79d2c90.zip
gcc-2adb671d186febfe9610f8d8ac8ba296b79d2c90.tar.gz
gcc-2adb671d186febfe9610f8d8ac8ba296b79d2c90.tar.bz2
compiler: add -fgo-c-header=FILE to create a C header
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
Diffstat (limited to 'gcc/go/gofrontend')
-rw-r--r--gcc/go/gofrontend/MERGE2
-rw-r--r--gcc/go/gofrontend/go.cc38
-rw-r--r--gcc/go/gofrontend/gogo.cc127
-rw-r--r--gcc/go/gofrontend/gogo.h24
-rw-r--r--gcc/go/gofrontend/types.cc272
-rw-r--r--gcc/go/gofrontend/types.h20
6 files changed, 464 insertions, 19 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index f73d5dd..e92aee3 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -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.
diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc
index ee87141..d0f523b 100644
--- a/gcc/go/gofrontend/go.cc
+++ b/gcc/go/gofrontend/go.cc
@@ -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.
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index e1ebd65..3b7ecd3 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -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.
diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index 0c45443..fe9322c 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -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_;
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc
index 1b764c0..d91180a 100644
--- a/gcc/go/gofrontend/types.cc
+++ b/gcc/go/gofrontend/types.cc
@@ -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*
diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h
index 682f611..5de49ae 100644
--- a/gcc/go/gofrontend/types.h
+++ b/gcc/go/gofrontend/types.h
@@ -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;