This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Go patch committed: Use precise type information on the heap


This patch from Chris Manghane adds support for precise type information
to the gccgo garbage collector.  This is only the first step, as the
compiler does not pass the type information to the allocator in most
cases.  I think that right now the precise type information will only be
available for slices.  Also the precise type information is only ever
available on the heap, not on the stack, and not yet for global
variables.  Still, this is a key first step.

Bootstrapped and ran Go testsuite on x86_64-unknown-linux-gnu.
Committed to mainline.

Ian


2014-09-03  Chris Manghane  <cmang@google.com>

	* go-gcc.cc (Gcc_backend::implicit_variable): Remove init
	parameter.  Add is_hidden parameter.
	(Gcc_backend::implicit_variable_set_init): New method.
	(Gcc_backend::implicit_variable_reference): New method.


Index: gcc/go/go-gcc.cc
===================================================================
--- gcc/go/go-gcc.cc	(revision 214893)
+++ gcc/go/go-gcc.cc	(revision 214894)
@@ -389,9 +389,16 @@ class Gcc_backend : public Backend
 		     Location, Bstatement**);
 
   Bvariable*
-  implicit_variable(const std::string&, Btype*, Bexpression*, bool, bool,
+  implicit_variable(const std::string&, Btype*, bool, bool, bool,
 		    size_t);
 
+  void
+  implicit_variable_set_init(Bvariable*, const std::string&, Btype*,
+			     bool, bool, bool, Bexpression*);
+
+  Bvariable*
+  implicit_variable_reference(const std::string&, Btype*);
+
   Bvariable*
   immutable_struct(const std::string&, bool, bool, Btype*, Location);
 
@@ -2505,45 +2512,101 @@ Gcc_backend::temporary_variable(Bfunctio
 
 Bvariable*
 Gcc_backend::implicit_variable(const std::string& name, Btype* type,
-			       Bexpression* init, bool is_constant,
+			       bool is_hidden, bool is_constant,
 			       bool is_common, size_t alignment)
 {
   tree type_tree = type->get_tree();
-  tree init_tree;
-  if (init == NULL)
-    init_tree = NULL_TREE;
-  else
-    init_tree = init->get_tree();
-  if (type_tree == error_mark_node || init_tree == error_mark_node)
+  if (type_tree == error_mark_node)
     return this->error_variable();
 
   tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
                          get_identifier_from_string(name), type_tree);
   DECL_EXTERNAL(decl) = 0;
-  TREE_PUBLIC(decl) = 0;
+  TREE_PUBLIC(decl) = !is_hidden;
   TREE_STATIC(decl) = 1;
+  TREE_USED(decl) = 1;
   DECL_ARTIFICIAL(decl) = 1;
   if (is_common)
     {
       DECL_COMMON(decl) = 1;
-      TREE_PUBLIC(decl) = 1;
-      gcc_assert(init_tree == NULL_TREE);
+
+      // When the initializer for one implicit_variable refers to another,
+      // it needs to know the visibility of the referenced struct so that
+      // compute_reloc_for_constant will return the right value.  On many
+      // systems calling make_decl_one_only will mark the decl as weak,
+      // which will change the return value of compute_reloc_for_constant.
+      // We can't reliably call make_decl_one_only yet, because we don't
+      // yet know the initializer.  This issue doesn't arise in C because
+      // Go initializers, unlike C initializers, can be indirectly
+      // recursive.  To ensure that compute_reloc_for_constant computes
+      // the right value if some other initializer refers to this one, we
+      // mark this symbol as weak here.  We undo that below in
+      // immutable_struct_set_init before calling mark_decl_one_only.
+      DECL_WEAK(decl) = 1;
     }
-  else if (is_constant)
+  if (is_constant)
     {
       TREE_READONLY(decl) = 1;
       TREE_CONSTANT(decl) = 1;
     }
-  DECL_INITIAL(decl) = init_tree;
-
   if (alignment != 0)
     {
       DECL_ALIGN(decl) = alignment * BITS_PER_UNIT;
       DECL_USER_ALIGN(decl) = 1;
     }
 
+  go_preserve_from_gc(decl);
+  return new Bvariable(decl);
+}
+
+// Set the initalizer for a variable created by implicit_variable.
+// This is where we finish compiling the variable.
+
+void
+Gcc_backend::implicit_variable_set_init(Bvariable* var, const std::string&,
+					Btype*, bool, bool, bool is_common,
+					Bexpression* init)
+{
+  tree decl = var->get_tree();
+  tree init_tree;
+  if (init == NULL)
+    init_tree = NULL_TREE;
+  else
+    init_tree = init->get_tree();
+  if (decl == error_mark_node || init_tree == error_mark_node)
+    return;
+
+  DECL_INITIAL(decl) = init_tree;
+
+  // Now that DECL_INITIAL is set, we can't call make_decl_one_only.
+  // See the comment where DECL_WEAK is set in implicit_variable.
+  if (is_common)
+    {
+      DECL_WEAK(decl) = 0;
+      make_decl_one_only(decl, DECL_ASSEMBLER_NAME(decl));
+    }
+
+  resolve_unique_section(decl, 2, 1);
+
   rest_of_decl_compilation(decl, 1, 0);
+}
+
+// Return a reference to an implicit variable defined in another package.
 
+Bvariable*
+Gcc_backend::implicit_variable_reference(const std::string& name, Btype* btype)
+{
+  tree type_tree = btype->get_tree();
+  if (type_tree == error_mark_node)
+    return this->error_variable();
+
+  tree decl = build_decl(BUILTINS_LOCATION, VAR_DECL,
+                         get_identifier_from_string(name), type_tree);
+  DECL_EXTERNAL(decl) = 0;
+  TREE_PUBLIC(decl) = 1;
+  TREE_STATIC(decl) = 1;
+  DECL_ARTIFICIAL(decl) = 1;
+  go_preserve_from_gc(decl);
   return new Bvariable(decl);
 }
 
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 214893)
+++ gcc/go/gofrontend/gogo.cc	(revision 214894)
@@ -655,9 +655,13 @@ Gogo::backend_zero_value()
 
   Btype* barray_type = this->backend()->array_type(bbtype_type, blength);
 
-  return this->backend()->implicit_variable(this->zero_value_->name(),
-					    barray_type, NULL, true, true,
-					    this->zero_value_align_);
+  std::string zname = this->zero_value_->name();
+  Bvariable* zvar =
+    this->backend()->implicit_variable(zname, barray_type, false,
+				       true, true, this->zero_value_align_);
+  this->backend()->implicit_variable_set_init(zvar, zname, barray_type,
+					      false, true, true, NULL);
+  return zvar;
 }
 
 // Add statements to INIT_STMTS which run the initialization
@@ -6837,8 +6841,10 @@ Named_object::get_backend(Gogo* gogo, st
           {
             named_type->
                 type_descriptor_pointer(gogo, Linemap::predeclared_location());
+	    named_type->gc_symbol_pointer(gogo);
             Type* pn = Type::make_pointer_type(named_type);
             pn->type_descriptor_pointer(gogo, Linemap::predeclared_location());
+	    pn->gc_symbol_pointer(gogo);
           }
       }
       break;
Index: gcc/go/gofrontend/types.h
===================================================================
--- gcc/go/gofrontend/types.h	(revision 214893)
+++ gcc/go/gofrontend/types.h	(revision 214894)
@@ -83,6 +83,28 @@ static const int RUNTIME_TYPE_KIND_UNSAF
 
 static const int RUNTIME_TYPE_KIND_NO_POINTERS = (1 << 7);
 
+// GC instruction opcodes.  These must match the values in libgo/runtime/mgc0.h.
+enum GC_Opcode
+{
+  GC_END = 0,     // End of object, loop or subroutine.
+  GC_PTR,         // A typed pointer.
+  GC_APTR,        // Pointer to an arbitrary object.
+  GC_ARRAY_START, // Start an array with a fixed length.
+  GC_ARRAY_NEXT,  // The next element of an array.
+  GC_CALL,        // Call a subroutine.
+  GC_CHAN_PTR,    // Go channel.
+  GC_STRING,      // Go string.
+  GC_EFACE,       // interface{}.
+  GC_IFACE,       // interface{...}.
+  GC_SLICE,       // Go slice.
+  GC_REGION,      // A region/part of the current object.
+
+  GC_NUM_INSTR    // Number of instruction opcodes
+};
+
+// The GC Stack Capacity must match the value in libgo/runtime/mgc0.h.
+static const int GC_STACK_CAPACITY = 8;
+
 // To build the complete list of methods for a named type we need to
 // gather all methods from anonymous fields.  Those methods may
 // require an arbitrary set of indirections and field offsets.  There
@@ -911,6 +933,10 @@ class Type
   Bexpression*
   type_descriptor_pointer(Gogo* gogo, Location);
 
+  // Build the Garbage Collection symbol for this type.  Return a pointer to it.
+  Bexpression*
+  gc_symbol_pointer(Gogo* gogo);
+
   // Return the type reflection string for this type.
   std::string
   reflection(Gogo*) const;
@@ -996,6 +1022,9 @@ class Type
   do_type_descriptor(Gogo*, Named_type* name) = 0;
 
   virtual void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int) = 0;
+
+  virtual void
   do_reflection(Gogo*, std::string*) const = 0;
 
   virtual void
@@ -1050,6 +1079,22 @@ class Type
   type_descriptor_constructor(Gogo*, int runtime_type_kind, Named_type*,
 			      const Methods*, bool only_value_methods);
 
+  // Generate the GC symbol for this TYPE.  VALS is the data so far in this
+  // symbol; extra values will be appended in do_gc_symbol.  OFFSET is the
+  // offset into the symbol where the GC data is located.  STACK_SIZE is the
+  // size of the GC stack when dealing with array types.
+  static void
+  gc_symbol(Gogo*, Type* type, Expression_list** vals, Expression** offset,
+	    int stack_size);
+
+  // Build a composite literal for the GC symbol of this type.
+  Expression*
+  gc_symbol_constructor(Gogo*);
+
+  // Advance the OFFSET of the GC symbol by the size of this type.
+  void
+  advance_gc_offset(Expression** offset);
+
   // For the benefit of child class reflection string generation.
   void
   append_reflection(const Type* type, Gogo* gogo, std::string* ret) const
@@ -1126,6 +1171,16 @@ class Type
   void
   make_type_descriptor_var(Gogo*);
 
+  // Map unnamed types to type descriptor decls.
+  typedef Unordered_map_hash(const Type*, Bvariable*, Type_hash_identical,
+			     Type_identical) GC_symbol_vars;
+
+  static GC_symbol_vars gc_symbol_vars;
+
+  // Build the GC symbol for this type.
+  void
+  make_gc_symbol_var(Gogo*);
+
   // Return the name of the type descriptor variable.  If NAME is not
   // NULL, it is the name to use.
   std::string
@@ -1253,6 +1308,9 @@ class Type
   // The type descriptor for this type.  This starts out as NULL and
   // is filled in as needed.
   Bvariable* type_descriptor_var_;
+  // The GC symbol for this type.  This starts out as NULL and
+  // is filled in as needed.
+  Bvariable* gc_symbol_var_;
 };
 
 // Type hash table operations.
@@ -1507,6 +1565,10 @@ protected:
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
+  { this->advance_gc_offset(offset); }
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
  private:
@@ -1584,6 +1646,10 @@ class Float_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
+  { this->advance_gc_offset(offset); }
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
  private:
@@ -1653,6 +1719,10 @@ class Complex_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
+  { this->advance_gc_offset(offset); }
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
  private:
@@ -1702,6 +1772,9 @@ class String_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string* ret) const;
 
  private:
@@ -1837,6 +1910,9 @@ class Function_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
   void
@@ -1953,6 +2029,9 @@ class Pointer_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
   void
@@ -2251,6 +2330,9 @@ class Struct_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
   void
@@ -2393,6 +2475,9 @@ class Array_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
   void
@@ -2408,6 +2493,12 @@ class Array_type : public Type
   Expression*
   slice_type_descriptor(Gogo*, Named_type*);
 
+  void
+  slice_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
+  array_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
   // The type of elements of the array.
   Type* element_type_;
   // The number of elements.  This may be NULL.
@@ -2485,6 +2576,9 @@ class Map_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
   void
@@ -2571,6 +2665,9 @@ class Channel_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
   void
@@ -2704,6 +2801,9 @@ class Interface_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string*) const;
 
   void
@@ -2989,6 +3089,10 @@ class Named_type : public Type
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo* gogo, Expression_list** vals, Expression** offset,
+	       int stack);
+
+  void
   do_mangled_name(Gogo*, std::string* ret) const;
 
   void
@@ -3133,6 +3237,11 @@ class Forward_declaration_type : public
   do_reflection(Gogo*, std::string*) const;
 
   void
+  do_gc_symbol(Gogo* gogo, Expression_list** vals, Expression** offset,
+	       int stack_size)
+  { Type::gc_symbol(gogo, this->real_type(), vals, offset, stack_size); }
+
+  void
   do_mangled_name(Gogo*, std::string* ret) const;
 
   void
Index: gcc/go/gofrontend/expressions.h
===================================================================
--- gcc/go/gofrontend/expressions.h	(revision 214893)
+++ gcc/go/gofrontend/expressions.h	(revision 214894)
@@ -103,6 +103,7 @@ class Expression
     EXPRESSION_HEAP,
     EXPRESSION_RECEIVE,
     EXPRESSION_TYPE_DESCRIPTOR,
+    EXPRESSION_GC_SYMBOL,
     EXPRESSION_TYPE_INFO,
     EXPRESSION_SLICE_INFO,
     EXPRESSION_SLICE_VALUE,
@@ -349,6 +350,11 @@ class Expression
   static Expression*
   make_type_descriptor(Type* type, Location);
 
+  // Make an expression which evaluates to the address of the gc
+  // symbol for TYPE.
+  static Expression*
+  make_gc_symbol(Type* type);
+
   // Make an expression which evaluates to some characteristic of a
   // type.  These are only used for type descriptors, so there is no
   // location parameter.
@@ -1513,6 +1519,10 @@ class Binary_expression : public Express
   { return this->left_->is_constant() && this->right_->is_constant(); }
 
   bool
+  do_is_immutable() const
+  { return this->left_->is_immutable() && this->right_->is_immutable(); }
+
+  bool
   do_numeric_constant_value(Numeric_constant*) const;
 
   bool
Index: gcc/go/gofrontend/backend.h
===================================================================
--- gcc/go/gofrontend/backend.h	(revision 214893)
+++ gcc/go/gofrontend/backend.h	(revision 214894)
@@ -545,24 +545,55 @@ class Backend
 		     Bstatement** pstatement) = 0;
 
   // Create an implicit variable that is compiler-defined.  This is
-  // used when generating GC root variables, when storing the values
-  // of a slice constructor, and for the zero value of types.  NAME is
-  // the name of the variable, either gc# for GC roots or C# for slice
-  // initializers.  TYPE is the type of the implicit variable with an
-  // initial value INIT.  IS_CONSTANT is true if the implicit variable
-  // should be treated like it is immutable.  For slice initializers,
-  // if the values must be copied to the heap, the variable
-  // IS_CONSTANT.  IS_COMMON is true if the implicit variable should
+  // used when generating GC data and roots, when storing the values
+  // of a slice constructor, and for the zero value of types.  This returns a
+  // Bvariable because it corresponds to an initialized variable in C.
+  //
+  // NAME is the name to use for the initialized variable this will create.
+  //
+  // TYPE is the type of the implicit variable. 
+  //
+  // IS_HIDDEN will be true if the descriptor should only be visible
+  // within the current object.
+  //
+  // IS_CONSTANT is true if the implicit variable should be treated like it is
+  // immutable.  For slice initializers, if the values must be copied to the
+  // heap, the variable IS_CONSTANT.
+  //
+  // IS_COMMON is true if the implicit variable should
   // be treated as a common variable (multiple definitions with
   // different sizes permitted in different object files, all merged
   // into the largest definition at link time); this will be true for
-  // the zero value.  If IS_COMMON is true, INIT will be NULL, and the
-  // variable should be initialized to all zeros.  If ALIGNMENT is not
-  // zero, it is the desired alignment of the variable.
+  // the zero value.  IS_HIDDEN and IS_COMMON will never both be true.
+  //
+  // If ALIGNMENT is not zero, it is the desired alignment of the variable.
   virtual Bvariable*
-  implicit_variable(const std::string& name, Btype* type, Bexpression* init,
+  implicit_variable(const std::string& name, Btype* type, bool is_hidden,
 		    bool is_constant, bool is_common, size_t alignment) = 0;
 
+
+  // Set the initial value of a variable created by implicit_variable.
+  // This must be called even if there is no initializer, i.e., INIT is NULL.
+  // The NAME, TYPE, IS_HIDDEN, IS_CONSTANT, and IS_COMMON parameters are
+  // the same ones passed to implicit_variable.  INIT will be a composite
+  // literal of type TYPE.  It will not contain any function calls or anything
+  // else that can not be put into a read-only data section.
+  // It may contain the address of variables created by implicit_variable.
+  //
+  // If IS_COMMON is true, INIT will be NULL, and the
+  // variable should be initialized to all zeros.
+  virtual void
+  implicit_variable_set_init(Bvariable*, const std::string& name, Btype* type,
+			     bool is_hidden, bool is_constant, bool is_common,
+			     Bexpression* init) = 0;
+
+  // Create a reference to a named implicit variable defined in some other
+  // package.  This will be a variable created by a call to implicit_variable
+  // with the same NAME and TYPE and with IS_COMMON passed as false.  This
+  // corresponds to an extern global variable in C.
+  virtual Bvariable*
+  implicit_variable_reference(const std::string& name, Btype* type) = 0;
+
   // Create a named immutable initialized data structure.  This is
   // used for type descriptors, map descriptors, and function
   // descriptors.  This returns a Bvariable because it corresponds to
Index: gcc/go/gofrontend/types.cc
===================================================================
--- gcc/go/gofrontend/types.cc	(revision 214893)
+++ gcc/go/gofrontend/types.cc	(revision 214894)
@@ -36,7 +36,8 @@ get_backend_interface_fields(Gogo* gogo,
 // Class Type.
 
 Type::Type(Type_classification classification)
-  : classification_(classification), btype_(NULL), type_descriptor_var_(NULL)
+  : classification_(classification), btype_(NULL), type_descriptor_var_(NULL),
+    gc_symbol_var_(NULL)
 {
 }
 
@@ -1236,7 +1237,7 @@ Type::make_type_descriptor_var(Gogo* gog
 	Type::type_descriptor_vars.insert(std::make_pair(this, bvnull));
       if (!ins.second)
 	{
-	  // We've already build a type descriptor for this type.
+	  // We've already built a type descriptor for this type.
 	  this->type_descriptor_var_ = ins.first->second;
 	  return;
 	}
@@ -1405,6 +1406,18 @@ Type::named_type_descriptor(Gogo* gogo,
   return type->do_type_descriptor(gogo, name);
 }
 
+// Generate the GC symbol for this TYPE.  VALS is the data so far in this
+// symbol; extra values will be appended in do_gc_symbol.  OFFSET is the
+// offset into the symbol where the GC data is located.  STACK_SIZE is the
+// size of the GC stack when dealing with array types.
+
+void
+Type::gc_symbol(Gogo* gogo, Type* type, Expression_list** vals,
+		Expression** offset, int stack_size)
+{
+  type->do_gc_symbol(gogo, vals, offset, stack_size);
+}
+
 // Make a builtin struct type from a list of fields.  The fields are
 // pairs of a name and a type.
 
@@ -1519,14 +1532,15 @@ Type::make_type_descriptor_type()
       // The type descriptor type.
 
       Struct_type* type_descriptor_type =
-	Type::make_builtin_struct_type(11,
-				       "Kind", uint8_type,
+	Type::make_builtin_struct_type(12,
+				       "kind", uint8_type,
 				       "align", uint8_type,
 				       "fieldAlign", uint8_type,
 				       "size", uintptr_type,
 				       "hash", uint32_type,
 				       "hashfn", uintptr_type,
 				       "equalfn", uintptr_type,
+				       "gc", unsafe_pointer_type,
 				       "string", pointer_string_type,
 				       "", pointer_uncommon_type,
 				       "ptrToThis",
@@ -1973,7 +1987,7 @@ Type::type_descriptor_constructor(Gogo*
   if (!this->has_pointer())
     runtime_type_kind |= RUNTIME_TYPE_KIND_NO_POINTERS;
   Struct_field_list::const_iterator p = fields->begin();
-  go_assert(p->is_field_name("Kind"));
+  go_assert(p->is_field_name("kind"));
   mpz_t iv;
   mpz_init_set_ui(iv, runtime_type_kind);
   vals->push_back(Expression::make_integer(&iv, p->type(), bloc));
@@ -2019,6 +2033,10 @@ Type::type_descriptor_constructor(Gogo*
   vals->push_back(Expression::make_func_code_reference(equal_fn, bloc));
 
   ++p;
+  go_assert(p->is_field_name("gc"));
+  vals->push_back(Expression::make_gc_symbol(this));
+
+  ++p;
   go_assert(p->is_field_name("string"));
   Expression* s = Expression::make_string((name != NULL
 					   ? name->reflection(gogo)
@@ -2067,6 +2085,160 @@ Type::type_descriptor_constructor(Gogo*
   return Expression::make_struct_composite_literal(td_type, vals, bloc);
 }
 
+// Return a pointer to the Garbage Collection information for this type.
+
+Bexpression*
+Type::gc_symbol_pointer(Gogo* gogo)
+{
+  Type* t = this->forwarded();
+  if (t->named_type() != NULL && t->named_type()->is_alias())
+    t = t->named_type()->real_type();
+  if (t->gc_symbol_var_ == NULL)
+    {
+      t->make_gc_symbol_var(gogo);
+      go_assert(t->gc_symbol_var_ != NULL);
+    }
+  Location bloc = Linemap::predeclared_location();
+  Bexpression* var_expr =
+      gogo->backend()->var_expression(t->gc_symbol_var_, bloc);
+  return gogo->backend()->address_expression(var_expr, bloc);
+}
+
+// A mapping from unnamed types to GC symbol variables.
+
+Type::GC_symbol_vars Type::gc_symbol_vars;
+
+// Build the GC symbol for this type.
+
+void
+Type::make_gc_symbol_var(Gogo* gogo)
+{
+  go_assert(this->gc_symbol_var_ == NULL);
+
+  Named_type* nt = this->named_type();
+
+  // We can have multiple instances of unnamed types and similar to type
+  // descriptors, we only want to the emit the GC data once, so we use a
+  // hash table.
+  Bvariable** phash = NULL;
+  if (nt == NULL)
+    {
+      Bvariable* bvnull = NULL;
+      std::pair<GC_symbol_vars::iterator, bool> ins =
+	Type::gc_symbol_vars.insert(std::make_pair(this, bvnull));
+      if (!ins.second)
+	{
+	  // We've already built a gc symbol for this type.
+	  this->gc_symbol_var_ = ins.first->second;
+	  return;
+	}
+      phash = &ins.first->second;
+    }
+
+  std::string sym_name = this->type_descriptor_var_name(gogo, nt) + "$gc";
+
+  // Build the contents of the gc symbol.
+  Expression* sym_init = this->gc_symbol_constructor(gogo);
+  Btype* sym_btype = sym_init->type()->get_backend(gogo);
+
+  // If the type descriptor for this type is defined somewhere else, so is the
+  // GC symbol.
+  const Package* dummy;
+  if (this->type_descriptor_defined_elsewhere(nt, &dummy))
+    {
+      this->gc_symbol_var_ =
+	gogo->backend()->implicit_variable_reference(sym_name, sym_btype);
+      if (phash != NULL)
+	*phash = this->gc_symbol_var_;
+      return;
+    }
+
+  // See if this gc symbol can appear in multiple packages.
+  bool is_common = false;
+  if (nt != NULL)
+    {
+      // We create the symbol for a builtin type whenever we need
+      // it.
+      is_common = nt->is_builtin();
+    }
+  else
+    {
+      // This is an unnamed type.  The descriptor could be defined in
+      // any package where it is needed, and the linker will pick one
+      // descriptor to keep.
+      is_common = true;
+    }
+
+  // Since we are building the GC symbol in this package, we must create the
+  // variable before converting the initializer to its backend representation
+  // because the initializer may refer to the GC symbol for this type.
+  this->gc_symbol_var_ =
+    gogo->backend()->implicit_variable(sym_name, sym_btype, false, true, is_common, 0);
+  if (phash != NULL)
+    *phash = this->gc_symbol_var_;
+
+  Translate_context context(gogo, NULL, NULL, NULL);
+  context.set_is_const();
+  Bexpression* sym_binit = sym_init->get_backend(&context);
+  gogo->backend()->implicit_variable_set_init(this->gc_symbol_var_, sym_name,
+					      sym_btype, false, true, is_common,
+					      sym_binit);
+}
+
+// Return an array literal for the Garbage Collection information for this type.
+
+Expression*
+Type::gc_symbol_constructor(Gogo* gogo)
+{
+  Location bloc = Linemap::predeclared_location();
+
+  // The common GC Symbol data starts with the width of the type and ends
+  // with the GC Opcode GC_END.
+  // However, for certain types, the GC symbol may include extra information
+  // before the ending opcode, so we pass the expression list into
+  // Type::gc_symbol to allow it to add extra information as is necessary.
+  Expression_list* vals = new Expression_list;
+
+  Type* uintptr_t = Type::lookup_integer_type("uintptr");
+  // width
+  vals->push_back(Expression::make_type_info(this,
+					     Expression::TYPE_INFO_SIZE));
+
+  mpz_t off;
+  mpz_init_set_ui(off, 0UL);
+  Expression* offset = Expression::make_integer(&off, uintptr_t, bloc);
+  mpz_clear(off);
+
+  this->do_gc_symbol(gogo, &vals, &offset, 0);
+
+  mpz_t end;
+  mpz_init_set_ui(end, GC_END);
+  vals->push_back(Expression::make_integer(&end, uintptr_t, bloc));
+  mpz_clear(end);
+
+  mpz_t lenval;
+  mpz_init_set_ui(lenval, vals->size() + 1);
+  Expression* len = Expression::make_integer(&lenval, NULL, bloc);
+  mpz_clear(lenval);
+
+  Array_type* gc_symbol_type = Type::make_array_type(uintptr_t, len);
+  return Expression::make_array_composite_literal(gc_symbol_type, vals, bloc);
+}
+
+// Advance the OFFSET of the GC symbol by this type's width.
+
+void
+Type::advance_gc_offset(Expression** offset)
+{
+  if (this->is_error_type())
+    return;
+
+  Location bloc = Linemap::predeclared_location();
+  Expression* width =
+    Expression::make_type_info(this, Expression::TYPE_INFO_SIZE);
+  *offset = Expression::make_binary(OPERATOR_PLUS, *offset, width, bloc);
+}
+
 // Return a composite literal for the uncommon type information for
 // this type.  UNCOMMON_STRUCT_TYPE is the type of the uncommon type
 // struct.  If name is not NULL, it is the name of the type.  If
@@ -2498,6 +2670,10 @@ class Error_type : public Type
   { go_assert(saw_errors()); }
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
+  { go_assert(saw_errors()); }
+
+  void
   do_mangled_name(Gogo*, std::string* ret) const
   { ret->push_back('E'); }
 };
@@ -2536,6 +2712,10 @@ class Void_type : public Type
   { }
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
+  { }
+
+  void
   do_mangled_name(Gogo*, std::string* ret) const
   { ret->push_back('v'); }
 };
@@ -2574,6 +2754,9 @@ class Boolean_type : public Type
   { ret->append("bool"); }
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int);
+
+  void
   do_mangled_name(Gogo*, std::string* ret) const
   { ret->push_back('b'); }
 };
@@ -2593,6 +2776,12 @@ Boolean_type::do_type_descriptor(Gogo* g
     }
 }
 
+// Update the offset of the GC symbol.
+
+void
+Boolean_type::do_gc_symbol(Gogo*, Expression_list**, Expression** offset, int)
+{ this->advance_gc_offset(offset); }
+
 Type*
 Type::make_boolean_type()
 {
@@ -3102,6 +3291,22 @@ String_type::do_reflection(Gogo*, std::s
   ret->append("string");
 }
 
+// Generate GC symbol for strings.
+
+void
+String_type::do_gc_symbol(Gogo*, Expression_list** vals,
+			  Expression** offset, int)
+{
+  Location bloc = Linemap::predeclared_location();
+  Type* uintptr_type = Type::lookup_integer_type("uintptr");
+  mpz_t opval;
+  mpz_init_set_ui(opval, GC_STRING);
+  (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc));
+  mpz_clear(opval);
+  (*vals)->push_back(*offset);
+  this->advance_gc_offset(offset);
+}
+
 // Mangled name of a string type.
 
 void
@@ -3173,6 +3378,10 @@ class Sink_type : public Type
   { go_unreachable(); }
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
+  { go_unreachable(); }
+
+  void
   do_mangled_name(Gogo*, std::string*) const
   { go_unreachable(); }
 };
@@ -3754,6 +3963,25 @@ Function_type::do_reflection(Gogo* gogo,
     }
 }
 
+// Generate GC symbol for a function type.
+
+void
+Function_type::do_gc_symbol(Gogo*, Expression_list** vals,
+			    Expression** offset, int)
+{
+  Location bloc = Linemap::predeclared_location();
+  Type* uintptr_type = Type::lookup_integer_type("uintptr");
+
+  // We use GC_APTR here because we do not currently have a way to describe the
+  // the type of the possible function closure.  FIXME.
+  mpz_t opval;
+  mpz_init_set_ui(opval, GC_APTR);
+  (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc));
+  mpz_clear(opval);
+  (*vals)->push_back(*offset);
+  this->advance_gc_offset(offset);
+}
+
 // Mangled name.
 
 void
@@ -4156,6 +4384,26 @@ Pointer_type::do_reflection(Gogo* gogo,
   this->append_reflection(this->to_type_, gogo, ret);
 }
 
+// Generate GC symbol for pointer types.
+
+void
+Pointer_type::do_gc_symbol(Gogo*, Expression_list** vals,
+			   Expression** offset, int)
+{
+  Location loc = Linemap::predeclared_location();
+  Type* uintptr_type = Type::lookup_integer_type("uintptr");
+
+  mpz_t opval;
+  mpz_init_set_ui(opval, this->to_type_->has_pointer() ? GC_PTR : GC_APTR);
+  (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, loc));
+  mpz_clear(opval);
+  (*vals)->push_back(*offset);
+
+  if (this->to_type_->has_pointer())
+    (*vals)->push_back(Expression::make_gc_symbol(this->to_type_));
+  this->advance_gc_offset(offset);
+}
+
 // Mangled name.
 
 void
@@ -4236,6 +4484,10 @@ class Nil_type : public Type
   { go_unreachable(); }
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
+  { go_unreachable(); }
+
+  void
   do_mangled_name(Gogo*, std::string* ret) const
   { ret->push_back('n'); }
 };
@@ -4293,6 +4545,10 @@ class Call_multiple_result_type : public
   { go_assert(saw_errors()); }
 
   void
+  do_gc_symbol(Gogo*, Expression_list**, Expression**, int)
+  { go_unreachable(); }
+
+  void
   do_mangled_name(Gogo*, std::string*) const
   { go_assert(saw_errors()); }
 
@@ -5319,6 +5575,27 @@ Struct_type::do_reflection(Gogo* gogo, s
   ret->push_back('}');
 }
 
+// Generate GC symbol for struct types.
+
+void
+Struct_type::do_gc_symbol(Gogo* gogo, Expression_list** vals,
+			  Expression** offset, int stack_size)
+{
+  Location bloc = Linemap::predeclared_location();
+  const Struct_field_list* sfl = this->fields();
+  for (Struct_field_list::const_iterator p = sfl->begin();
+       p != sfl->end();
+       ++p)
+    {
+      Expression* field_offset =
+  	Expression::make_struct_field_offset(this, &*p);
+      Expression* o =
+  	Expression::make_binary(OPERATOR_PLUS, *offset, field_offset, bloc);
+      Type::gc_symbol(gogo, p->type(), vals, &o, stack_size);
+    }
+  this->advance_gc_offset(offset);
+}
+
 // Mangled name.
 
 void
@@ -6204,6 +6481,115 @@ Array_type::do_reflection(Gogo* gogo, st
   this->append_reflection(this->element_type_, gogo, ret);
 }
 
+// GC Symbol construction for array types.
+
+void
+Array_type::do_gc_symbol(Gogo* gogo, Expression_list** vals,
+			 Expression** offset, int stack_size)
+{
+  if (this->length_ == NULL)
+    this->slice_gc_symbol(gogo, vals, offset, stack_size);
+  else
+    this->array_gc_symbol(gogo, vals, offset, stack_size);
+}
+
+// Generate the GC Symbol for a slice.
+
+void
+Array_type::slice_gc_symbol(Gogo* gogo, Expression_list** vals,
+			    Expression** offset, int)
+{
+  Location bloc = Linemap::predeclared_location();
+
+  // Differentiate between slices with zero-length and non-zero-length values.
+  Type* element_type = this->element_type();
+  Btype* ebtype = element_type->get_backend(gogo);
+  size_t element_size = gogo->backend()->type_size(ebtype);
+
+  Type* uintptr_type = Type::lookup_integer_type("uintptr");
+  mpz_t opval;
+  mpz_init_set_ui(opval, element_size == 0 ? GC_APTR : GC_SLICE);
+  (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc));
+  mpz_clear(opval);
+  (*vals)->push_back(*offset);
+
+  if (element_size != 0)
+    (*vals)->push_back(Expression::make_gc_symbol(element_type));
+  this->advance_gc_offset(offset);
+}
+
+// Generate the GC symbol for an array.
+
+void
+Array_type::array_gc_symbol(Gogo* gogo, Expression_list** vals,
+			    Expression** offset, int stack_size)
+{
+  Location bloc = Linemap::predeclared_location();
+
+  Numeric_constant nc;
+  unsigned long bound;
+  if (!this->length_->numeric_constant_value(&nc)
+      || nc.to_unsigned_long(&bound) == Numeric_constant::NC_UL_NOTINT)
+    go_assert(saw_errors());
+
+  Btype* pbtype = gogo->backend()->pointer_type(gogo->backend()->void_type());
+  size_t pwidth = gogo->backend()->type_size(pbtype);
+  size_t iwidth = gogo->backend()->type_size(this->get_backend(gogo));
+
+  Type* element_type = this->element_type();
+  if (bound < 1 || !element_type->has_pointer())
+    this->advance_gc_offset(offset);
+  else if (bound == 1 || iwidth <= 4 * pwidth)
+    {
+      for (unsigned int i = 0; i < bound; ++i)
+	Type::gc_symbol(gogo, element_type, vals, offset, stack_size);
+    }
+  else
+    {
+      Type* uintptr_type = Type::lookup_integer_type("uintptr");
+
+      mpz_t op;
+      if (stack_size < GC_STACK_CAPACITY)
+  	{
+  	  mpz_init_set_ui(op, GC_ARRAY_START);
+  	  (*vals)->push_back(Expression::make_integer(&op, uintptr_type, bloc));
+  	  mpz_clear(op);
+  	  (*vals)->push_back(*offset);
+	  Expression* uintptr_len =
+	    Expression::make_cast(uintptr_type, this->length_, bloc);
+  	  (*vals)->push_back(uintptr_len);
+
+	  Expression* width =
+	    Expression::make_type_info(element_type,
+				       Expression::TYPE_INFO_SIZE);
+  	  (*vals)->push_back(width);
+
+  	  mpz_t zero;
+  	  mpz_init_set_ui(zero, 0UL);
+  	  Expression* offset2 =
+  	    Expression::make_integer(&zero, uintptr_type, bloc);
+  	  mpz_clear(zero);
+
+	  Type::gc_symbol(gogo, element_type, vals, &offset2, stack_size + 1);
+  	  mpz_init_set_ui(op, GC_ARRAY_NEXT);
+  	  (*vals)->push_back(Expression::make_integer(&op, uintptr_type, bloc));
+  	}
+      else
+  	{
+  	  mpz_init_set_ui(op, GC_REGION);
+  	  (*vals)->push_back(Expression::make_integer(&op, uintptr_type, bloc));
+	  (*vals)->push_back(*offset);
+
+	  Expression* width =
+	    Expression::make_type_info(this, Expression::TYPE_INFO_SIZE);
+  	  (*vals)->push_back(width);
+	  (*vals)->push_back(Expression::make_gc_symbol(this));
+  	}
+      mpz_clear(op);
+      this->advance_gc_offset(offset);
+    }
+}
+
 // Mangled name.
 
 void
@@ -6513,6 +6899,24 @@ Map_type::do_reflection(Gogo* gogo, std:
   this->append_reflection(this->val_type_, gogo, ret);
 }
 
+// Generate GC symbol for a map.
+
+void
+Map_type::do_gc_symbol(Gogo*, Expression_list** vals,
+		       Expression** offset, int)
+{
+  // TODO(cmang): Generate GC data for the Map elements.
+  Location bloc = Linemap::predeclared_location();
+  Type* uintptr_type = Type::lookup_integer_type("uintptr");
+
+  mpz_t opval;
+  mpz_init_set_ui(opval, GC_APTR);
+  (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc));
+  mpz_clear(opval);
+  (*vals)->push_back(*offset);
+  this->advance_gc_offset(offset);
+}
+
 // Mangled name for a map.
 
 void
@@ -6686,6 +7090,30 @@ Channel_type::do_reflection(Gogo* gogo,
   this->append_reflection(this->element_type_, gogo, ret);
 }
 
+// Generate GC symbol for channels.
+
+void
+Channel_type::do_gc_symbol(Gogo*, Expression_list** vals,
+			   Expression** offset, int)
+{
+  Location bloc = Linemap::predeclared_location();
+  Type* uintptr_type = Type::lookup_integer_type("uintptr");
+
+  mpz_t opval;
+  mpz_init_set_ui(opval, GC_CHAN_PTR);
+  (*vals)->push_back(Expression::make_integer(&opval, uintptr_type, bloc));
+  mpz_clear(opval);
+  (*vals)->push_back(*offset);
+ 
+  Type* unsafeptr_type = Type::make_pointer_type(Type::make_void_type());
+  Expression* type_descriptor =
+    Expression::make_type_descriptor(this, bloc);
+  type_descriptor =
+    Expression::make_unsafe_cast(unsafeptr_type, type_descriptor, bloc);
+  (*vals)->push_back(type_descriptor);
+  this->advance_gc_offset(offset);
+}
+
 // Mangled name.
 
 void
@@ -7574,6 +8002,24 @@ Interface_type::do_reflection(Gogo* gogo
   ret->append("}");
 }
 
+// Generate GC symbol for interface types.
+
+void
+Interface_type::do_gc_symbol(Gogo*, Expression_list** vals,
+			     Expression** offset, int)
+{
+  Location bloc = Linemap::predeclared_location();
+  Type* uintptr_type = Type::lookup_integer_type("uintptr");
+
+  mpz_t opval;
+  mpz_init_set_ui(opval, this->is_empty() ? GC_EFACE : GC_IFACE);
+  (*vals)->push_back(Expression::make_integer(&opval, uintptr_type,
+					      bloc));
+  mpz_clear(opval);
+  (*vals)->push_back(*offset);
+  this->advance_gc_offset(offset);
+}
+
 // Mangled name.
 
 void
@@ -8810,6 +9256,20 @@ Named_type::do_reflection(Gogo* gogo, st
   ret->append(Gogo::unpack_hidden_name(this->named_object_->name()));
 }
 
+// Generate GC symbol for named types.
+
+void
+Named_type::do_gc_symbol(Gogo* gogo, Expression_list** vals,
+			 Expression** offset, int stack)
+{
+  if (!this->seen_)
+    {
+      this->seen_ = true;
+      Type::gc_symbol(gogo, this->real_type(), vals, offset, stack);
+      this->seen_ = false;
+    }
+}
+
 // Get the mangled name.
 
 void
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 214893)
+++ gcc/go/gofrontend/expressions.cc	(revision 214894)
@@ -3440,6 +3440,9 @@ class Unsafe_type_conversion_expression
   int
   do_traverse(Traverse* traverse);
 
+  bool
+  do_is_immutable() const;
+
   Type*
   do_type()
   { return this->type_; }
@@ -3480,6 +3483,27 @@ Unsafe_type_conversion_expression::do_tr
   return TRAVERSE_CONTINUE;
 }
 
+// Return whether an unsafe type conversion is immutable.
+
+bool
+Unsafe_type_conversion_expression::do_is_immutable() const
+{
+  Type* type = this->type_;
+  Type* expr_type = this->expr_->type();
+
+  if (type->interface_type() != NULL
+      || expr_type->interface_type() != NULL)
+    return false;
+
+  if (!this->expr_->is_immutable())
+    return false;
+
+  if (Type::are_convertible(type, expr_type, NULL))
+    return true;
+
+  return type->is_basic_type() && expr_type->is_basic_type();
+}
+
 // Convert to backend representation.
 
 Bexpression*
@@ -4115,8 +4139,11 @@ Unary_expression::do_get_backend(Transla
 			      && !context->is_const());
 	    }
 	  Bvariable* implicit =
-	    gogo->backend()->implicit_variable(buf, btype, bexpr, copy_to_heap,
+	    gogo->backend()->implicit_variable(buf, btype, true, copy_to_heap,
 					       false, 0);
+	  gogo->backend()->implicit_variable_set_init(implicit, buf, btype,
+						      true, copy_to_heap, false,
+						      bexpr);
 	  bexpr = gogo->backend()->var_expression(implicit, loc);
 	}
       else if ((this->expr_->is_composite_literal()
@@ -13987,6 +14014,65 @@ Expression::make_type_descriptor(Type* t
   return new Type_descriptor_expression(type, location);
 }
 
+// An expression which evaluates to a pointer to the Garbage Collection symbol
+// of a type.
+
+class GC_symbol_expression : public Expression
+{
+ public:
+  GC_symbol_expression(Type* type)
+    : Expression(EXPRESSION_GC_SYMBOL, Linemap::predeclared_location()),
+      type_(type)
+  {}
+
+ protected:
+  Type*
+  do_type()
+  { return Type::make_pointer_type(Type::make_void_type()); }
+
+  bool
+  do_is_immutable() const
+  { return true; }
+
+  void
+  do_determine_type(const Type_context*)
+  { }
+
+  Expression*
+  do_copy()
+  { return this; }
+
+  Bexpression*
+  do_get_backend(Translate_context* context)
+  { return this->type_->gc_symbol_pointer(context->gogo()); }
+
+  void
+  do_dump_expression(Ast_dump_context*) const;
+
+ private:
+  // The type which this gc symbol describes.
+  Type* type_;
+};
+
+// Dump ast representation for a gc symbol expression.
+
+void
+GC_symbol_expression::do_dump_expression(
+    Ast_dump_context* ast_dump_context) const
+{
+  ast_dump_context->ostream() << "gcdata(";
+  ast_dump_context->dump_type(this->type_);
+  ast_dump_context->ostream() << ")";
+}
+
+// Make a gc symbol expression.
+
+Expression*
+Expression::make_gc_symbol(Type* type)
+{
+  return new GC_symbol_expression(type);
+}
+
 // An expression which evaluates to some characteristic of a type.
 // This is only used to initialize fields of a type descriptor.  Using
 // a new expression class is slightly inefficient but gives us a good
Index: libgo/runtime/go-unsafe-pointer.c
===================================================================
--- libgo/runtime/go-unsafe-pointer.c	(revision 214893)
+++ libgo/runtime/go-unsafe-pointer.c	(revision 214894)
@@ -8,6 +8,7 @@
 
 #include "runtime.h"
 #include "go-type.h"
+#include "mgc0.h"
 
 /* A pointer with a zero value.  */
 static void *zero_pointer;
@@ -20,6 +21,9 @@ static void *zero_pointer;
 extern const struct __go_type_descriptor unsafe_Pointer
   __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer");
 
+extern const uintptr unsafe_Pointer_gc[]
+  __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer$gc");
+
 /* Used to determine the field alignment.  */
 struct field_align
 {
@@ -35,6 +39,8 @@ static const String reflection_string =
   sizeof REFLECTION - 1
 };
 
+const uintptr unsafe_Pointer_gc[] = {8, GC_APTR, 0, GC_END};
+
 const struct __go_type_descriptor unsafe_Pointer =
 {
   /* __code */
@@ -51,6 +57,8 @@ const struct __go_type_descriptor unsafe
   __go_type_hash_identity,
   /* __equalfn */
   __go_type_equal_identity,
+  /* __gc */
+  unsafe_Pointer_gc,
   /* __reflection */
   &reflection_string,
   /* __uncommon */
@@ -94,6 +102,8 @@ const struct __go_ptr_type pointer_unsaf
     __go_type_hash_identity,
     /* __equalfn */
     __go_type_equal_identity,
+    /* __gc */
+    unsafe_Pointer_gc,
     /* __reflection */
     &preflection_string,
     /* __uncommon */
Index: libgo/runtime/go-type.h
===================================================================
--- libgo/runtime/go-type.h	(revision 214893)
+++ libgo/runtime/go-type.h	(revision 214894)
@@ -59,7 +59,7 @@ struct String;
 #define GO_CODE_MASK 0x7f
 
 /* For each Go type the compiler constructs one of these structures.
-   This is used for type reflectin, interfaces, maps, and reference
+   This is used for type reflection, interfaces, maps, and reference
    counting.  */
 
 struct __go_type_descriptor
@@ -93,6 +93,9 @@ struct __go_type_descriptor
      size of this type, and returns whether the values are equal.  */
   _Bool (*__equalfn) (const void *, const void *, uintptr_t);
 
+  /* The garbage collection data. */
+  const uintptr *__gc;
+
   /* A string describing this type.  This is only used for
      debugging.  */
   const struct String *__reflection;
Index: libgo/runtime/runtime.h
===================================================================
--- libgo/runtime/runtime.h	(revision 214893)
+++ libgo/runtime/runtime.h	(revision 214894)
@@ -800,7 +800,7 @@ uintptr	runtime_memlimit(void);
 
 enum
 {
-	UseSpanType = 0,
+	UseSpanType = 1,
 };
 
 #define runtime_setitimer setitimer
Index: libgo/runtime/mgc0.c
===================================================================
--- libgo/runtime/mgc0.c	(revision 214893)
+++ libgo/runtime/mgc0.c	(revision 214894)
@@ -181,7 +181,7 @@ struct Finalizer
 	FuncVal *fn;
 	void *arg;
 	const struct __go_func_type *ft;
-	const struct __go_ptr_type *ot;
+	const PtrType *ot;
 };
 
 typedef struct FinBlock FinBlock;
@@ -403,8 +403,6 @@ struct BufferList
 };
 static BufferList bufferList[MaxGcproc];
 
-static Type *itabtype;
-
 static void enqueue(Obj obj, Workbuf **_wbuf, Obj **_wp, uintptr *_nobj);
 
 // flushptrbuf moves data from the PtrTarget buffer to the work buffer.
@@ -649,23 +647,22 @@ flushobjbuf(Scanbuf *sbuf)
 // Program that scans the whole block and treats every block element as a potential pointer
 static uintptr defaultProg[2] = {PtrSize, GC_DEFAULT_PTR};
 
-#if 0
 // Hchan program
 static uintptr chanProg[2] = {0, GC_CHAN};
-#endif
 
 // Local variables of a program fragment or loop
 typedef struct Frame Frame;
 struct Frame {
 	uintptr count, elemsize, b;
-	uintptr *loop_or_ret;
+	const uintptr *loop_or_ret;
 };
 
 // Sanity check for the derived type info objti.
 static void
 checkptr(void *obj, uintptr objti)
 {
-	uintptr type, tisize, i, x;
+	uintptr *pc1, type, tisize, i, j, x;
+	const uintptr *pc2;
 	byte *objstart;
 	Type *t;
 	MSpan *s;
@@ -703,9 +700,8 @@ checkptr(void *obj, uintptr objti)
 		(runtime_strcmp((const char *)t->string->str, (const char*)"unsafe.Pointer") &&
 		// Runtime and gc think differently about closures.
 		 runtime_strstr((const char *)t->string->str, (const char*)"struct { F uintptr") != (const char *)t->string->str)) {
-#if 0
 		pc1 = (uintptr*)objti;
-		pc2 = (uintptr*)t->gc;
+		pc2 = (const uintptr*)t->__gc;
 		// A simple best-effort check until first GC_END.
 		for(j = 1; pc1[j] != GC_END && pc2[j] != GC_END; j++) {
 			if(pc1[j] != pc2[j]) {
@@ -714,7 +710,6 @@ checkptr(void *obj, uintptr objti)
 				runtime_throw("invalid gc type info");
 			}
 		}
-#endif
 	}
 }					
 
@@ -728,11 +723,10 @@ static void
 scanblock(Workbuf *wbuf, bool keepworking)
 {
 	byte *b, *arena_start, *arena_used;
-	uintptr n, i, end_b, elemsize, size, ti, objti, count, /* type, */ nobj;
-	uintptr *pc, precise_type, nominal_size;
-#if 0
-	uintptr *chan_ret, chancap;
-#endif
+	uintptr n, i, end_b, elemsize, size, ti, objti, count, type, nobj;
+	uintptr precise_type, nominal_size;
+	const uintptr *pc, *chan_ret;
+	uintptr chancap;
 	void *obj;
 	const Type *t, *et;
 	Slice *sliceptr;
@@ -742,10 +736,8 @@ scanblock(Workbuf *wbuf, bool keepworkin
 	Scanbuf sbuf;
 	Eface *eface;
 	Iface *iface;
-#if 0
 	Hchan *chan;
-	ChanType *chantype;
-#endif
+	const ChanType *chantype;
 	Obj *wp;
 
 	if(sizeof(Workbuf) % WorkbufSize != 0)
@@ -782,11 +774,9 @@ scanblock(Workbuf *wbuf, bool keepworkin
 	sbuf.nobj = nobj;
 
 	// (Silence the compiler)
-#if 0
 	chan = nil;
 	chantype = nil;
 	chan_ret = nil;
-#endif
 
 	goto next_block;
 
@@ -800,7 +790,7 @@ scanblock(Workbuf *wbuf, bool keepworkin
 			runtime_xadd64(&gcstats.obj.cnt, 1);
 		}
 
-		if(ti != 0 && false) {
+		if(ti != 0) {
 			if(Debug > 1) {
 				runtime_printf("scanblock %p %D ti %p\n", b, (int64)n, ti);
 			}
@@ -826,11 +816,10 @@ scanblock(Workbuf *wbuf, bool keepworkin
 					runtime_throw("invalid gc type info");
 				}
 			}
-		} else if(UseSpanType && false) {
+		} else if(UseSpanType) {
 			if(CollectStats)
 				runtime_xadd64(&gcstats.obj.notype, 1);
 
-#if 0
 			type = runtime_gettype(b);
 			if(type != 0) {
 				if(CollectStats)
@@ -839,13 +828,13 @@ scanblock(Workbuf *wbuf, bool keepworkin
 				t = (Type*)(type & ~(uintptr)(PtrSize-1));
 				switch(type & (PtrSize-1)) {
 				case TypeInfo_SingleObject:
-					pc = (uintptr*)t->gc;
+					pc = (const uintptr*)t->__gc;
 					precise_type = true;  // type information about 'b' is precise
 					stack_top.count = 1;
 					stack_top.elemsize = pc[0];
 					break;
 				case TypeInfo_Array:
-					pc = (uintptr*)t->gc;
+					pc = (const uintptr*)t->__gc;
 					if(pc[0] == 0)
 						goto next_block;
 					precise_type = true;  // type information about 'b' is precise
@@ -855,7 +844,7 @@ scanblock(Workbuf *wbuf, bool keepworkin
 					break;
 				case TypeInfo_Chan:
 					chan = (Hchan*)b;
-					chantype = (ChanType*)t;
+					chantype = (const ChanType*)t;
 					chan_ret = nil;
 					pc = chanProg;
 					break;
@@ -872,7 +861,6 @@ scanblock(Workbuf *wbuf, bool keepworkin
 				if(Debug > 1)
 					runtime_printf("scanblock %p %D unknown type\n", b, (int64)n);
 			}
-#endif
 		} else {
 			pc = defaultProg;
 			if(Debug > 1)
@@ -954,7 +942,7 @@ scanblock(Workbuf *wbuf, bool keepworkin
 
 			// eface->__object
 			if((byte*)eface->__object >= arena_start && (byte*)eface->__object < arena_used) {
-				if(t->__size <= sizeof(void*)) {
+				if(__go_is_pointer_type(t)) {
 					if((t->__code & KindNoPointers))
 						continue;
 
@@ -965,13 +953,11 @@ scanblock(Workbuf *wbuf, bool keepworkin
 						// dgcsym1 in case TPTR32/case TPTR64. See rationale there.
 						et = ((const PtrType*)t)->elem;
 						if(!(et->__code & KindNoPointers))
-							// objti = (uintptr)((const PtrType*)t)->elem->gc;
-							objti = 0;
+							objti = (uintptr)((const PtrType*)t)->elem->__gc;
 					}
 				} else {
 					obj = eface->__object;
-					// objti = (uintptr)t->gc;
-					objti = 0;
+					objti = (uintptr)t->__gc;
 				}
 			}
 			break;
@@ -986,16 +972,15 @@ scanblock(Workbuf *wbuf, bool keepworkin
 			
 			// iface->tab
 			if((byte*)iface->tab >= arena_start && (byte*)iface->tab < arena_used) {
-				*sbuf.ptr.pos++ = (PtrTarget){iface->tab, /* (uintptr)itabtype->gc */ 0};
+				*sbuf.ptr.pos++ = (PtrTarget){iface->tab, 0};
 				if(sbuf.ptr.pos == sbuf.ptr.end)
 					flushptrbuf(&sbuf);
 			}
 
 			// iface->data
 			if((byte*)iface->__object >= arena_start && (byte*)iface->__object < arena_used) {
-				// t = iface->tab->type;
-				t = nil;
-				if(t->__size <= sizeof(void*)) {
+				t = (const Type*)iface->tab[0];
+				if(__go_is_pointer_type(t)) {
 					if((t->__code & KindNoPointers))
 						continue;
 
@@ -1006,13 +991,11 @@ scanblock(Workbuf *wbuf, bool keepworkin
 						// dgcsym1 in case TPTR32/case TPTR64. See rationale there.
 						et = ((const PtrType*)t)->elem;
 						if(!(et->__code & KindNoPointers))
-							// objti = (uintptr)((const PtrType*)t)->elem->gc;
-							objti = 0;
+							objti = (uintptr)((const PtrType*)t)->elem->__gc;
 					}
 				} else {
 					obj = iface->__object;
-					// objti = (uintptr)t->gc;
-					objti = 0;
+					objti = (uintptr)t->__gc;
 				}
 			}
 			break;
@@ -1092,7 +1075,7 @@ scanblock(Workbuf *wbuf, bool keepworkin
 			// Stack push.
 			*stack_ptr-- = stack_top;
 			stack_top = (Frame){1, 0, stack_top.b + pc[1], pc+3 /*return address*/};
-			pc = (uintptr*)((byte*)pc + *(int32*)(pc+2));  // target of the CALL instruction
+			pc = (const uintptr*)((const byte*)pc + *(const int32*)(pc+2));  // target of the CALL instruction
 			continue;
 
 		case GC_REGION:
@@ -1108,7 +1091,6 @@ scanblock(Workbuf *wbuf, bool keepworkin
 				flushobjbuf(&sbuf);
 			continue;
 
-#if 0
 		case GC_CHAN_PTR:
 			chan = *(Hchan**)(stack_top.b + pc[1]);
 			if(Debug > 2 && chan != nil)
@@ -1141,8 +1123,8 @@ scanblock(Workbuf *wbuf, bool keepworkin
 					// in-use part of the circular buffer is scanned.
 					// (Channel routines zero the unused part, so the current
 					// code does not lead to leaks, it's just a little inefficient.)
-					*sbuf.obj.pos++ = (Obj){(byte*)chan+runtime_Hchansize, chancap*chantype->elem->size,
-						(uintptr)chantype->elem->gc | PRECISE | LOOP};
+					*sbuf.obj.pos++ = (Obj){(byte*)chan+runtime_Hchansize, chancap*chantype->elem->__size,
+						(uintptr)chantype->elem->__gc | PRECISE | LOOP};
 					if(sbuf.obj.pos == sbuf.obj.end)
 						flushobjbuf(&sbuf);
 				}
@@ -1151,7 +1133,6 @@ scanblock(Workbuf *wbuf, bool keepworkin
 				goto next_block;
 			pc = chan_ret;
 			continue;
-#endif
 
 		default:
 			runtime_printf("runtime: invalid GC instruction %p at %p\n", pc[0], pc);
@@ -1828,7 +1809,7 @@ runtime_MSpan_Sweep(MSpan *s)
 }
 
 // State of background sweep.
-// Pretected by gclock.
+// Protected by gclock.
 static struct
 {
 	G*	g;
@@ -2260,12 +2241,6 @@ gc(struct gc_args *args)
 		work.markfor = runtime_parforalloc(MaxGcproc);
 	m->locks--;
 
-	if(itabtype == nil) {
-		// get C pointer to the Go type "itab"
-		// runtime_gc_itab_ptr(&eface);
-		// itabtype = ((PtrType*)eface.__type_descriptor)->elem;
-	}
-
 	t1 = 0;
 	if(runtime_debug.gctrace)
 		t1 = runtime_nanotime();
Index: libgo/go/runtime/type.go
===================================================================
--- libgo/go/runtime/type.go	(revision 214893)
+++ libgo/go/runtime/type.go	(revision 214894)
@@ -15,7 +15,7 @@ package runtime
 import "unsafe"
 
 type rtype struct {
-	Kind       uint8
+	kind       uint8
 	align      uint8
 	fieldAlign uint8
 	size       uintptr
@@ -24,6 +24,7 @@ type rtype struct {
 	hashfn  func(unsafe.Pointer, uintptr) uintptr
 	equalfn func(unsafe.Pointer, unsafe.Pointer, uintptr) bool
 
+	gc     unsafe.Pointer
 	string *string
 	*uncommonType
 	ptrToThis *rtype
Index: libgo/go/reflect/type.go
===================================================================
--- libgo/go/reflect/type.go	(revision 214893)
+++ libgo/go/reflect/type.go	(revision 214894)
@@ -255,6 +255,7 @@ type rtype struct {
 	hashfn  uintptr // hash function code
 	equalfn uintptr // equality function code
 
+	gc            unsafe.Pointer // garbage collection data
 	string        *string        // string form; unnecessary  but undeniably useful
 	*uncommonType                // (relatively) uncommon fields
 	ptrToThis     *rtype         // type for pointer to this type, if used in binary or has methods
@@ -1130,6 +1131,18 @@ func (t *rtype) ptrTo() *rtype {
 	p.zero = unsafe.Pointer(&make([]byte, p.size)[0])
 	p.elem = t
 
+	if t.kind&kindNoPointers != 0 {
+		p.gc = unsafe.Pointer(&ptrDataGCProg)
+	} else {
+		p.gc = unsafe.Pointer(&ptrGC{
+			width:  p.size,
+			op:     _GC_PTR,
+			off:    0,
+			elemgc: t.gc,
+			end:    _GC_END,
+		})
+	}
+
 	q := canonicalize(&p.rtype)
 	p = (*ptrType)(unsafe.Pointer(q.(*rtype)))
 
@@ -1471,8 +1484,16 @@ func ChanOf(dir ChanDir, t Type) Type {
 	ch.ptrToThis = nil
 	ch.zero = unsafe.Pointer(&make([]byte, ch.size)[0])
 
+	ch.gc = unsafe.Pointer(&chanGC{
+		width: ch.size,
+		op:    _GC_CHAN_PTR,
+		off:   0,
+		typ:   &ch.rtype,
+		end:   _GC_END,
+	})
+
 	// INCORRECT. Uncomment to check that TestChanOfGC fails when ch.gc is wrong.
-	//ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END})
+	// ch.gc = unsafe.Pointer(&badGC{width: ch.size, end: _GC_END})
 
 	return cachePut(ckey, &ch.rtype)
 }
@@ -1524,10 +1545,13 @@ func MapOf(key, elem Type) Type {
 	// 	width:  unsafe.Sizeof(uintptr(0)),
 	// 	op:     _GC_PTR,
 	// 	off:    0,
-	// 	elemgc: mt.hmap.gc,
+	// 	elemgc: nil,
 	// 	end:    _GC_END,
 	// })
 
+	// TODO(cmang): Generate GC data for Map elements.
+	mt.gc = unsafe.Pointer(&ptrDataGCProg)
+
 	// INCORRECT. Uncomment to check that TestMapOfGC and TestMapOfGCValues
 	// fail when mt.gc is wrong.
 	//mt.gc = unsafe.Pointer(&badGC{width: mt.size, end: _GC_END})
@@ -1593,8 +1617,7 @@ func bucketOf(ktyp, etyp *rtype) *rtype
 
 // Take the GC program for "t" and append it to the GC program "gc".
 func appendGCProgram(gc []uintptr, t *rtype) []uintptr {
-	// p := t.gc
-	var p unsafe.Pointer
+	p := t.gc
 	p = unsafe.Pointer(uintptr(p) + unsafe.Sizeof(uintptr(0))) // skip size
 loop:
 	for {
@@ -1707,8 +1730,20 @@ func SliceOf(t Type) Type {
 	slice.ptrToThis = nil
 	slice.zero = unsafe.Pointer(&make([]byte, slice.size)[0])
 
+	if typ.size == 0 {
+		slice.gc = unsafe.Pointer(&sliceEmptyGCProg)
+	} else {
+		slice.gc = unsafe.Pointer(&sliceGC{
+			width:  slice.size,
+			op:     _GC_SLICE,
+			off:    0,
+			elemgc: typ.gc,
+			end:    _GC_END,
+		})
+	}
+
 	// INCORRECT. Uncomment to check that TestSliceOfOfGC fails when slice.gc is wrong.
-	//slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END})
+	// slice.gc = unsafe.Pointer(&badGC{width: slice.size, end: _GC_END})
 
 	return cachePut(ckey, &slice.rtype)
 }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]