+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
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, ,
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,
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. */
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 ();
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.
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.
#include "go-system.h"
+#include <fstream>
+
#include "filenames.h"
#include "go-c.h"
: ""),
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.
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
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
const Bindings*
current_bindings() const;
+ void
+ write_c_header();
+
// Get the decl for the magic initialization function.
Named_object*
initialization_function_decl();
// 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_;
#include "go-system.h"
+#include <ostream>
+
#include "go-c.h"
#include "gogo.h"
#include "operator.h"
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*
#ifndef GO_TYPES_H
#define GO_TYPES_H
+#include <ostream>
+
#include "go-linemap.h"
#include "escape.h"
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*);
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;
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.
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.