From f1d2ac4f84b4b5354779b3b0a0e125b8302ebb49 Mon Sep 17 00:00:00 2001
From: Chris Manghane <cmang@google.com>
Date: Wed, 3 Sep 2014 22:56:09 +0000
Subject: compiler: Add precise type information on the heap.

	* 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.

From-SVN: r214894
---
 gcc/go/gofrontend/backend.h      |  55 ++++-
 gcc/go/gofrontend/expressions.cc |  88 +++++++-
 gcc/go/gofrontend/expressions.h  |  10 +
 gcc/go/gofrontend/gogo.cc        |  12 +-
 gcc/go/gofrontend/types.cc       | 470 ++++++++++++++++++++++++++++++++++++++-
 gcc/go/gofrontend/types.h        | 109 +++++++++
 6 files changed, 723 insertions(+), 21 deletions(-)

(limited to 'gcc/go/gofrontend')

diff --git a/gcc/go/gofrontend/backend.h b/gcc/go/gofrontend/backend.h
index 323ac2e..98c36c1 100644
--- a/gcc/go/gofrontend/backend.h
+++ b/gcc/go/gofrontend/backend.h
@@ -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
diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index 6414136..df1650a1 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -3440,6 +3440,9 @@ class Unsafe_type_conversion_expression : public 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_traverse(Traverse* traverse)
   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(Translate_context* context)
 			      && !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* type, Location location)
   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
diff --git a/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index 0ce6f22..77153db 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -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 Expression
   { 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
diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc
index 654b6c3..dcc2ae6 100644
--- a/gcc/go/gofrontend/gogo.cc
+++ b/gcc/go/gofrontend/gogo.cc
@@ -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, std::vector<Bexpression*>& const_decls,
           {
             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;
diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc
index 395b5e5..302faee 100644
--- a/gcc/go/gofrontend/types.cc
+++ b/gcc/go/gofrontend/types.cc
@@ -36,7 +36,8 @@ get_backend_interface_fields(Gogo* gogo, Interface_type* type,
 // 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* gogo)
 	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, Type* type, Named_type* name)
   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* gogo, int runtime_type_kind,
   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* gogo, int runtime_type_kind,
   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* gogo, int runtime_type_kind,
   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* gogo, Named_type* name)
     }
 }
 
+// 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::string* ret) const
   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, std::string* ret) const
     }
 }
 
+// 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, std::string* ret) const
   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 Type
   { 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, std::string* ret) const
   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, std::string* ret) const
   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::string* ret) const
   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, std::string* ret) const
   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, std::string* ret) const
   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, std::string* ret) const
   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
diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h
index 6fa6513..447861c 100644
--- a/gcc/go/gofrontend/types.h
+++ b/gcc/go/gofrontend/types.h
@@ -83,6 +83,28 @@ static const int RUNTIME_TYPE_KIND_UNSAFE_POINTER = 26;
 
 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 Type
   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
-- 
cgit v1.1