Jakub told me that some tests are failing when using GNU ld because GNU ld issues an error if it sees a dynamic symbol which is a global variable with zero size. Go permits types with zero size, and such variables are not an error in Go. This patch avoids these variables at the level of the gcc interface, by converting externally visible global variables with zero size to have a 1 byte size instead. This required changing the initialization and assignment code to avoid initializing and assigning zero-sized values. Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu. Committed to mainline.
Ian 2012-03-08 Ian Lance Taylor <i...@google.com> * go-gcc.cc (Gcc_backend::init_statement): Don't initialize a zero-sized variable. (go_non_zero_struct): New global variable. (Gcc_backend::non_zero_size_type): New function. (Gcc_backend::global_variable): Don't build an assignment for a zero-sized value. * go-c.h (go_non_zero_struct): Declare. * config-lang.in (gtfiles): Add go-c.h.
Index: gcc/go/go-gcc.cc =================================================================== --- gcc/go/go-gcc.cc (revision 184684) +++ gcc/go/go-gcc.cc (working copy) @@ -338,6 +338,9 @@ class Gcc_backend : public Backend Btype* fill_in_array(Btype*, Btype*, Bexpression*); + + tree + non_zero_size_type(tree); }; // A helper function. @@ -870,9 +873,27 @@ Gcc_backend::init_statement(Bvariable* v if (var_tree == error_mark_node || init_tree == error_mark_node) return this->error_statement(); gcc_assert(TREE_CODE(var_tree) == VAR_DECL); - DECL_INITIAL(var_tree) = init_tree; - return this->make_statement(build1_loc(DECL_SOURCE_LOCATION(var_tree), - DECL_EXPR, void_type_node, var_tree)); + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // initialization of a zero-sized expression to a non-zero sized + // variable, or vice-versa. Avoid crashes by omitting the + // initializer. Such initializations don't mean anything anyhow. + if (int_size_in_bytes(TREE_TYPE(var_tree)) != 0 + && init_tree != NULL_TREE + && int_size_in_bytes(TREE_TYPE(init_tree)) != 0) + { + DECL_INITIAL(var_tree) = init_tree; + init_tree = NULL_TREE; + } + + tree ret = build1_loc(DECL_SOURCE_LOCATION(var_tree), DECL_EXPR, + void_type_node, var_tree); + if (init_tree != NULL_TREE) + ret = build2_loc(DECL_SOURCE_LOCATION(var_tree), COMPOUND_EXPR, + void_type_node, init_tree, ret); + + return this->make_statement(ret); } // Assignment. @@ -885,6 +906,18 @@ Gcc_backend::assignment_statement(Bexpre tree rhs_tree = rhs->get_tree(); if (lhs_tree == error_mark_node || rhs_tree == error_mark_node) return this->error_statement(); + + // To avoid problems with GNU ld, we don't make zero-sized + // externally visible variables. That might lead us to doing an + // assignment of a zero-sized expression to a non-zero sized + // expression; avoid crashes here by avoiding assignments of + // zero-sized expressions. Such assignments don't really mean + // anything anyhow. + if (int_size_in_bytes(TREE_TYPE(lhs_tree)) == 0 + || int_size_in_bytes(TREE_TYPE(rhs_tree)) == 0) + return this->compound_statement(this->expression_statement(lhs), + this->expression_statement(rhs)); + return this->make_statement(fold_build2_loc(location.gcc_location(), MODIFY_EXPR, void_type_node, @@ -1178,6 +1211,48 @@ Gcc_backend::block_statement(Bblock* bbl return this->make_statement(bind_tree); } +// This is not static because we declare it with GTY(()) in go-c.h. +tree go_non_zero_struct; + +// Return a type corresponding to TYPE with non-zero size. + +tree +Gcc_backend::non_zero_size_type(tree type) +{ + if (int_size_in_bytes(type) != 0) + return type; + + switch (TREE_CODE(type)) + { + case RECORD_TYPE: + { + if (go_non_zero_struct == NULL_TREE) + { + type = make_node(RECORD_TYPE); + tree field = build_decl(UNKNOWN_LOCATION, FIELD_DECL, + get_identifier("dummy"), + boolean_type_node); + DECL_CONTEXT(field) = type; + TYPE_FIELDS(type) = field; + layout_type(type); + go_non_zero_struct = type; + } + return go_non_zero_struct; + } + + case ARRAY_TYPE: + { + tree element_type = non_zero_size_type(TREE_TYPE(type)); + return build_array_type_nelts(element_type, 1); + } + + default: + gcc_unreachable(); + } + + gcc_unreachable(); +} + // Make a global variable. Bvariable* @@ -1193,6 +1268,10 @@ Gcc_backend::global_variable(const std:: if (type_tree == error_mark_node) return this->error_variable(); + // The GNU linker does not like dynamic variables with zero size. + if ((is_external || !is_hidden) && int_size_in_bytes(type_tree) == 0) + type_tree = this->non_zero_size_type(type_tree); + std::string var_name(package_name); var_name.push_back('.'); var_name.append(name); Index: gcc/go/config-lang.in =================================================================== --- gcc/go/config-lang.in (revision 184521) +++ gcc/go/config-lang.in (working copy) @@ -34,7 +34,7 @@ target_libs="target-libgo target-libffi" # compiler during stage 1. lang_requires_boot_languages=c++ -gtfiles="\$(srcdir)/go/go-lang.c" +gtfiles="\$(srcdir)/go/go-lang.c \$(srcdir)/go/go-c.h" # Do not build by default. build_by_default="no" Index: gcc/go/gofrontend/gogo-tree.cc =================================================================== --- gcc/go/gofrontend/gogo-tree.cc (revision 184521) +++ gcc/go/gofrontend/gogo-tree.cc (working copy) @@ -843,7 +843,9 @@ Gogo::write_globals() this->backend()->global_variable_set_init(var, tree_to_expr(init)); } - else if (is_sink) + else if (is_sink + || int_size_in_bytes(TREE_TYPE(init)) == 0 + || int_size_in_bytes(TREE_TYPE(vec[i])) == 0) var_init_tree = init; else var_init_tree = fold_build2_loc(no->location().gcc_location(), Index: gcc/go/go-c.h =================================================================== --- gcc/go/go-c.h (revision 184521) +++ gcc/go/go-c.h (working copy) @@ -69,6 +69,8 @@ extern void go_write_export_data (const extern const char *go_read_export_data (int, off_t, char **, size_t *, int *); +extern GTY(()) tree go_non_zero_struct; + #if defined(__cplusplus) && !defined(ENABLE_BUILD_WITH_CXX) } /* End extern "C". */ #endif