diff gcc/go/gofrontend/export.cc @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
line wrap: on
line diff
--- a/gcc/go/gofrontend/export.cc	Thu Oct 25 07:37:49 2018 +0900
+++ b/gcc/go/gofrontend/export.cc	Thu Feb 13 11:34:05 2020 +0900
@@ -6,14 +6,14 @@
 
 #include "go-system.h"
 
+#include "go-c.h"
+#include "go-diagnostics.h"
 #include "go-sha1.h"
-#include "go-c.h"
-
 #include "gogo.h"
 #include "types.h"
+#include "expressions.h"
 #include "statements.h"
 #include "export.h"
-
 #include "go-linemap.h"
 #include "backend.h"
 
@@ -41,14 +41,6 @@
 
 const int Export::checksum_len;
 
-// Constructor.
-
-Export::Export(Stream* stream)
-  : stream_(stream), type_index_(1), packages_()
-{
-  go_assert(Export::checksum_len == Go_sha1_helper::checksum_len);
-}
-
 // Type hash table operations, treating aliases as distinct.
 
 class Type_hash_alias_identical
@@ -60,6 +52,7 @@
     return type->hash_for_method(NULL,
 				 (Type::COMPARE_ERRORS
 				  | Type::COMPARE_TAGS
+				  | Type::COMPARE_EMBEDDED_INTERFACES
 				  | Type::COMPARE_ALIASES));
   }
 };
@@ -73,28 +66,382 @@
     return Type::are_identical(t1, t2,
 			       (Type::COMPARE_ERRORS
 				| Type::COMPARE_TAGS
+                                | Type::COMPARE_EMBEDDED_INTERFACES
 				| Type::COMPARE_ALIASES),
 			       NULL);
   }
 };
 
-// Mapping from Type objects to a constant index.  This would be nicer
-// as a field in Export, but then export.h would have to #include
-// types.h.
+// Mapping from Type objects to a constant index.
+typedef Unordered_map_hash(const Type*, int, Type_hash_alias_identical,
+                           Type_alias_identical) Type_refs;
+
+// Implementation object for class Export.  Hidden implementation avoids
+// having to #include types.h in export.h, or use a static map.
+
+struct Export_impl {
+  Type_refs type_refs;
+};
+
+// Constructor.
+
+Export::Export(Stream* stream)
+    : stream_(stream), type_index_(1), packages_(), impl_(new Export_impl)
+{
+  go_assert(Export::checksum_len == Go_sha1_helper::checksum_len);
+}
+
+// Destructor.
+
+Export::~Export()
+{
+  delete this->impl_;
+}
+
+// A traversal class to collect functions and global variables
+// referenced by inlined functions, and also to gather up
+// referenced types that need to be included in the exports.
+
+class Collect_export_references : public Traverse
+{
+ public:
+  Collect_export_references(Export* exp,
+                            Unordered_set(Named_object*)* exports,
+                            Unordered_set(const Package*)* imports)
+    : Traverse(traverse_expressions
+               | traverse_types),
+      exp_(exp), exports_(exports), imports_(imports),
+      inline_fcn_worklist_(NULL), exports_finalized_(false)
+  { }
+
+  // Initial entry point; performs a walk to expand the exports set.
+  void
+  expand_exports(std::vector<Named_object*>* inlinable_functions);
+
+  // Second entry point (called after the method above), to find
+  // all types referenced by exports.
+  void
+  prepare_types(const std::vector<Named_object*>& sorted_exports);
+
+ protected:
+  // Override of parent class method.
+  int
+  expression(Expression**);
+
+  // Override of parent class method.
+  int
+  type(Type* type);
+
+  // Traverse the components of a function type.
+  void
+  traverse_function_type(Function_type*);
+
+  // Traverse the methods of a named type, and register its package.
+  void
+  traverse_named_type(Named_type*);
+
+ private:
+
+  // Add a named object to the exports set (during expand_exports()).
+  // Returns TRUE if a new object was added to the exports set,
+  // FALSE otherwise.
+  bool
+  add_to_exports(Named_object*);
+
+  // The exporter.
+  Export* exp_;
+  // The set of named objects to export.
+  Unordered_set(Named_object*)* exports_;
+  // Set containing all directly and indirectly imported packages.
+  Unordered_set(const Package*)* imports_;
+  // Functions we've already traversed and don't need to visit again.
+  Unordered_set(Named_object*) checked_functions_;
+  // Worklist of functions we are exporting with inline bodies that need
+  // to be checked.
+  std::vector<Named_object*>* inline_fcn_worklist_;
+  // Set to true if expand_exports() has been called and is complete.
+  bool exports_finalized_;
+};
 
-typedef Unordered_map_hash(const Type*, int, Type_hash_alias_identical,
-			   Type_alias_identical) Type_refs;
+void
+Collect_export_references::expand_exports(std::vector<Named_object*>* fcns)
+{
+  this->inline_fcn_worklist_ = fcns;
+  while (!this->inline_fcn_worklist_->empty())
+    {
+      Named_object* no = this->inline_fcn_worklist_->back();
+      this->inline_fcn_worklist_->pop_back();
+      std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
+	this->checked_functions_.insert(no);
+      if (ins.second)
+	{
+	  // This traversal may add new objects to this->exports_ and new
+	  // functions to this->inline_fcn_worklist_.
+	  no->func_value()->block()->traverse(this);
+	}
+    }
+  this->inline_fcn_worklist_ = NULL;
+  this->exports_finalized_ = true;
+}
+
+bool
+Collect_export_references::add_to_exports(Named_object* no)
+{
+  std::pair<Unordered_set(Named_object*)::iterator, bool> ins =
+      this->exports_->insert(no);
+  // If the export list has been finalized, then we should not be
+  // adding anything new to the exports set.
+  go_assert(!this->exports_finalized_ || !ins.second);
+  return ins.second;
+}
+
+int
+Collect_export_references::expression(Expression** pexpr)
+{
+  const Expression* expr = *pexpr;
+
+  const Var_expression* ve = expr->var_expression();
+  if (ve != NULL)
+    {
+      Named_object* no = ve->named_object();
+      if (no->is_variable() && no->var_value()->is_global())
+	{
+          const Package* var_package = no->package();
+          if (var_package != NULL)
+            this->imports_->insert(var_package);
 
-static Type_refs type_refs;
+	  this->add_to_exports(no);
+	  no->var_value()->set_is_referenced_by_inline();
+	}
+      return TRAVERSE_CONTINUE;
+    }
+
+  const Func_expression* fe = expr->func_expression();
+  if (fe != NULL)
+    {
+      Named_object* no = fe->named_object();
+
+      const Package* func_package = fe->named_object()->package();
+      if (func_package != NULL)
+        this->imports_->insert(func_package);
+
+      if (no->is_function_declaration()
+	  && no->func_declaration_value()->type()->is_builtin())
+	return TRAVERSE_CONTINUE;
+
+      if (this->inline_fcn_worklist_ != NULL)
+        {
+          bool added = this->add_to_exports(no);
+
+          if (no->is_function())
+            no->func_value()->set_is_referenced_by_inline();
+
+          // If 'added' is false then this object was already in
+          // exports_, in which case it was already added to
+          // check_inline_refs_ the first time we added it to exports_, so
+          // we don't need to add it again.
+          if (added
+              && no->is_function()
+              && no->func_value()->export_for_inlining())
+            this->inline_fcn_worklist_->push_back(no);
+        }
+
+      return TRAVERSE_CONTINUE;
+    }
+
+  const Named_object* nco = expr->named_constant();
+  if (nco != 0)
+    {
+      const Named_constant *nc = nco->const_value();
+      Type::traverse(nc->type(), this);
+      return TRAVERSE_CONTINUE;
+    }
 
-// A functor to sort Named_object pointers by name.
+  return TRAVERSE_CONTINUE;
+}
+
+// Collect up the set of types mentioned in things we're exporting, and collect
+// all the packages encountered during type traversal, to make sure we can
+// declare things referered to indirectly (for example, in the body of an
+// exported inline function from another package).
+
+void
+Collect_export_references::prepare_types(const std::vector<Named_object*>& sorted_exports)
+{
+  // Iterate through the exported objects and traverse any types encountered.
+  for (std::vector<Named_object*>::const_iterator p = sorted_exports.begin();
+       p != sorted_exports.end();
+       ++p)
+    {
+      Named_object* no = *p;
+      switch (no->classification())
+	{
+	case Named_object::NAMED_OBJECT_CONST:
+	  {
+	    Type* t = no->const_value()->type();
+	    if (t != NULL && !t->is_abstract())
+	      Type::traverse(t, this);
+	  }
+	  break;
 
-struct Sort_bindings
+	case Named_object::NAMED_OBJECT_TYPE:
+	  Type::traverse(no->type_value()->real_type(), this);
+	  this->traverse_named_type(no->type_value());
+	  break;
+
+	case Named_object::NAMED_OBJECT_VAR:
+	  Type::traverse(no->var_value()->type(), this);
+	  break;
+
+	case Named_object::NAMED_OBJECT_FUNC:
+	  {
+	    Function* fn = no->func_value();
+	    this->traverse_function_type(fn->type());
+	    if (fn->export_for_inlining())
+	      fn->block()->traverse(this);
+	  }
+	  break;
+
+	case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
+	  this->traverse_function_type(no->func_declaration_value()->type());
+	  break;
+
+	default:
+	  // We shouldn't see anything else.  If we do we'll give an
+	  // error later when we try to actually export it.
+	  break;
+	}
+    }
+}
+
+// Record referenced type, record package imports, and make sure we traverse
+// methods of named types.
+
+int
+Collect_export_references::type(Type* type)
 {
-  bool
-  operator()(const Named_object* n1, const Named_object* n2) const
-  { return n1->name() < n2->name(); }
-};
+  // Skip forwarders; don't try to give them a type index.
+  if (type->forward_declaration_type() != NULL)
+    return TRAVERSE_CONTINUE;
+
+  // Skip the void type, which we'll see when exporting
+  // unsafe.Pointer.  The void type is not itself exported, because
+  // Pointer_type::do_export checks for it.
+  if (type->is_void_type())
+    return TRAVERSE_SKIP_COMPONENTS;
+
+  // Skip the nil type, turns up in function bodies.
+  if (type->is_nil_type())
+    return TRAVERSE_SKIP_COMPONENTS;
+
+  // Skip abstract types.  We should never see these in real code,
+  // only in things like const declarations.
+  if (type->is_abstract())
+    return TRAVERSE_SKIP_COMPONENTS;
+
+  // For interfaces make sure that embedded methods are sorted, since the
+  // comparison function we use for indexing types relies on it (this call has
+  // to happen before the record_type call below).
+  if (type->classification() == Type::TYPE_INTERFACE)
+    {
+      Interface_type* it = type->interface_type();
+      if (it != NULL)
+        it->sort_embedded();
+    }
+
+  if (!this->exp_->record_type(type))
+    {
+      // We've already seen this type.
+      return TRAVERSE_SKIP_COMPONENTS;
+    }
+
+  // At this stage of compilation traversing interface types traverses
+  // the final list of methods, but we export the locally defined
+  // methods.  If there is an embedded interface type we need to make
+  // sure to export that.  Check classification, rather than calling
+  // the interface_type method, because we want to handle named types
+  // below.
+  if (type->classification() == Type::TYPE_INTERFACE)
+    {
+      Interface_type* it = type->interface_type();
+      const Typed_identifier_list* methods = it->local_methods();
+      if (methods != NULL)
+	{
+	  for (Typed_identifier_list::const_iterator p = methods->begin();
+	       p != methods->end();
+	       ++p)
+	    {
+	      if (p->name().empty())
+		Type::traverse(p->type(), this);
+	      else
+		this->traverse_function_type(p->type()->function_type());
+	    }
+	}
+      return TRAVERSE_SKIP_COMPONENTS;
+    }
+
+  Named_type* nt = type->named_type();
+  if (nt != NULL)
+    this->traverse_named_type(nt);
+
+  return TRAVERSE_CONTINUE;
+}
+
+void
+Collect_export_references::traverse_named_type(Named_type* nt)
+{
+  const Package* package = nt->named_object()->package();
+  if (package != NULL)
+    this->imports_->insert(package);
+
+  // We have to traverse the methods of named types, because we are
+  // going to export them.  This is not done by ordinary type
+  // traversal.
+  const Bindings* methods = nt->local_methods();
+  if (methods != NULL)
+    {
+      for (Bindings::const_definitions_iterator pm =
+	     methods->begin_definitions();
+	   pm != methods->end_definitions();
+	   ++pm)
+	{
+	  Function* fn = (*pm)->func_value();
+	  this->traverse_function_type(fn->type());
+	  if (fn->export_for_inlining())
+	    fn->block()->traverse(this);
+	}
+
+      for (Bindings::const_declarations_iterator pm =
+	     methods->begin_declarations();
+	   pm != methods->end_declarations();
+	   ++pm)
+	{
+	  Named_object* mno = pm->second;
+	  if (mno->is_function_declaration())
+	    this->traverse_function_type(mno->func_declaration_value()->type());
+	}
+    }
+}
+
+// Traverse the types in a function type.  We don't need the function
+// type itself, just the receiver, parameter, and result types.
+
+void
+Collect_export_references::traverse_function_type(Function_type* type)
+{
+  go_assert(type != NULL);
+  if (this->remember_type(type))
+    return;
+  const Typed_identifier* receiver = type->receiver();
+  if (receiver != NULL)
+    Type::traverse(receiver->type(), this);
+  const Typed_identifier_list* parameters = type->parameters();
+  if (parameters != NULL)
+    parameters->traverse(this);
+  const Typed_identifier_list* results = type->results();
+  if (results != NULL)
+    results->traverse(this);
+}
 
 // Return true if we should export NO.
 
@@ -133,6 +480,54 @@
   return true;
 }
 
+// A functor to sort Named_object pointers by name.
+
+struct Sort_bindings
+{
+  bool
+  operator()(const Named_object* n1, const Named_object* n2) const
+  {
+    if (n1->package() != n2->package())
+      {
+	if (n1->package() == NULL)
+	  return true;
+	if (n2->package() == NULL)
+	  return false;
+	return n1->package()->pkgpath() < n2->package()->pkgpath();
+      }
+
+    return n1->name() < n2->name();
+  }
+};
+
+// A functor to sort types for export.
+
+struct Sort_types
+{
+  bool
+  operator()(const Type* t1, const Type* t2) const
+  {
+    const Named_type* nt1 = t1->named_type();
+    const Named_type* nt2 = t2->named_type();
+    if (nt1 != NULL)
+      {
+        if (nt2 != NULL)
+          {
+            Sort_bindings sb;
+            return sb(nt1->named_object(), nt2->named_object());
+          }
+        else
+          return true;
+      }
+    else if (nt2 != NULL)
+      return false;
+    if (t1->classification() != t2->classification())
+      return t1->classification() < t2->classification();
+    Gogo* gogo = go_get_gogo();
+    return gogo->type_descriptor_name(t1, NULL).compare(gogo->type_descriptor_name(t2, NULL)) < 0;
+  }
+};
+
 // Export those identifiers marked for exporting.
 
 void
@@ -143,7 +538,8 @@
 		       const std::map<std::string, Package*>& imports,
 		       const std::string& import_init_fn,
                        const Import_init_set& imported_init_fns,
-		       const Bindings* bindings)
+		       const Bindings* bindings,
+                       Unordered_set(Named_object*)* functions_marked_inline)
 {
   // If there have been any errors so far, don't try to export
   // anything.  That way the export code doesn't have to worry about
@@ -151,17 +547,28 @@
   if (saw_errors())
     return;
 
-  // Export the symbols in sorted order.  That will reduce cases where
-  // irrelevant changes to the source code affect the exported
-  // interface.
-  std::vector<Named_object*> exports;
-  exports.reserve(bindings->size_definitions());
+  // EXPORTS is the set of objects to export.  CHECK_INLINE_REFS is a
+  // list of exported function with inline bodies that need to be
+  // checked for references to other objects.  Every function on
+  // CHECK_INLINE_REFS is also on EXPORTS.
+  Unordered_set(Named_object*) exports;
+  std::vector<Named_object*> check_inline_refs;
+  check_inline_refs.reserve(functions_marked_inline->size());
+
+  // Add all functions/methods from the "marked inlined" set to the
+  // CHECK_INLINE_REFS worklist.
+  for (Unordered_set(Named_object*)::const_iterator p = functions_marked_inline->begin();
+       p != functions_marked_inline->end();
+       ++p)
+      check_inline_refs.push_back(*p);
 
   for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
        p != bindings->end_definitions();
        ++p)
-    if (should_export(*p))
-      exports.push_back(*p);
+    {
+      if (should_export(*p))
+        exports.insert(*p);
+    }
 
   for (Bindings::const_declarations_iterator p =
 	 bindings->begin_declarations();
@@ -172,15 +579,45 @@
       // supporting C code.  We do not export type declarations.
       if (p->second->is_function_declaration()
 	  && should_export(p->second))
-	exports.push_back(p->second);
+	exports.insert(p->second);
     }
 
-  std::sort(exports.begin(), exports.end(), Sort_bindings());
+  // Track all imported packages mentioned in export data.
+  Unordered_set(const Package*) all_imports;
+
+  Collect_export_references collect(this, &exports, &all_imports);
+
+  // Walk the set of inlinable routine bodies collected above. This
+  // can potentially expand the exports set.
+  collect.expand_exports(&check_inline_refs);
+
+  // Export the symbols in sorted order.  That will reduce cases where
+  // irrelevant changes to the source code affect the exported
+  // interface.
+  std::vector<Named_object*> sorted_exports;
+  sorted_exports.reserve(exports.size());
+
+  for (Unordered_set(Named_object*)::const_iterator p = exports.begin();
+       p != exports.end();
+       ++p)
+    {
+      sorted_exports.push_back(*p);
+
+      const Package* pkg = (*p)->package();
+      if (pkg != NULL)
+	all_imports.insert(pkg);
+    }
+
+  std::sort(sorted_exports.begin(), sorted_exports.end(), Sort_bindings());
+
+  // Collect up the set of types mentioned in things we're exporting,
+  // and any packages that may be referred to indirectly.
+  collect.prepare_types(sorted_exports);
 
   // Assign indexes to all exported types and types referenced by
-  // exported types, and collect all packages mentioned.
-  Unordered_set(const Package*) type_imports;
-  int unexported_type_index = this->prepare_types(&exports, &type_imports);
+  // things we're exporting.  Return value is index of first non-exported
+  // type.
+  int unexported_type_index = this->assign_type_indices(sorted_exports);
 
   // Although the export data is readable, at least this version is,
   // it is conceptually a binary format.  Start with a four byte
@@ -208,7 +645,7 @@
 
   this->write_packages(packages);
 
-  this->write_imports(imports, type_imports);
+  this->write_imports(imports, all_imports);
 
   this->write_imported_init_fns(package_name, import_init_fn,
 				imported_init_fns);
@@ -221,8 +658,8 @@
   this->write_types(unexported_type_index);
 
   // Write out the non-type export data.
-  for (std::vector<Named_object*>::const_iterator p = exports.begin();
-       p != exports.end();
+  for (std::vector<Named_object*>::const_iterator p = sorted_exports.begin();
+       p != sorted_exports.end();
        ++p)
     {
       if (!(*p)->is_type())
@@ -245,233 +682,88 @@
   this->stream_->write_checksum(s);
 }
 
-// Traversal class to find referenced types.
-
-class Find_types_to_prepare : public Traverse
-{
- public:
-  Find_types_to_prepare(Export* exp,
-			Unordered_set(const Package*)* imports)
-    : Traverse(traverse_types),
-      exp_(exp), imports_(imports)
-  { }
-
-  int
-  type(Type* type);
-
-  // Traverse the components of a function type.
-  void
-  traverse_function(Function_type*);
-
-  // Traverse the methods of a named type, and register its package.
-  void
-  traverse_named_type(Named_type*);
-
- private:
-  // Exporters.
-  Export* exp_;
-  // List of packages we are building.
-  Unordered_set(const Package*)* imports_;
-};
-
-// Set type index of referenced type, record package imports, and make
-// sure we traverse methods of named types.
-
-int
-Find_types_to_prepare::type(Type* type)
-{
-  // Skip forwarders; don't try to give them a type index.
-  if (type->forward_declaration_type() != NULL)
-    return TRAVERSE_CONTINUE;
-
-  // Skip the void type, which we'll see when exporting
-  // unsafe.Pointer.  The void type is not itself exported, because
-  // Pointer_type::do_export checks for it.
-  if (type->is_void_type())
-    return TRAVERSE_SKIP_COMPONENTS;
-
-  if (!this->exp_->set_type_index(type))
-    {
-      // We've already seen this type.
-      return TRAVERSE_SKIP_COMPONENTS;
-    }
-
-  // At this stage of compilation traversing interface types traverses
-  // the final list of methods, but we export the locally defined
-  // methods.  If there is an embedded interface type we need to make
-  // sure to export that.  Check classification, rather than calling
-  // the interface_type method, because we want to handle named types
-  // below.
-  if (type->classification() == Type::TYPE_INTERFACE)
-    {
-      Interface_type* it = type->interface_type();
-      const Typed_identifier_list* methods = it->local_methods();
-      if (methods != NULL)
-	{
-	  for (Typed_identifier_list::const_iterator p = methods->begin();
-	       p != methods->end();
-	       ++p)
-	    {
-	      if (p->name().empty())
-		Type::traverse(p->type(), this);
-	      else
-		this->traverse_function(p->type()->function_type());
-	    }
-	}
-      return TRAVERSE_SKIP_COMPONENTS;
-    }
-
-  Named_type* nt = type->named_type();
-  if (nt != NULL)
-    this->traverse_named_type(nt);
-
-  return TRAVERSE_CONTINUE;
-}
-
-// Traverse the types in a function type.  We don't need the function
-// type itself, just the receiver, parameter, and result types.
-
-void
-Find_types_to_prepare::traverse_function(Function_type* type)
-{
-  go_assert(type != NULL);
-  if (this->remember_type(type))
-    return;
-  const Typed_identifier* receiver = type->receiver();
-  if (receiver != NULL)
-    Type::traverse(receiver->type(), this);
-  const Typed_identifier_list* parameters = type->parameters();
-  if (parameters != NULL)
-    parameters->traverse(this);
-  const Typed_identifier_list* results = type->results();
-  if (results != NULL)
-    results->traverse(this);
-}
-
-// Traverse the methods of a named type, and record its package.
-
-void
-Find_types_to_prepare::traverse_named_type(Named_type* nt)
-{
-  const Package* package = nt->named_object()->package();
-  if (package != NULL)
-    this->imports_->insert(package);
-
-  // We have to traverse the methods of named types, because we are
-  // going to export them.  This is not done by ordinary type
-  // traversal.
-  const Bindings* methods = nt->local_methods();
-  if (methods != NULL)
-    {
-      for (Bindings::const_definitions_iterator pm =
-	     methods->begin_definitions();
-	   pm != methods->end_definitions();
-	   ++pm)
-	this->traverse_function((*pm)->func_value()->type());
-
-      for (Bindings::const_declarations_iterator pm =
-	     methods->begin_declarations();
-	   pm != methods->end_declarations();
-	   ++pm)
-	{
-	  Named_object* mno = pm->second;
-	  if (mno->is_function_declaration())
-	    this->traverse_function(mno->func_declaration_value()->type());
-	}
-    }
-}
-
-// Prepare to export types by assigning a type index to every exported
-// type and every type referenced by an exported type.  Also collect
-// all the packages we see in types, so that if we refer to any types
-// from indirectly imported packages we can tell the importer about
-// the package.  This returns the number of exported types.
-
-int
-Export::prepare_types(const std::vector<Named_object*>* exports,
-		      Unordered_set(const Package*)* imports)
-{
-  // Assign indexes to all the exported types.
-  for (std::vector<Named_object*>::const_iterator p = exports->begin();
-       p != exports->end();
-       ++p)
-    {
-      if (!(*p)->is_type())
-	continue;
-      this->set_type_index((*p)->type_value());
-    }
-
-  int ret = this->type_index_;
-
-  // Use a single instance of the traversal class because traversal
-  // classes keep track of which types they've already seen.  That
-  // lets us avoid type reference loops.
-  Find_types_to_prepare find(this, imports);
-
-  // Traverse all the exported objects and assign indexes to all types.
-  for (std::vector<Named_object*>::const_iterator p = exports->begin();
-       p != exports->end();
-       ++p)
-    {
-      Named_object* no = *p;
-      switch (no->classification())
-	{
-	case Named_object::NAMED_OBJECT_CONST:
-	  {
-	    Type* t = no->const_value()->type();
-	    if (t != NULL && !t->is_abstract())
-	      Type::traverse(t, &find);
-	  }
-	  break;
-
-	case Named_object::NAMED_OBJECT_TYPE:
-	  Type::traverse(no->type_value()->real_type(), &find);
-	  find.traverse_named_type(no->type_value());
-	  break;
-
-	case Named_object::NAMED_OBJECT_VAR:
-	  Type::traverse(no->var_value()->type(), &find);
-	  break;
-
-	case Named_object::NAMED_OBJECT_FUNC:
-	  find.traverse_function(no->func_value()->type());
-	  break;
-
-	case Named_object::NAMED_OBJECT_FUNC_DECLARATION:
-	  find.traverse_function(no->func_declaration_value()->type());
-	  break;
-
-	default:
-	  // We shouldn't see anything else.  If we do we'll give an
-	  // error later when we try to actually export it.
-	  break;
-	}
-    }
-
-  return ret;
-}
-
-// Give a type an index if it doesn't already have one.  Return true
-// if we set the type index, false if it was already known.
+// Record a type in the "to be indexed" set. Return true if the type
+// was not already in the set, false otherwise.
 
 bool
-Export::set_type_index(Type* type)
+Export::record_type(Type* type)
 {
   type = type->forwarded();
 
   std::pair<Type_refs::iterator, bool> ins =
-    type_refs.insert(std::make_pair(type, 0));
+    this->impl_->type_refs.insert(std::make_pair(type, 0));
   if (!ins.second)
     {
       // We've already seen this type.
       return false;
     }
+  ins.first->second = 0;
 
+  return true;
+}
+
+// Assign the specified type an index.
+
+void
+Export::set_type_index(const Type* type)
+{
+  type = type->forwarded();
+  std::pair<Type_refs::iterator, bool> ins =
+    this->impl_->type_refs.insert(std::make_pair(type, 0));
+  go_assert(!ins.second);
   int index = this->type_index_;
   ++this->type_index_;
+  go_assert(ins.first->second == 0);
   ins.first->second = index;
+}
+
+// This helper assigns type indices to all types mentioned directly or
+// indirectly in the things we're exporting. Actual exported types are given
+// indices according to where the appear on the sorted exports list; all other
+// types appear afterwards. Return value is the total number of exported types
+// plus 1, e.g. the index of the 1st non-exported type.
 
-  return true;
+int
+Export::assign_type_indices(const std::vector<Named_object*>& sorted_exports)
+{
+  // Assign indexes to all the exported types.
+  for (std::vector<Named_object*>::const_iterator p = sorted_exports.begin();
+       p != sorted_exports.end();
+       ++p)
+    {
+      if (!(*p)->is_type())
+	continue;
+      Interface_type* it = (*p)->type_value()->interface_type();
+      if (it != NULL)
+        it->sort_embedded();
+      this->record_type((*p)->type_value());
+      this->set_type_index((*p)->type_value());
+    }
+  int ret = this->type_index_;
+
+  // Collect export-referenced, non-builtin types.
+  std::vector<const Type*> types;
+  types.reserve(this->impl_->type_refs.size());
+  for (Type_refs::const_iterator p = this->impl_->type_refs.begin();
+       p != this->impl_->type_refs.end();
+       ++p)
+    {
+      const Type* t = p->first;
+      if (p->second != 0)
+        continue;
+      types.push_back(t);
+    }
+
+  // Sort the types.
+  std::sort(types.begin(), types.end(), Sort_types());
+
+  // Assign numbers to the sorted list.
+  for (std::vector<const Type *>::const_iterator p = types.begin();
+       p != types.end();
+       ++p)
+    this->set_type_index((*p));
+
+  return ret;
 }
 
 // Sort packages.
@@ -479,7 +771,24 @@
 static bool
 packages_compare(const Package* a, const Package* b)
 {
-  return a->package_name() < b->package_name();
+  if (a->package_name() < b->package_name())
+    return true;
+  else if (a->package_name() > b->package_name())
+    return false;
+
+  if (a->pkgpath() < b->pkgpath())
+    return true;
+  else if (a->pkgpath() > b->pkgpath())
+    return false;
+
+  // In principle if we get here then a == b.  Try to do something sensible
+  // even if the import information is inconsistent.
+  if (a->pkgpath_symbol() < b->pkgpath_symbol())
+    return true;
+  else if (a->pkgpath_symbol() > b->pkgpath_symbol())
+    return false;
+
+  return a < b;
 }
 
 // Write out all the known packages whose pkgpath symbol is not a
@@ -529,7 +838,7 @@
 
 void
 Export::write_imports(const std::map<std::string, Package*>& imports,
-		      const Unordered_set(const Package*)& type_imports)
+		      const Unordered_set(const Package*)& all_imports)
 {
   // Sort the imports for more consistent output.
   Unordered_set(const Package*) seen;
@@ -544,6 +853,7 @@
 
   std::sort(sorted_imports.begin(), sorted_imports.end(), import_compare);
 
+  int package_index = 1;
   for (std::vector<std::pair<std::string, Package*> >::const_iterator p =
 	 sorted_imports.begin();
        p != sorted_imports.end();
@@ -557,14 +867,15 @@
       this->write_string(p->first);
       this->write_c_string("\"\n");
 
-      this->packages_.insert(p->second);
+      this->packages_[p->second] = package_index;
+      package_index++;
     }
 
   // Write out a separate list of indirectly imported packages.
   std::vector<const Package*> indirect_imports;
   for (Unordered_set(const Package*)::const_iterator p =
-	 type_imports.begin();
-       p != type_imports.end();
+	 all_imports.begin();
+       p != all_imports.end();
        ++p)
     {
       if (seen.find(*p) == seen.end())
@@ -584,6 +895,9 @@
       this->write_c_string(" ");
       this->write_string((*p)->pkgpath());
       this->write_c_string("\n");
+
+      this->packages_[*p] = package_index;
+      package_index++;
     }
 }
 
@@ -614,6 +928,8 @@
        ++p)
     {
       const Import_init* ii = *p;
+      if (ii->is_dummy())
+        continue;
       std::map<std::string, unsigned>::const_iterator srcit =
           init_idx.find(ii->init_name());
       go_assert(srcit != init_idx.end());
@@ -712,7 +1028,7 @@
 
   // Now add edges from the local init function to each of the
   // imported fcns.
-  if (!import_init_fn.empty())
+  if (!import_init_fn.empty() && import_init_fn[0] != '~')
     {
       unsigned src = 0;
       go_assert(init_idx[import_init_fn] == 0);
@@ -721,6 +1037,8 @@
            ++p)
 	{
           const Import_init* ii = *p;
+          if (ii->is_dummy())
+            continue;
 	  unsigned sink = init_idx[ii->init_name()];
 	  add_init_graph_edge(&init_graph, src, sink);
 	}
@@ -814,8 +1132,8 @@
 {
   // Map from type index to type.
   std::vector<const Type*> types(static_cast<size_t>(this->type_index_));
-  for (Type_refs::const_iterator p = type_refs.begin();
-       p != type_refs.end();
+  for (Type_refs::const_iterator p = this->impl_->type_refs.begin();
+       p != this->impl_->type_refs.end();
        ++p)
     {
       if (p->second >= 0)
@@ -913,7 +1231,7 @@
   if (name.empty())
     this->write_c_string("?");
   else
-    this->write_string(Gogo::message_name(name));
+    this->write_string(Gogo::unpack_hidden_name(name));
 }
 
 // Write an integer value to the export stream.
@@ -936,21 +1254,54 @@
   this->write_c_string(buf);
 }
 
+// Return the index of a package.
+
+int
+Export::package_index(const Package* pkg) const
+{
+  Unordered_map(const Package *, int)::const_iterator p =
+    this->packages_.find(pkg);
+  go_assert(p != this->packages_.end());
+  int index = p->second;
+  go_assert(index != 0);
+  return index;
+}
+
+// Return the index of a type.
+
+int
+Export::type_index(const Type* type)
+{
+  type = type->forwarded();
+  Type_refs::const_iterator p = this->impl_->type_refs.find(type);
+  go_assert(p != this->impl_->type_refs.end());
+  int index = p->second;
+  go_assert(index != 0);
+  return index;
+}
+
 // Export a type.
 
 void
 Export::write_type(const Type* type)
 {
-  type = type->forwarded();
-  Type_refs::const_iterator p = type_refs.find(type);
-  go_assert(p != type_refs.end());
-  int index = p->second;
-  go_assert(index != 0);
+  int index = this->type_index(type);
   char buf[30];
   snprintf(buf, sizeof buf, "<type %d>", index);
   this->write_c_string(buf);
 }
 
+// Export a type to a function body.
+
+void
+Export::write_type_to(const Type* type, Export_function_body* efb)
+{
+  int index = this->type_index(type);
+  char buf[30];
+  snprintf(buf, sizeof buf, "<type %d>", index);
+  efb->write_c_string(buf);
+}
+
 // Export escape note.
 
 void
@@ -1001,7 +1352,7 @@
   Named_object* named_object = gogo->lookup_global(name);
   go_assert(named_object != NULL && named_object->is_type());
   std::pair<Type_refs::iterator, bool> ins =
-    type_refs.insert(std::make_pair(named_object->type_value(), code));
+    this->impl_->type_refs.insert(std::make_pair(named_object->type_value(), code));
   go_assert(ins.second);
 
   // We also insert the underlying type.  We can see the underlying
@@ -1009,7 +1360,7 @@
   // fails--we expect duplications here, and it doesn't matter when
   // they occur.
   Type* real_type = named_object->type_value()->real_type();
-  type_refs.insert(std::make_pair(real_type, code));
+  this->impl_->type_refs.insert(std::make_pair(real_type, code));
 }
 
 // Class Export::Stream.
@@ -1066,3 +1417,56 @@
 {
   this->backend_->write_export_data (bytes, length);
 }
+
+// Class Export_function_body.
+
+// Record a temporary statement.
+
+unsigned int
+Export_function_body::record_temporary(const Temporary_statement* temp)
+{
+  unsigned int ret = this->next_temporary_index_;
+  if (ret > 0x7fffffff)
+    go_error_at(temp->location(),
+		"too many temporary statements in export data");
+  ++this->next_temporary_index_;
+  std::pair<const Temporary_statement*, unsigned int> val(temp, ret);
+  std::pair<Unordered_map(const Temporary_statement*, unsigned int)::iterator,
+	    bool> ins = this->temporary_indexes_.insert(val);
+  go_assert(ins.second);
+  return ret;
+}
+
+// Return the index of a temporary statement.
+
+unsigned int
+Export_function_body::temporary_index(const Temporary_statement* temp)
+{
+  Unordered_map(const Temporary_statement*, unsigned int)::const_iterator p =
+    this->temporary_indexes_.find(temp);
+  go_assert(p != this->temporary_indexes_.end());
+  return p->second;
+}
+
+// Return the index of an unnamed label.  If it doesn't already have
+// an index, give it one.
+
+unsigned int
+Export_function_body::unnamed_label_index(const Unnamed_label* label)
+{
+  unsigned int next = this->next_label_index_;
+  std::pair<const Unnamed_label*, unsigned int> val(label, next);
+  std::pair<Unordered_map(const Unnamed_label*, unsigned int)::iterator,
+	    bool> ins =
+    this->label_indexes_.insert(val);
+  if (!ins.second)
+    return ins.first->second;
+  else
+    {
+      if (next > 0x7fffffff)
+	go_error_at(label->location(),
+		    "too many unnamed labels in export data");
+      ++this->next_label_index_;
+      return next;
+    }
+}