diff mbox

Go patch committed: Avoid zero-sized global variables

Message ID mcr4ntypsxe.fsf@dhcp-172-18-216-180.mtv.corp.google.com
State New
Headers show

Commit Message

Ian Lance Taylor March 8, 2012, 11:35 p.m. UTC
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.
diff mbox

Patch

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