This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Go patch committed: Avoid zero-sized global variables
- From: Ian Lance Taylor <iant at google dot com>
- To: gcc-patches at gcc dot gnu dot org, gofrontend-dev at googlegroups dot com
- Date: Thu, 08 Mar 2012 15:35:41 -0800
- Subject: Go patch committed: Avoid zero-sized global variables
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 <iant@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