mirror of git://gcc.gnu.org/git/gcc.git
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
This commit is contained in:
parent
0b390d6089
commit
2adb671d18
|
|
@ -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>
|
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.
|
||||||
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*);
|
||||||
|
|
||||||
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
|
||||||
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)
|
||||||
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 (
|
||||||
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;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
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;
|
||||||
|
|
||||||
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)
|
if (args->pkgpath != NULL)
|
||||||
::gogo->set_pkgpath(pkgpath);
|
::gogo->set_pkgpath(args->pkgpath);
|
||||||
else if (prefix != NULL)
|
else if (args->prefix != NULL)
|
||||||
::gogo->set_prefix(prefix);
|
::gogo->set_prefix(args->prefix);
|
||||||
|
|
||||||
if (relative_import_path != NULL)
|
if (args->relative_import_path != NULL)
|
||||||
::gogo->set_relative_import_path(relative_import_path);
|
::gogo->set_relative_import_path(args->relative_import_path);
|
||||||
if (check_divide_by_zero)
|
if (args->check_divide_by_zero)
|
||||||
::gogo->set_check_divide_by_zero(check_divide_by_zero);
|
::gogo->set_check_divide_by_zero(args->check_divide_by_zero);
|
||||||
if (check_divide_overflow)
|
if (args->check_divide_overflow)
|
||||||
::gogo->set_check_divide_overflow(check_divide_overflow);
|
::gogo->set_check_divide_overflow(args->check_divide_overflow);
|
||||||
::gogo->set_debug_escape_level(debug_escape_level);
|
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 @@
|
||||||
|
|
||||||
#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()
|
||||||
: ""),
|
: ""),
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
// 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 @@
|
||||||
|
|
||||||
#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)
|
||||||
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 @@
|
||||||
#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
|
||||||
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
|
||||||
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
|
||||||
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
|
||||||
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.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue