diff mbox series

Go patch committed: Export indexed type data, read unexported types lazily

Message ID CAOyqgcXvnbWOJ1L9hs160MVYdpZUfN_GpvxpdVM1_2-8o5kJvw@mail.gmail.com
State New
Headers show
Series Go patch committed: Export indexed type data, read unexported types lazily | expand

Commit Message

Ian Lance Taylor Oct. 23, 2018, 2:46 a.m. UTC
This patch to the Go frontend changes the export format to index type
data so that it doesn't have to be read until needed.  It changes the
importer to only read the exported types at first, and to read other
types as they are needed.  It introduces a new "types" command to the
export data to record the number of types and the size of their export
data. It is immediately followed by new "type" commands that can be
indexed.  Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.
Committed to mainline.

Ian
diff mbox series

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 265408)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@ 
-e1dc92a6037a3f81ea1b8ea8fb6207af33505f0c
+6db7e35d3bcd75ab3cb15296a5ddc5178038c9c1
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/export.cc
===================================================================
--- gcc/go/gofrontend/export.cc	(revision 265408)
+++ gcc/go/gofrontend/export.cc	(working copy)
@@ -44,11 +44,49 @@  const int Export::checksum_len;
 // Constructor.
 
 Export::Export(Stream* stream)
-  : stream_(stream), type_refs_(), type_index_(1), packages_()
+  : 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
+{
+ public:
+  unsigned int
+  operator()(const Type* type) const
+  {
+    return type->hash_for_method(NULL,
+				 (Type::COMPARE_ERRORS
+				  | Type::COMPARE_TAGS
+				  | Type::COMPARE_ALIASES));
+  }
+};
+
+class Type_alias_identical
+{
+ public:
+  bool
+  operator()(const Type* t1, const Type* t2) const
+  {
+    return Type::are_identical(t1, t2,
+			       (Type::COMPARE_ERRORS
+				| Type::COMPARE_TAGS
+				| 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.
+
+typedef Unordered_map_hash(const Type*, int, Type_hash_alias_identical,
+			   Type_alias_identical) Type_refs;
+
+static Type_refs type_refs;
+
 // A functor to sort Named_object pointers by name.
 
 struct Sort_bindings
@@ -139,9 +177,10 @@  Export::export_globals(const std::string
 
   std::sort(exports.begin(), exports.end(), Sort_bindings());
 
-  // Find all packages not explicitly imported but mentioned by types.
+  // Assign indexes to all exported types and types referenced by
+  // exported types, and collect all packages mentioned.
   Unordered_set(const Package*) type_imports;
-  this->prepare_types(&exports, &type_imports);
+  int unexported_type_index = this->prepare_types(&exports, &type_imports);
 
   // Although the export data is readable, at least this version is,
   // it is conceptually a binary format.  Start with a four byte
@@ -178,10 +217,17 @@  Export::export_globals(const std::string
   // and ABI being used, although ideally any problems in that area
   // would be caught by the linker.
 
+  // Write out all the types, both exported and not.
+  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();
        ++p)
-    (*p)->export_named_object(this);
+    {
+      if (!(*p)->is_type())
+	(*p)->export_named_object(this);
+    }
 
   std::string checksum = this->stream_->checksum();
   std::string s = "checksum ";
@@ -204,9 +250,10 @@  Export::export_globals(const std::string
 class Find_types_to_prepare : public Traverse
 {
  public:
-  Find_types_to_prepare(Unordered_set(const Package*)* imports)
+  Find_types_to_prepare(Export* exp,
+			Unordered_set(const Package*)* imports)
     : Traverse(traverse_types),
-      imports_(imports)
+      exp_(exp), imports_(imports)
   { }
 
   int
@@ -221,19 +268,34 @@  class Find_types_to_prepare : public Tra
   traverse_named_type(Named_type*);
 
  private:
+  // Exporters.
+  Export* exp_;
   // List of packages we are building.
   Unordered_set(const Package*)* imports_;
 };
 
-// Traverse a type.
+// 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.
+  // 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
@@ -267,7 +329,7 @@  Find_types_to_prepare::type(Type* type)
 }
 
 // Traverse the types in a function type.  We don't need the function
-// type tself, just the receiver, parameter, and result types.
+// type itself, just the receiver, parameter, and result types.
 
 void
 Find_types_to_prepare::traverse_function(Function_type* type)
@@ -319,20 +381,34 @@  Find_types_to_prepare::traverse_named_ty
     }
 }
 
-// Collect all the pacakges we see in types, so that if we refer to
-// any types from indirectly importe packages we can tell the importer
-// about the package.
+// 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.
 
-void
+int
 Export::prepare_types(const std::vector<Named_object*>* exports,
 		      Unordered_set(const Package*)* imports)
 {
-  // Use a single index of the traversal class because traversal
+  // 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(imports);
+  Find_types_to_prepare find(this, imports);
 
-  // Traverse all the exported objects.
+  // 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)
@@ -349,7 +425,8 @@  Export::prepare_types(const std::vector<
 	  break;
 
 	case Named_object::NAMED_OBJECT_TYPE:
-	  Type::traverse(no->type_value(), &find);
+	  Type::traverse(no->type_value()->real_type(), &find);
+	  find.traverse_named_type(no->type_value());
 	  break;
 
 	case Named_object::NAMED_OBJECT_VAR:
@@ -370,6 +447,31 @@  Export::prepare_types(const std::vector<
 	  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.
+
+bool
+Export::set_type_index(Type* type)
+{
+  type = type->forwarded();
+
+  std::pair<Type_refs::iterator, bool> ins =
+    type_refs.insert(std::make_pair(type, 0));
+  if (!ins.second)
+    {
+      // We've already seen this type.
+      return false;
+    }
+
+  int index = this->type_index_;
+  ++this->type_index_;
+  ins.first->second = index;
+
+  return true;
 }
 
 // Sort packages.
@@ -705,6 +807,104 @@  Export::write_imported_init_fns(const st
   this->write_c_string("\n");
 }
 
+// Write the types to the export stream.
+
+void
+Export::write_types(int unexported_type_index)
+{
+  // 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();
+       ++p)
+    {
+      if (p->second >= 0)
+	types.at(p->second) = p->first;
+    }
+
+  // Write the type information to a buffer.
+  Stream_to_string type_data;
+  Export::Stream* orig_stream = this->stream_;
+  this->stream_ = &type_data;
+
+  std::vector<size_t> type_sizes(static_cast<size_t>(this->type_index_));
+  type_sizes[0] = 0;
+
+  // Start at 1 because type index 0 is not used.
+  size_t start_size = 0;
+  for (int i = 1; i < this->type_index_; ++i)
+    {
+      this->write_type_definition(types[i], i);
+
+      size_t cur_size = type_data.string().size();
+      type_sizes[i] = cur_size - start_size;
+      start_size = cur_size;
+    }
+
+  // Back to original stream.
+  this->stream_ = orig_stream;
+
+  // The line "types MAXP1 EXPORTEDP1 SIZES..." appears before the
+  // types.  MAXP1 is one more than the maximum type index used; that
+  // is, it is the size of the array we need to allocate to hold all
+  // the values.  Indexes 1 up to but not including EXPORTEDP1 are the
+  // exported types.  The other types are not exported.  SIZES... is a
+  // list of MAXP1-1 entries listing the size of the type definition
+  // for each type, starting at index 1.
+  char buf[100];
+  snprintf(buf, sizeof buf, "types %d %d", this->type_index_,
+	   unexported_type_index);
+  this->write_c_string(buf);
+
+  // Start at 1 because type index 0 is not used.
+  for (int i = 1; i < this->type_index_; ++i)
+    {
+      snprintf(buf, sizeof buf, " %lu",
+	       static_cast<unsigned long>(type_sizes[i]));
+      this->write_c_string(buf);
+    }
+  this->write_c_string("\n");
+  this->write_string(type_data.string());
+}
+
+// Write a single type to the export stream.
+
+void
+Export::write_type_definition(const Type* type, int index)
+{
+  this->write_c_string("type ");
+
+  char buf[30];
+  snprintf(buf, sizeof buf, "%d ", index);
+  this->write_c_string(buf);
+
+  const Named_type* nt = type->named_type();
+  if (nt != NULL)
+    {
+      const Named_object* no = nt->named_object();
+      const Package* package = no->package();
+
+      this->write_c_string("\"");
+      if (package != NULL && !Gogo::is_hidden_name(no->name()))
+	{
+	  this->write_string(package->pkgpath());
+	  this->write_c_string(".");
+	}
+      this->write_string(nt->named_object()->name());
+      this->write_c_string("\" ");
+
+      if (nt->is_alias())
+	this->write_c_string("= ");
+    }
+
+  type->export_type(this);
+
+  // Type::export_type will print a newline for a named type, but not
+  // otherwise.
+  if (nt == NULL)
+    this->write_c_string("\n");
+}
+
 // Write a name to the export stream.
 
 void
@@ -736,91 +936,19 @@  Export::write_unsigned(unsigned value)
   this->write_c_string(buf);
 }
 
-// Export a type.  We have to ensure that on import we create a single
-// Named_type node for each named type.  We do this by keeping a hash
-// table mapping named types to reference numbers.  The first time we
-// see a named type we assign it a reference number by making an entry
-// in the hash table.  If we see it again, we just refer to the
-// reference number.
-
-// Named types are, of course, associated with packages.  Note that we
-// may see a named type when importing one package, and then later see
-// the same named type when importing a different package.  The home
-// package may or may not be imported during this compilation.  The
-// reference number scheme has to get this all right.  Basic approach
-// taken from "On the Linearization of Graphs and Writing Symbol
-// Files" by Robert Griesemer.
+// Export a type.
 
 void
 Export::write_type(const Type* type)
 {
-  // We don't want to assign a reference number to a forward
-  // declaration to a type which was defined later.
   type = type->forwarded();
-
-  Type_refs::const_iterator p = this->type_refs_.find(type);
-  if (p != this->type_refs_.end())
-    {
-      // This type was already in the table.
-      int index = p->second;
-      go_assert(index != 0);
-      char buf[30];
-      snprintf(buf, sizeof buf, "<type %d>", index);
-      this->write_c_string(buf);
-      return;
-    }
-
-  const Named_type* named_type = type->named_type();
-  const Forward_declaration_type* forward = type->forward_declaration_type();
-
-  int index = this->type_index_;
-  ++this->type_index_;
-
+  Type_refs::const_iterator p = type_refs.find(type);
+  go_assert(p != type_refs.end());
+  int index = p->second;
+  go_assert(index != 0);
   char buf[30];
-  snprintf(buf, sizeof buf, "<type %d ", index);
+  snprintf(buf, sizeof buf, "<type %d>", index);
   this->write_c_string(buf);
-
-  if (named_type != NULL || forward != NULL)
-    {
-      const Named_object* named_object;
-      if (named_type != NULL)
-	{
-	  // The builtin types should have been predefined.
-	  go_assert(!Linemap::is_predeclared_location(named_type->location())
-		     || (named_type->named_object()->package()->package_name()
-			 == "unsafe"));
-	  named_object = named_type->named_object();
-	}
-      else
-	named_object = forward->named_object();
-
-      const Package* package = named_object->package();
-
-      std::string s = "\"";
-      if (package != NULL && !Gogo::is_hidden_name(named_object->name()))
-	{
-	  s += package->pkgpath();
-	  s += '.';
-	}
-      s += named_object->name();
-      s += "\" ";
-      this->write_string(s);
-
-      // We must add a named type to the table now, since the
-      // definition of the type may refer to the named type via a
-      // pointer.
-      this->type_refs_[type] = index;
-
-      if (named_type != NULL && named_type->is_alias())
-	this->write_c_string("= ");
-    }
-
-  type->export_type(this);
-
-  this->write_c_string(">");
-
-  if (named_type == NULL)
-    this->type_refs_[type] = index;
 }
 
 // Export escape note.
@@ -873,18 +1001,15 @@  Export::register_builtin_type(Gogo* gogo
   Named_object* named_object = gogo->lookup_global(name);
   go_assert(named_object != NULL && named_object->is_type());
   std::pair<Type_refs::iterator, bool> ins =
-    this->type_refs_.insert(std::make_pair(named_object->type_value(), code));
+    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
-  // type at least for string and bool.  We skip the type aliases byte
-  // and rune here.
-  if (code != BUILTIN_BYTE && code != BUILTIN_RUNE)
-    {
-      Type* real_type = named_object->type_value()->real_type();
-      ins = this->type_refs_.insert(std::make_pair(real_type, code));
-      go_assert(ins.second);
-    }
+  // type at least for string and bool.  It's OK if this insert
+  // 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));
 }
 
 // Class Export::Stream.
Index: gcc/go/gofrontend/export.h
===================================================================
--- gcc/go/gofrontend/export.h	(revision 265408)
+++ gcc/go/gofrontend/export.h	(working copy)
@@ -11,6 +11,7 @@ 
 
 class Go_sha1_helper;
 class Gogo;
+class Named_object;
 class Import_init;
 class Named_object;
 class Bindings;
@@ -154,6 +155,10 @@  class Export : public String_dump
 		 const Import_init_set& imported_init_fns,
 		 const Bindings* bindings);
 
+  // Set the index of a type.
+  bool
+  set_type_index(Type*);
+
   // Write a string to the export stream.
   void
   write_string(const std::string& s)
@@ -196,7 +201,7 @@  class Export : public String_dump
   Export& operator=(const Export&);
 
   // Prepare types for exporting.
-  void
+  int
   prepare_types(const std::vector<Named_object*>* exports,
 		Unordered_set(const Package*)* imports);
 
@@ -224,24 +229,27 @@  class Export : public String_dump
   write_imported_init_fns(const std::string& package_name,
 			  const std::string&, const Import_init_set&);
 
+  // Write out all types.
+  void
+  write_types(int unexported_type_index);
+
+  // Write out one type definition.
+  void
+  write_type_definition(const Type* type, int index);
+
   // Register one builtin type.
   void
   register_builtin_type(Gogo*, const char* name, Builtin_code);
 
-  // Mapping from Type objects to a constant index.
-  typedef Unordered_map(const Type*, int) Type_refs;
-
   // The stream to which we are writing data.
   Stream* stream_;
-  // Type mappings.
-  Type_refs type_refs_;
   // Index number of next type.
   int type_index_;
   // Packages we have written out.
   Unordered_set(const Package*) packages_;
 };
 
-// An export streamer which puts the export stream in a named section.
+// An export streamer that puts the export stream in a named section.
 
 class Stream_to_section : public Export::Stream
 {
@@ -256,4 +264,26 @@  class Stream_to_section : public Export:
   Backend* backend_;
 };
 
+// An export streamer that puts the export stream in a string.
+
+class Stream_to_string : public Export::Stream
+{
+ public:
+  Stream_to_string()
+    : string_()
+  {}
+
+  const std::string&
+  string() const
+  { return this->string_; }
+
+ protected:
+  void
+  do_write(const char* s, size_t len)
+  { this->string_.append(s, len); }
+
+ private:
+  std::string string_;
+};
+
 #endif // !defined(GO_EXPORT_H)
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 265408)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -7511,8 +7511,8 @@  Named_object::export_named_object(Export
       break;
 
     case NAMED_OBJECT_TYPE:
-      this->type_value()->export_named_type(exp, this->name_);
-      break;
+      // Types are handled by export::write_types.
+      go_unreachable();
 
     case NAMED_OBJECT_TYPE_DECLARATION:
       go_error_at(this->type_declaration_value()->location(),
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 265408)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -12,9 +12,7 @@ 
 class Traverse;
 class Statement_inserter;
 class Type;
-class Type_hash_identical;
 class Type_equal;
-class Type_identical;
 class Typed_identifier;
 class Typed_identifier_list;
 class Function_type;
Index: gcc/go/gofrontend/import.cc
===================================================================
--- gcc/go/gofrontend/import.cc	(revision 265408)
+++ gcc/go/gofrontend/import.cc	(working copy)
@@ -236,7 +236,7 @@  Import::find_export_data(const std::stri
     }
 
   char buf[len];
-  ssize_t c = read(fd, buf, len);
+  ssize_t c = ::read(fd, buf, len);
   if (c < len)
     return NULL;
 
@@ -288,7 +288,7 @@  Import::find_object_export_data(const st
 
 Import::Import(Stream* stream, Location location)
   : gogo_(NULL), stream_(stream), location_(location), package_(NULL),
-    add_to_globals_(false),
+    add_to_globals_(false), type_data_(), type_pos_(0), type_offsets_(),
     builtin_types_((- SMALLEST_BUILTIN_CODE) + 1),
     types_(), version_(EXPORT_FORMAT_UNKNOWN)
 {
@@ -403,6 +403,12 @@  Import::import(Gogo* gogo, const std::st
       if (stream->match_c_string("init"))
 	this->read_import_init_fns(gogo);
 
+      if (stream->match_c_string("types "))
+	{
+	  if (!this->read_types())
+	    return NULL;
+	}
+
       // Loop over all the input data for this package.
       while (!stream->saw_error())
 	{
@@ -585,6 +591,86 @@  Import::read_import_init_fns(Gogo* gogo)
     }
 }
 
+// Import the types.  Starting in export format version 3 all the
+// types are listed first.
+
+bool
+Import::read_types()
+{
+  this->require_c_string("types ");
+  std::string str = this->read_identifier();
+  int maxp1;
+  if (!this->string_to_int(str, false, &maxp1))
+    return false;
+
+  this->require_c_string(" ");
+  str = this->read_identifier();
+  int exportedp1;
+  if (!this->string_to_int(str, false, &exportedp1))
+    return false;
+
+  this->type_offsets_.resize(maxp1, std::make_pair<size_t, size_t>(0, 0));
+  size_t total_type_size = 0;
+  // Start at 1 because type index 0 not used.
+  for (int i = 1; i < maxp1; i++)
+    {
+      this->require_c_string(" ");
+      str = this->read_identifier();
+      int v;
+      if (!this->string_to_int(str, false, &v))
+	return false;
+      size_t vs = static_cast<size_t>(v);
+      this->type_offsets_[i] = std::make_pair(total_type_size, vs);
+      total_type_size += vs;
+    }
+
+  this->require_c_string("\n");
+
+  // Types can refer to each other in an unpredictable order.  Read
+  // all the type data into type_data_.  The type_offsets_ vector we
+  // just initialized provides indexes into type_data_.
+
+  this->type_pos_ = this->stream_->pos();
+  const char* type_data;
+  if (!this->stream_->peek(total_type_size, &type_data))
+    return false;
+  this->type_data_ = std::string(type_data, total_type_size);
+  this->advance(total_type_size);
+
+  this->types_.resize(maxp1, NULL);
+
+  // Parse all the exported types now, so that the names are properly
+  // bound and visible to the parser.  Parse unexported types lazily.
+
+  // Start at 1 because there is no type 0.
+  for (int i = 1; i < exportedp1; i++)
+    {
+      // We may have already parsed this type when we parsed an
+      // earlier type.
+      Type* type = this->types_[i];
+      if (type == NULL)
+	{
+	  if (!this->parse_type(i))
+	    return false;
+	  type = this->types_[i];
+	  go_assert(type != NULL);
+	}
+      Named_type* nt = type->named_type();
+      if (nt == NULL)
+	{
+	  go_error_at(this->location_,
+		      "error in import data: exported unnamed type %d",
+		      i);
+	  return false;
+	}
+      nt->set_is_visible();
+      if (this->add_to_globals_)
+	this->gogo_->add_named_type(nt);
+    }
+
+  return true;
+}
+
 // Import a constant.
 
 void
@@ -605,6 +691,18 @@  Import::import_const()
 void
 Import::import_type()
 {
+  if (this->version_ >= EXPORT_FORMAT_V3)
+    {
+      if (!this->stream_->saw_error())
+	{
+	  go_error_at(this->location_,
+		    "error in import data at %d: old type syntax",
+		    this->stream_->pos());
+	  this->stream_->set_saw_error();
+	}
+      return;
+    }
+
   Named_type* type;
   Named_type::import_named_type(this, &type);
 
@@ -694,9 +792,73 @@  Import::import_func(Package* package)
   return no;
 }
 
+// Read a type definition and initialize the entry in this->types_.
+// This parses the type definition saved by read_types earlier.  This
+// returns true on success, false on failure.
+
+bool
+Import::parse_type(int i)
+{
+  go_assert(i >= 0 && static_cast<size_t>(i) < this->types_.size());
+  go_assert(this->types_[i] == NULL);
+  size_t offset = this->type_offsets_[i].first;
+  size_t len = this->type_offsets_[i].second;
+
+  Stream* orig_stream = this->stream_;
+
+  Stream_from_string_ref stream(this->type_data_, offset, len);
+  stream.set_pos(this->type_pos_ + offset);
+  this->stream_ = &stream;
+
+  this->require_c_string("type ");
+  std::string str = this->read_identifier();
+  int id;
+  if (!this->string_to_int(str, false, &id))
+    {
+      this->stream_ = orig_stream;
+      return false;
+    }
+  if (i != id)
+    {
+      go_error_at(this->location_,
+		  ("error in import data at %d: "
+		   "type ID mismatch: got %d, want %d"),
+		  stream.pos(), id, i);
+      this->stream_ = orig_stream;
+      return false;
+    }
+
+  this->require_c_string(" ");
+  if (stream.peek_char() == '"')
+    {
+      stream.advance(1);
+      Type* type = this->read_named_type(i);
+      if (type->is_error_type())
+	{
+	  this->stream_ = orig_stream;
+	  return false;
+	}
+    }
+  else
+    {
+      Type* type = Type::import_type(this);
+      if (type->is_error_type())
+	{
+	  this->stream_ = orig_stream;
+	  return false;
+	}
+      this->types_[i] = type;
+
+      this->require_c_string("\n");
+    }
+
+  this->stream_ = orig_stream;
+  return true;
+}
+
 // Read a type in the import stream.  This records the type by the
-// type index.  If the type is named, it registers the name, but marks
-// it as invisible.
+// type index.  If the type is named (which can only happen with older
+// export formats), it registers the name, but marks it as invisible.
 
 Type*
 Import::read_type()
@@ -720,7 +882,28 @@  Import::read_type()
 
   if (c == '>')
     {
-      // This type was already defined.
+      // A reference to a type defined earlier.
+
+      if (index >= 0 && !this->type_data_.empty())
+	{
+	  if (static_cast<size_t>(index) >= this->type_offsets_.size())
+	    {
+	      go_error_at(this->location_,
+			  ("error in import data at %d: "
+			   "bad type index %d >= %d"),
+			  stream->pos(), index,
+			  static_cast<int>(this->type_offsets_.size()));
+	      stream->set_saw_error();
+	      return Type::make_error_type();
+	    }
+
+	  if (this->types_[index] == NULL)
+	    {
+	      if (!this->parse_type(index))
+		return Type::make_error_type();
+	    }
+	}
+
       if (index < 0
 	  ? (static_cast<size_t>(- index) >= this->builtin_types_.size()
 	     || this->builtin_types_[- index] == NULL)
@@ -737,11 +920,21 @@  Import::read_type()
       return index < 0 ? this->builtin_types_[- index] : this->types_[index];
     }
 
+  if (this->version_ >= EXPORT_FORMAT_V3)
+    {
+      if (!stream->saw_error())
+	go_error_at(this->location_,
+		    "error in import data at %d: expected %<>%>",
+		    stream->pos());
+      stream->set_saw_error();
+      return Type::make_error_type();
+    }
+
   if (c != ' ')
     {
       if (!stream->saw_error())
 	go_error_at(this->location_,
-		    "error in import data at %d: expect %< %> or %<>%>'",
+		    "error in import data at %d: expected %< %> or %<>%>'",
 		    stream->pos());
       stream->set_saw_error();
       stream->advance(1);
@@ -774,10 +967,25 @@  Import::read_type()
       return type;
     }
 
-  // This type has a name.
-
   stream->advance(1);
+
+  Type* type = this->read_named_type(index);
+
+  this->require_c_string(">");
+
+  return type;
+}
+
+// Read a named type from the import stream and store it in
+// this->types_[index].  The stream should be positioned immediately
+// after the '"' that starts the name.
+
+Type*
+Import::read_named_type(int index)
+{
+  Stream* stream = this->stream_;
   std::string type_name;
+  int c;
   while ((c = stream->get_char()) != '"')
     type_name += c;
 
@@ -863,7 +1071,7 @@  Import::read_type()
   // If there is no type definition, then this is just a forward
   // declaration of a type defined in some other file.
   Type* type;
-  if (this->match_c_string(">"))
+  if (this->match_c_string(">") || this->match_c_string("\n"))
     type = this->types_[index];
   else
     {
@@ -912,8 +1120,6 @@  Import::read_type()
 	}
     }
 
-  this->require_c_string(">");
-
   return type;
 }
 
@@ -1125,10 +1331,9 @@  Stream_from_file::do_peek(size_t length,
       *bytes = this->data_.data();
       return true;
     }
-  // Don't bother to handle the general case, since we don't need it.
-  go_assert(length < 64);
-  char buf[64];
-  ssize_t got = read(this->fd_, buf, length);
+
+  this->data_.resize(length);
+  ssize_t got = ::read(this->fd_, &this->data_[0], length);
 
   if (got < 0)
     {
@@ -1149,8 +1354,6 @@  Stream_from_file::do_peek(size_t length,
   if (static_cast<size_t>(got) < length)
     return false;
 
-  this->data_.assign(buf, got);
-
   *bytes = this->data_.data();
   return true;
 }
Index: gcc/go/gofrontend/import.h
===================================================================
--- gcc/go/gofrontend/import.h	(revision 265408)
+++ gcc/go/gofrontend/import.h	(working copy)
@@ -30,6 +30,11 @@  class Import
     Stream();
     virtual ~Stream();
 
+    // Set the position, for error messages.
+    void
+    set_pos(int pos)
+    { this->pos_ = pos; }
+
     // Return whether we have seen an error.
     bool
     saw_error() const
@@ -249,6 +254,10 @@  class Import
   void
   read_import_init_fns(Gogo*);
 
+  // Read the types.
+  bool
+  read_types();
+
   // Import a constant.
   void
   import_const();
@@ -265,6 +274,14 @@  class Import
   Named_object*
   import_func(Package*);
 
+  // Parse a type definition.
+  bool
+  parse_type(int index);
+
+  // Read a named type and store it at this->type_[index].
+  Type*
+  read_named_type(int index);
+
   // Register a single builtin type.
   void
   register_builtin_type(Gogo*, const char* name, Builtin_code);
@@ -299,6 +316,12 @@  class Import
   // Whether to add new objects to the global scope, rather than to a
   // package scope.
   bool add_to_globals_;
+  // All type data.
+  std::string type_data_;
+  // Position of type data in the stream.
+  int type_pos_;
+  // Mapping from type code to offset/length in type_data_.
+  std::vector<std::pair<size_t, size_t> > type_offsets_;
   // Mapping from negated builtin type codes to Type structures.
   std::vector<Named_type*> builtin_types_;
   // Mapping from exported type codes to Type structures.
@@ -399,4 +422,41 @@  class Stream_from_file : public Import::
   std::string data_;
 };
 
+// Read import data from an offset into a std::string.  This uses a
+// reference to the string, to avoid copying, so the string must be
+// kept alive through some other mechanism.
+
+class Stream_from_string_ref : public Import::Stream
+{
+ public:
+  Stream_from_string_ref(const std::string& str, size_t offset, size_t length)
+    : str_(str), pos_(offset), end_(offset + length)
+  { }
+
+  ~Stream_from_string_ref()
+  {}
+
+ protected:
+  bool
+  do_peek(size_t length, const char** bytes)
+  {
+    if (this->pos_ + length > this->end_)
+      return false;
+    *bytes = &this->str_[this->pos_];
+    return true;
+  }
+
+  void
+  do_advance(size_t length)
+  { this->pos_ += length; }
+
+ private:
+  // A reference to the string we are reading from.
+  const std::string& str_;
+  // The current offset into the string.
+  size_t pos_;
+  // The index after the last byte we can read.
+  size_t end_;
+};
+
 #endif // !defined(GO_IMPORT_H)
Index: gcc/go/gofrontend/types.cc
===================================================================
--- gcc/go/gofrontend/types.cc	(revision 265408)
+++ gcc/go/gofrontend/types.cc	(working copy)
@@ -10865,19 +10865,8 @@  Named_type::append_reflection_type_name(
   ret->append(Gogo::unpack_hidden_name(this->named_object_->name()));
 }
 
-// Export the type.  This is called to export a global type.
-
-void
-Named_type::export_named_type(Export* exp, const std::string&) const
-{
-  // We don't need to write the name of the type here, because it will
-  // be written by Export::write_type anyhow.
-  exp->write_c_string("type ");
-  exp->write_type(this);
-  exp->write_c_string("\n");
-}
-
-// Import a named type.
+// Import a named type.  This is only used for export format versions
+// before version 3.
 
 void
 Named_type::import_named_type(Import* imp, Named_type** ptype)
@@ -10891,12 +10880,15 @@  Named_type::import_named_type(Import* im
 }
 
 // Export the type when it is referenced by another type.  In this
-// case Export::export_type will already have issued the name.
+// case Export::export_type will already have issued the name.  The
+// output always ends with a newline, since that is convenient if
+// there are methods.
 
 void
 Named_type::do_export(Export* exp) const
 {
   exp->write_type(this->type_);
+  exp->write_c_string("\n");
 
   // To save space, we only export the methods directly attached to
   // this type.
@@ -10904,7 +10896,6 @@  Named_type::do_export(Export* exp) const
   if (methods == NULL)
     return;
 
-  exp->write_c_string("\n");
   for (Bindings::const_definitions_iterator p = methods->begin_definitions();
        p != methods->end_definitions();
        ++p)
Index: gcc/go/gofrontend/types.h
===================================================================
--- gcc/go/gofrontend/types.h	(revision 265408)
+++ gcc/go/gofrontend/types.h	(working copy)
@@ -3445,10 +3445,6 @@  class Named_type : public Type
   void
   append_mangled_type_name(Gogo*, bool use_alias, std::string*) const;
 
-  // Export the type.
-  void
-  export_named_type(Export*, const std::string& name) const;
-
   // Import a named type.
   static void
   import_named_type(Import*, Named_type**);
Index: libgo/go/go/internal/gccgoimporter/parser.go
===================================================================
--- libgo/go/go/internal/gccgoimporter/parser.go	(revision 265408)
+++ libgo/go/go/internal/gccgoimporter/parser.go	(working copy)
@@ -18,7 +18,7 @@  import (
 )
 
 type parser struct {
-	scanner  scanner.Scanner
+	scanner  *scanner.Scanner
 	version  string                    // format version
 	tok      rune                      // current token
 	lit      string                    // literal string; only valid for Ident, Int, String tokens
@@ -27,18 +27,24 @@  type parser struct {
 	pkg      *types.Package            // reference to imported package
 	imports  map[string]*types.Package // package path -> package object
 	typeMap  map[int]types.Type        // type number -> type
+	typeData []string                  // unparsed type data
 	initdata InitData                  // package init priority data
 }
 
 func (p *parser) init(filename string, src io.Reader, imports map[string]*types.Package) {
+	p.scanner = new(scanner.Scanner)
+	p.initScanner(filename, src)
+	p.imports = imports
+	p.typeMap = make(map[int]types.Type)
+}
+
+func (p *parser) initScanner(filename string, src io.Reader) {
 	p.scanner.Init(src)
 	p.scanner.Error = func(_ *scanner.Scanner, msg string) { p.error(msg) }
 	p.scanner.Mode = scanner.ScanIdents | scanner.ScanInts | scanner.ScanFloats | scanner.ScanStrings | scanner.ScanComments | scanner.SkipComments
 	p.scanner.Whitespace = 1<<'\t' | 1<<' '
 	p.scanner.Filename = filename // for good error messages
 	p.next()
-	p.imports = imports
-	p.typeMap = make(map[int]types.Type)
 }
 
 type importError struct {
@@ -720,6 +726,9 @@  func (p *parser) parseType(pkg *types.Pa
 		n := p.parseInt()
 
 		if p.tok == '>' {
+			if len(p.typeData) > 0 && p.typeMap[int(n)] == nil {
+				p.parseSavedType(pkg, int(n))
+			}
 			t = p.typeMap[int(n)]
 		} else {
 			t = p.parseTypeDefinition(pkg, int(n))
@@ -739,6 +748,67 @@  func (p *parser) parseType(pkg *types.Pa
 	return
 }
 
+// Types = "types" maxp1 exportedp1 (offset length)* .
+func (p *parser) parseTypes(pkg *types.Package) {
+	maxp1 := p.parseInt()
+	exportedp1 := p.parseInt()
+
+	type typeOffset struct {
+		offset int
+		length int
+	}
+	var typeOffsets []typeOffset
+
+	total := 0
+	for i := 1; i < int(maxp1); i++ {
+		len := int(p.parseInt())
+		typeOffsets = append(typeOffsets, typeOffset{total, len})
+		total += len
+	}
+
+	// We should now have p.tok pointing to the final newline.
+	// The next runes from the scanner should be the type data.
+
+	var sb strings.Builder
+	for sb.Len() < total {
+		r := p.scanner.Next()
+		if r == scanner.EOF {
+			p.error("unexpected EOF")
+		}
+		sb.WriteRune(r)
+	}
+	allTypeData := sb.String()
+
+	p.typeData = []string{""} // type 0, unused
+	for _, to := range typeOffsets {
+		p.typeData = append(p.typeData, allTypeData[to.offset:to.offset+to.length])
+	}
+
+	for i := 1; i < int(exportedp1); i++ {
+		p.parseSavedType(pkg, i)
+	}
+}
+
+// parseSavedType parses one saved type definition.
+func (p *parser) parseSavedType(pkg *types.Package, i int) {
+	defer func(s *scanner.Scanner, tok rune, lit string) {
+		p.scanner = s
+		p.tok = tok
+		p.lit = lit
+	}(p.scanner, p.tok, p.lit)
+
+	p.scanner = new(scanner.Scanner)
+	p.initScanner(p.scanner.Filename, strings.NewReader(p.typeData[i]))
+	p.expectKeyword("type")
+	id := int(p.parseInt())
+	if id != i {
+		p.errorf("type ID mismatch: got %d, want %d", id, i)
+	}
+	if p.typeMap[i] == nil {
+		p.typeMap[i] = p.parseTypeDefinition(pkg, i)
+	}
+}
+
 // PackageInit = unquotedString unquotedString int .
 func (p *parser) parsePackageInit() PackageInit {
 	name := p.parseUnquotedString()
@@ -883,6 +953,11 @@  func (p *parser) parseDirective() {
 		p.getPkg(pkgpath, pkgname)
 		p.expectEOL()
 
+	case "types":
+		p.next()
+		p.parseTypes(p.pkg)
+		p.expectEOL()
+
 	case "func":
 		p.next()
 		fun := p.parseFunc(p.pkg)