Commit 2adb671d by Ian Lance Taylor Committed by Ian Lance Taylor

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
parent 0b390d60
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> 2016-08-09 Ian Lance Taylor <iant@google.com>
* gccgo.texi (Invoking gccgo): Document -fgo-optimize-allocs and * gccgo.texi (Invoking gccgo): Document -fgo-optimize-allocs and
......
...@@ -239,6 +239,17 @@ heap when possible. In the future this may be the default. ...@@ -239,6 +239,17 @@ heap when possible. In the future this may be the default.
Output escape analysis debugging information. Larger values of Output escape analysis debugging information. Larger values of
@var{n} generate more information. @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 @item -g
@cindex @option{-g for gccgo} @cindex @option{-g for gccgo}
This is the standard @command{gcc} option (@pxref{Debugging Options, , This is the standard @command{gcc} option (@pxref{Debugging Options, ,
......
...@@ -31,11 +31,21 @@ extern int go_enable_optimize (const char*); ...@@ -31,11 +31,21 @@ extern int go_enable_optimize (const char*);
extern void go_add_search_path (const char*); extern void go_add_search_path (const char*);
extern void go_create_gogo (int int_type_size, int pointer_size, struct go_create_gogo_args
const char* pkgpath, const char *prefix, {
const char *relative_import_path, int int_type_size;
bool check_divide_zero, bool check_divide_overflow, int pointer_size;
int debug_escape_level); 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, extern void go_parse_input_files (const char**, unsigned int,
bool only_check_syntax, bool only_check_syntax,
......
...@@ -83,6 +83,7 @@ struct GTY(()) language_function ...@@ -83,6 +83,7 @@ struct GTY(()) language_function
static const char *go_pkgpath = NULL; static const char *go_pkgpath = NULL;
static const char *go_prefix = NULL; static const char *go_prefix = NULL;
static const char *go_relative_import_path = NULL; static const char *go_relative_import_path = NULL;
static const char *go_c_header = NULL;
/* Language hooks. */ /* Language hooks. */
...@@ -99,9 +100,18 @@ go_langhook_init (void) ...@@ -99,9 +100,18 @@ go_langhook_init (void)
to, e.g., unsigned_char_type_node) but before calling to, e.g., unsigned_char_type_node) but before calling
build_common_builtin_nodes (because it calls, indirectly, build_common_builtin_nodes (because it calls, indirectly,
go_type_for_size). */ go_type_for_size). */
go_create_gogo (INT_TYPE_SIZE, POINTER_SIZE, go_pkgpath, go_prefix, struct go_create_gogo_args args;
go_relative_import_path, go_check_divide_zero, args.int_type_size = INT_TYPE_SIZE;
go_check_divide_overflow, go_debug_escape_level); 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 (); build_common_builtin_nodes ();
...@@ -247,6 +257,10 @@ go_langhook_handle_option ( ...@@ -247,6 +257,10 @@ go_langhook_handle_option (
go_relative_import_path = arg; go_relative_import_path = arg;
break; break;
case OPT_fgo_c_header_:
go_c_header = arg;
break;
default: default:
/* Just return 1 to indicate that the option is valid. */ /* Just return 1 to indicate that the option is valid. */
break; break;
......
0e505f5d191182abd8beb9b4c8232174bc116f97 9c91e7eeb404b5b639cd6e80e2a38da948bb35ec
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -20,27 +20,29 @@ static Gogo* gogo; ...@@ -20,27 +20,29 @@ static Gogo* gogo;
GO_EXTERN_C GO_EXTERN_C
void void
go_create_gogo(int int_type_size, int pointer_size, const char *pkgpath, go_create_gogo(const struct go_create_gogo_args* args)
const char *prefix, const char *relative_import_path,
bool check_divide_by_zero, bool check_divide_overflow,
int debug_escape_level)
{ {
go_assert(::gogo == NULL); go_assert(::gogo == NULL);
Linemap* linemap = go_get_linemap(); 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); if (args->pkgpath != NULL)
else if (prefix != NULL) ::gogo->set_pkgpath(args->pkgpath);
::gogo->set_prefix(prefix); else if (args->prefix != NULL)
::gogo->set_prefix(args->prefix);
if (relative_import_path != NULL)
::gogo->set_relative_import_path(relative_import_path); if (args->relative_import_path != NULL)
if (check_divide_by_zero) ::gogo->set_relative_import_path(args->relative_import_path);
::gogo->set_check_divide_by_zero(check_divide_by_zero); if (args->check_divide_by_zero)
if (check_divide_overflow) ::gogo->set_check_divide_by_zero(args->check_divide_by_zero);
::gogo->set_check_divide_overflow(check_divide_overflow); if (args->check_divide_overflow)
::gogo->set_debug_escape_level(debug_escape_level); ::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. // Parse the input files.
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include "go-system.h" #include "go-system.h"
#include <fstream>
#include "filenames.h" #include "filenames.h"
#include "go-c.h" #include "go-c.h"
...@@ -4429,6 +4431,131 @@ Gogo::do_exports() ...@@ -4429,6 +4431,131 @@ Gogo::do_exports()
: ""), : ""),
this->imported_init_fns_, this->imported_init_fns_,
this->package_->bindings()); 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. // Find the blocks in order to convert named types defined in blocks.
......
...@@ -250,6 +250,12 @@ class Gogo ...@@ -250,6 +250,12 @@ class Gogo
set_relative_import_path(const std::string& s) set_relative_import_path(const std::string& s)
{ this->relative_import_path_ = 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. // Return whether to check for division by zero in binary operations.
bool bool
check_divide_by_zero() const check_divide_by_zero() const
...@@ -270,6 +276,16 @@ class Gogo ...@@ -270,6 +276,16 @@ class Gogo
set_check_divide_overflow(bool b) set_check_divide_overflow(bool b)
{ this->check_divide_overflow_ = 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. // Return the level of escape analysis debug information to emit.
int int
debug_escape_level() const debug_escape_level() const
...@@ -731,6 +747,9 @@ class Gogo ...@@ -731,6 +747,9 @@ class Gogo
const Bindings* const Bindings*
current_bindings() const; current_bindings() const;
void
write_c_header();
// Get the decl for the magic initialization function. // Get the decl for the magic initialization function.
Named_object* Named_object*
initialization_function_decl(); initialization_function_decl();
...@@ -841,12 +860,17 @@ class Gogo ...@@ -841,12 +860,17 @@ class Gogo
// The relative import path, from the -fgo-relative-import-path // The relative import path, from the -fgo-relative-import-path
// option. // option.
std::string relative_import_path_; 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 // Whether or not to check for division by zero, from the
// -fgo-check-divide-zero option. // -fgo-check-divide-zero option.
bool check_divide_by_zero_; bool check_divide_by_zero_;
// Whether or not to check for division overflow, from the // Whether or not to check for division overflow, from the
// -fgo-check-divide-overflow option. // -fgo-check-divide-overflow option.
bool check_divide_overflow_; 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 // The level of escape analysis debug information to emit, from the
// -fgo-debug-escape option. // -fgo-debug-escape option.
int debug_escape_level_; int debug_escape_level_;
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include "go-system.h" #include "go-system.h"
#include <ostream>
#include "go-c.h" #include "go-c.h"
#include "gogo.h" #include "gogo.h"
#include "operator.h" #include "operator.h"
...@@ -5680,6 +5682,276 @@ Struct_type::do_import(Import* imp) ...@@ -5680,6 +5682,276 @@ Struct_type::do_import(Import* imp)
return Type::make_struct_type(fields, imp->location()); 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. // Make a struct type.
Struct_type* Struct_type*
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
#ifndef GO_TYPES_H #ifndef GO_TYPES_H
#define GO_TYPES_H #define GO_TYPES_H
#include <ostream>
#include "go-linemap.h" #include "go-linemap.h"
#include "escape.h" #include "escape.h"
...@@ -2329,6 +2331,16 @@ class Struct_type : public Type ...@@ -2329,6 +2331,16 @@ class Struct_type : public Type
void void
write_equal_function(Gogo*, Named_type*); 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: protected:
int int
do_traverse(Traverse*); do_traverse(Traverse*);
...@@ -2364,6 +2376,14 @@ class Struct_type : public Type ...@@ -2364,6 +2376,14 @@ class Struct_type : public Type
do_export(Export*) const; do_export(Export*) const;
private: 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. // Used to merge method sets of identical unnamed structs.
typedef Unordered_map_hash(Struct_type*, Struct_type*, Type_hash_identical, typedef Unordered_map_hash(Struct_type*, Struct_type*, Type_hash_identical,
Type_identical) Identical_structs; Type_identical) Identical_structs;
......
...@@ -37,6 +37,10 @@ Wall ...@@ -37,6 +37,10 @@ Wall
Go Go
; Documented in c.opt ; 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 fgo-check-divide-zero
Go Var(go_check_divide_zero) Init(1) Go Var(go_check_divide_zero) Init(1)
Add explicit checks for division by zero. Add explicit checks for division by zero.
...@@ -45,6 +49,10 @@ fgo-check-divide-overflow ...@@ -45,6 +49,10 @@ fgo-check-divide-overflow
Go Var(go_check_divide_overflow) Init(1) Go Var(go_check_divide_overflow) Init(1)
Add explicit checks for division overflow in INT_MIN / -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- fgo-dump-
Go Joined RejectNegative Go Joined RejectNegative
-fgo-dump-<type> Dump Go frontend internal information. -fgo-dump-<type> Dump Go frontend internal information.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment