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>
* 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
@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, ,
......
......@@ -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,
......
......@@ -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;
......
0e505f5d191182abd8beb9b4c8232174bc116f97
9c91e7eeb404b5b639cd6e80e2a38da948bb35ec
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -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.
......
......@@ -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.
......
......@@ -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_;
......
......@@ -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*
......
......@@ -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;
......
......@@ -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.
......
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