diff gcc/go/gofrontend/export.cc @ 111:04ced10e8804

gcc 7
author kono
date Fri, 27 Oct 2017 22:46:09 +0900
parents
children 84e7813d76e9
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gcc/go/gofrontend/export.cc	Fri Oct 27 22:46:09 2017 +0900
@@ -0,0 +1,748 @@
+// export.cc -- Export declarations in Go frontend.
+
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "go-system.h"
+
+#include "go-sha1.h"
+#include "go-c.h"
+
+#include "gogo.h"
+#include "types.h"
+#include "statements.h"
+#include "export.h"
+
+#include "go-linemap.h"
+#include "backend.h"
+
+// This file handles exporting global declarations.
+
+// Class Export.
+
+const int Export::magic_len;
+
+// Current version magic string.
+const char Export::cur_magic[Export::magic_len] =
+  {
+    'v', '2', ';', '\n'
+  };
+
+// Magic string for previous version (still supported)
+const char Export::v1_magic[Export::magic_len] =
+  {
+    'v', '1', ';', '\n'
+  };
+
+const int Export::checksum_len;
+
+// Constructor.
+
+Export::Export(Stream* stream)
+  : stream_(stream), type_refs_(), type_index_(1), packages_()
+{
+  go_assert(Export::checksum_len == Go_sha1_helper::checksum_len);
+}
+
+// A functor to sort Named_object pointers by name.
+
+struct Sort_bindings
+{
+  bool
+  operator()(const Named_object* n1, const Named_object* n2) const
+  { return n1->name() < n2->name(); }
+};
+
+// Return true if we should export NO.
+
+static bool
+should_export(Named_object* no)
+{
+  // We only export objects which are locally defined.
+  if (no->package() != NULL)
+    return false;
+
+  // We don't export packages.
+  if (no->is_package())
+    return false;
+
+  // We don't export hidden names.
+  if (Gogo::is_hidden_name(no->name()))
+    return false;
+
+  // We don't export nested functions.
+  if (no->is_function() && no->func_value()->enclosing() != NULL)
+    return false;
+
+  // We don't export thunks.
+  if (no->is_function() && Gogo::is_thunk(no))
+    return false;
+
+  // Methods are exported with the type, not here.
+  if (no->is_function()
+      && no->func_value()->type()->is_method())
+    return false;
+  if (no->is_function_declaration()
+      && no->func_declaration_value()->type()->is_method())
+    return false;
+
+  // Don't export dummy global variables created for initializers when
+  // used with sinks.
+  if (no->is_variable() && no->name()[0] == '_' && no->name()[1] == '.')
+    return false;
+
+  return true;
+}
+
+// Export those identifiers marked for exporting.
+
+void
+Export::export_globals(const std::string& package_name,
+		       const std::string& prefix,
+		       const std::string& pkgpath,
+		       const std::map<std::string, Package*>& packages,
+		       const std::map<std::string, Package*>& imports,
+		       const std::string& import_init_fn,
+                       const Import_init_set& imported_init_fns,
+		       const Bindings* bindings)
+{
+  // 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
+  // mismatched types or other confusions.
+  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());
+
+  for (Bindings::const_definitions_iterator p = bindings->begin_definitions();
+       p != bindings->end_definitions();
+       ++p)
+    if (should_export(*p))
+      exports.push_back(*p);
+
+  for (Bindings::const_declarations_iterator p =
+	 bindings->begin_declarations();
+       p != bindings->end_declarations();
+       ++p)
+    {
+      // We export a function declaration as it may be implemented in
+      // supporting C code.  We do not export type declarations.
+      if (p->second->is_function_declaration()
+	  && should_export(p->second))
+	exports.push_back(p->second);
+    }
+
+  std::sort(exports.begin(), exports.end(), Sort_bindings());
+
+  // Although the export data is readable, at least this version is,
+  // it is conceptually a binary format.  Start with a four byte
+  // version number.
+  this->write_bytes(Export::cur_magic, Export::magic_len);
+
+  // The package name.
+  this->write_c_string("package ");
+  this->write_string(package_name);
+  this->write_c_string(";\n");
+
+  // The prefix or package path, used for all global symbols.
+  if (prefix.empty())
+    {
+      go_assert(!pkgpath.empty());
+      this->write_c_string("pkgpath ");
+      this->write_string(pkgpath);
+    }
+  else
+    {
+      this->write_c_string("prefix ");
+      this->write_string(prefix);
+    }
+  this->write_c_string(";\n");
+
+  this->write_packages(packages);
+
+  this->write_imports(imports);
+
+  this->write_imported_init_fns(package_name, import_init_fn,
+				imported_init_fns);
+
+  // FIXME: It might be clever to add something about the processor
+  // and ABI being used, although ideally any problems in that area
+  // would be caught by the linker.
+
+  for (std::vector<Named_object*>::const_iterator p = exports.begin();
+       p != exports.end();
+       ++p)
+    (*p)->export_named_object(this);
+
+  std::string checksum = this->stream_->checksum();
+  std::string s = "checksum ";
+  for (std::string::const_iterator p = checksum.begin();
+       p != checksum.end();
+       ++p)
+    {
+      unsigned char c = *p;
+      unsigned int dig = c >> 4;
+      s += dig < 10 ? '0' + dig : 'A' + dig - 10;
+      dig = c & 0xf;
+      s += dig < 10 ? '0' + dig : 'A' + dig - 10;
+    }
+  s += ";\n";
+  this->stream_->write_checksum(s);
+}
+
+// Sort packages.
+
+static bool
+packages_compare(const Package* a, const Package* b)
+{
+  return a->package_name() < b->package_name();
+}
+
+// Write out all the known packages whose pkgpath symbol is not a
+// simple transformation of the pkgpath, so that the importing code
+// can reliably know it.
+
+void
+Export::write_packages(const std::map<std::string, Package*>& packages)
+{
+  // Sort for consistent output.
+  std::vector<Package*> out;
+  for (std::map<std::string, Package*>::const_iterator p = packages.begin();
+       p != packages.end();
+       ++p)
+    {
+      if (p->second->pkgpath_symbol()
+	  != Gogo::pkgpath_for_symbol(p->second->pkgpath()))
+	out.push_back(p->second);
+    }
+
+  std::sort(out.begin(), out.end(), packages_compare);
+
+  for (std::vector<Package*>::const_iterator p = out.begin();
+       p != out.end();
+       ++p)
+    {
+      this->write_c_string("package ");
+      this->write_string((*p)->package_name());
+      this->write_c_string(" ");
+      this->write_string((*p)->pkgpath());
+      this->write_c_string(" ");
+      this->write_string((*p)->pkgpath_symbol());
+      this->write_c_string(";\n");
+    }
+}
+
+// Sort imported packages.
+
+static bool
+import_compare(const std::pair<std::string, Package*>& a,
+	       const std::pair<std::string, Package*>& b)
+{
+  return a.first < b.first;
+}
+
+// Write out the imported packages.
+
+void
+Export::write_imports(const std::map<std::string, Package*>& imports)
+{
+  // Sort the imports for more consistent output.
+  std::vector<std::pair<std::string, Package*> > sorted_imports;
+  for (std::map<std::string, Package*>::const_iterator p = imports.begin();
+       p != imports.end();
+       ++p)
+    sorted_imports.push_back(std::make_pair(p->first, p->second));
+
+  std::sort(sorted_imports.begin(), sorted_imports.end(), import_compare);
+
+  for (std::vector<std::pair<std::string, Package*> >::const_iterator p =
+	 sorted_imports.begin();
+       p != sorted_imports.end();
+       ++p)
+    {
+      this->write_c_string("import ");
+      this->write_string(p->second->package_name());
+      this->write_c_string(" ");
+      this->write_string(p->second->pkgpath());
+      this->write_c_string(" \"");
+      this->write_string(p->first);
+      this->write_c_string("\";\n");
+
+      this->packages_.insert(p->second);
+    }
+}
+
+void
+Export::add_init_graph_edge(Init_graph* init_graph, unsigned src, unsigned sink)
+{
+  Init_graph::iterator it = init_graph->find(src);
+  if (it != init_graph->end())
+    it->second.insert(sink);
+  else
+    {
+      std::set<unsigned> succs;
+      succs.insert(sink);
+      (*init_graph)[src] = succs;
+    }
+}
+
+// Constructs the imported portion of the init graph, e.g. those
+// edges that we read from imported packages.
+
+void
+Export::populate_init_graph(Init_graph* init_graph,
+                            const Import_init_set& imported_init_fns,
+                            const std::map<std::string, unsigned>& init_idx)
+{
+  for (Import_init_set::const_iterator p = imported_init_fns.begin();
+       p != imported_init_fns.end();
+       ++p)
+    {
+      const Import_init* ii = *p;
+      std::map<std::string, unsigned>::const_iterator srcit =
+          init_idx.find(ii->init_name());
+      go_assert(srcit != init_idx.end());
+      unsigned src = srcit->second;
+      for (std::set<std::string>::const_iterator pci = ii->precursors().begin();
+           pci != ii->precursors().end();
+           ++pci)
+	{
+	  std::map<std::string, unsigned>::const_iterator it =
+	      init_idx.find(*pci);
+	  go_assert(it != init_idx.end());
+	  unsigned sink = it->second;
+	  add_init_graph_edge(init_graph, src, sink);
+	}
+    }
+}
+
+// Write out the initialization functions which need to run for this
+// package.
+
+void
+Export::write_imported_init_fns(const std::string& package_name,
+                                const std::string& import_init_fn,
+                                const Import_init_set& imported_init_fns)
+{
+  if (import_init_fn.empty() && imported_init_fns.empty()) return;
+
+  // Maps a given init function to the its index in the exported "init" clause.
+  std::map<std::string, unsigned> init_idx;
+
+  this->write_c_string("init");
+
+  if (!import_init_fn.empty())
+    {
+      this->write_c_string(" ");
+      this->write_string(package_name);
+      this->write_c_string(" ");
+      this->write_string(import_init_fn);
+      init_idx[import_init_fn] = 0;
+    }
+
+  if (imported_init_fns.empty())
+    {
+      this->write_c_string(";\n");
+      return;
+    }
+
+  typedef std::map<int, std::vector<std::string> > level_map;
+  Init_graph init_graph;
+  level_map inits_at_level;
+
+  // Walk through the set of import inits (already sorted by
+  // init fcn name) and write them out to the exports.
+  for (Import_init_set::const_iterator p = imported_init_fns.begin();
+       p != imported_init_fns.end();
+       ++p)
+    {
+      const Import_init* ii = *p;
+
+      if (ii->init_name() == import_init_fn)
+	continue;
+
+      this->write_c_string(" ");
+      this->write_string(ii->package_name());
+      this->write_c_string(" ");
+      this->write_string(ii->init_name());
+
+      // Populate init_idx.
+      go_assert(init_idx.find(ii->init_name()) == init_idx.end());
+      unsigned idx = init_idx.size();
+      init_idx[ii->init_name()] = idx;
+
+      // If the init function has a non-negative priority value, this
+      // is an indication that it was referred to in an older version
+      // export data section (e.g. we read a legacy object
+      // file). Record such init fcns so that we can fix up the graph
+      // for them (handled later in this function).
+      if (ii->priority() > 0)
+	{
+	  level_map::iterator it = inits_at_level.find(ii->priority());
+	  if (it == inits_at_level.end())
+	    {
+	      std::vector<std::string> l;
+	      l.push_back(ii->init_name());
+	      inits_at_level[ii->priority()] = l;
+	    }
+	  else
+	    it->second.push_back(ii->init_name());
+	}
+    }
+  this->write_c_string(";\n");
+
+  // Create the init graph. Start by populating the graph with
+  // all the edges we inherited from imported packages.
+  populate_init_graph(&init_graph, imported_init_fns, init_idx);
+
+  // Now add edges from the local init function to each of the
+  // imported fcns.
+  if (!import_init_fn.empty())
+    {
+      unsigned src = 0;
+      go_assert(init_idx[import_init_fn] == 0);
+      for (Import_init_set::const_iterator p = imported_init_fns.begin();
+           p != imported_init_fns.end();
+           ++p)
+	{
+          const Import_init* ii = *p;
+	  unsigned sink = init_idx[ii->init_name()];
+	  add_init_graph_edge(&init_graph, src, sink);
+	}
+    }
+
+  // In the scenario where one or more of the packages we imported
+  // was written with the legacy export data format, add dummy edges
+  // to capture the priority relationships. Here is a package import
+  // graph as an example:
+  //
+  //       *A
+  //       /|
+  //      / |
+  //     B  *C
+  //       /|
+  //      / |
+  //    *D *E
+  //     | /|
+  //     |/ |
+  //    *F  *G
+  //
+  // Let's suppose that the object for package "C" is from an old
+  // gccgo, e.g. it has the old export data format. All other
+  // packages are compiled with the new compiler and have the new
+  // format. Packages with *'s have init functions. The scenario is
+  // that we're compiling a package "A"; during this process we'll
+  // read the export data for "C". It should look something like
+  //
+  //   init F F..import 1 G G..import 1 D D..import 2 E E..import 2;
+  //
+  // To capture this information and convey it to the consumers of
+  // "A", the code below adds edges to the graph from each priority K
+  // function to every priority K-1 function for appropriate values
+  // of K. This will potentially add more edges than we need (for
+  // example, an edge from D to G), but given that we don't expect
+  // to see large numbers of old objects, this will hopefully be OK.
+
+  if (inits_at_level.size() > 0)
+    {
+      for (level_map::reverse_iterator it = inits_at_level.rbegin();
+           it != inits_at_level.rend(); ++it)
+	{
+	  int level = it->first;
+	  if (level < 2) break;
+	  const std::vector<std::string>& fcns_at_level = it->second;
+	  for (std::vector<std::string>::const_iterator sit =
+	           fcns_at_level.begin();
+	       sit != fcns_at_level.end(); ++sit)
+	    {
+	      unsigned src = init_idx[*sit];
+	      level_map::iterator it2 = inits_at_level.find(level - 1);
+	      if (it2 != inits_at_level.end())
+		{
+		  const std::vector<std::string> fcns_at_lm1 = it2->second;
+		  for (std::vector<std::string>::const_iterator mit =
+		           fcns_at_lm1.begin();
+		       mit != fcns_at_lm1.end(); ++mit)
+		    {
+		      unsigned sink = init_idx[*mit];
+		      add_init_graph_edge(&init_graph, src, sink);
+		    }
+		}
+	    }
+	}
+    }
+
+  // Write out the resulting graph.
+  this->write_c_string("init_graph");
+  for (Init_graph::const_iterator ki = init_graph.begin();
+       ki != init_graph.end(); ++ki)
+    {
+      unsigned src = ki->first;
+      const std::set<unsigned>& successors = ki->second;
+      for (std::set<unsigned>::const_iterator vi = successors.begin();
+           vi != successors.end(); ++vi)
+	{
+	  this->write_c_string(" ");
+	  this->write_unsigned(src);
+	  unsigned sink = (*vi);
+	  this->write_c_string(" ");
+	  this->write_unsigned(sink);
+	}
+    }
+  this->write_c_string(";\n");
+}
+
+// Write a name to the export stream.
+
+void
+Export::write_name(const std::string& name)
+{
+  if (name.empty())
+    this->write_c_string("?");
+  else
+    this->write_string(Gogo::message_name(name));
+}
+
+// Write an integer value to the export stream.
+
+void
+Export::write_int(int value)
+{
+  char buf[100];
+  snprintf(buf, sizeof buf, "%d", value);
+  this->write_c_string(buf);
+}
+
+// Write an integer value to the export stream.
+
+void
+Export::write_unsigned(unsigned value)
+{
+  char buf[100];
+  snprintf(buf, sizeof buf, "%u", 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.
+
+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_;
+
+  char buf[30];
+  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);
+
+      // It is possible that this type was imported indirectly, and is
+      // not in a package in the import list.  If we have not
+      // mentioned this package before, write out the package name
+      // here so that any package importing this one will know it.
+      if (package != NULL
+	  && this->packages_.find(package) == this->packages_.end())
+	{
+	  this->write_c_string("\"");
+	  this->write_string(package->package_name());
+	  this->packages_.insert(package);
+	  this->write_c_string("\" ");
+	}
+
+      // 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.
+
+void
+Export::write_escape(std::string* note)
+{
+  if (note != NULL && *note != "esc:0x0")
+    {
+      this->write_c_string(" ");
+      char buf[50];
+      go_assert(note->find("esc:") != std::string::npos);
+      snprintf(buf, sizeof buf, "<%s>", note->c_str());
+      this->write_c_string(buf);
+    }
+}
+
+// Add the builtin types to the export table.
+
+void
+Export::register_builtin_types(Gogo* gogo)
+{
+  this->register_builtin_type(gogo, "int8", BUILTIN_INT8);
+  this->register_builtin_type(gogo, "int16", BUILTIN_INT16);
+  this->register_builtin_type(gogo, "int32", BUILTIN_INT32);
+  this->register_builtin_type(gogo, "int64", BUILTIN_INT64);
+  this->register_builtin_type(gogo, "uint8", BUILTIN_UINT8);
+  this->register_builtin_type(gogo, "uint16", BUILTIN_UINT16);
+  this->register_builtin_type(gogo, "uint32", BUILTIN_UINT32);
+  this->register_builtin_type(gogo, "uint64", BUILTIN_UINT64);
+  this->register_builtin_type(gogo, "float32", BUILTIN_FLOAT32);
+  this->register_builtin_type(gogo, "float64", BUILTIN_FLOAT64);
+  this->register_builtin_type(gogo, "complex64", BUILTIN_COMPLEX64);
+  this->register_builtin_type(gogo, "complex128", BUILTIN_COMPLEX128);
+  this->register_builtin_type(gogo, "int", BUILTIN_INT);
+  this->register_builtin_type(gogo, "uint", BUILTIN_UINT);
+  this->register_builtin_type(gogo, "uintptr", BUILTIN_UINTPTR);
+  this->register_builtin_type(gogo, "bool", BUILTIN_BOOL);
+  this->register_builtin_type(gogo, "string", BUILTIN_STRING);
+  this->register_builtin_type(gogo, "error", BUILTIN_ERROR);
+  this->register_builtin_type(gogo, "byte", BUILTIN_BYTE);
+  this->register_builtin_type(gogo, "rune", BUILTIN_RUNE);
+}
+
+// Register one builtin type in the export table.
+
+void
+Export::register_builtin_type(Gogo* gogo, const char* name, Builtin_code code)
+{
+  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));
+  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);
+    }
+}
+
+// Class Export::Stream.
+
+Export::Stream::Stream()
+{
+  this->sha1_helper_ = go_create_sha1_helper();
+  go_assert(this->sha1_helper_ != NULL);
+}
+
+Export::Stream::~Stream()
+{
+}
+
+// Write bytes to the stream.  This keeps a checksum of bytes as they
+// go by.
+
+void
+Export::Stream::write_and_sum_bytes(const char* bytes, size_t length)
+{
+  this->sha1_helper_->process_bytes(bytes, length);
+  this->do_write(bytes, length);
+}
+
+// Get the checksum.
+
+std::string
+Export::Stream::checksum()
+{
+  std::string rval = this->sha1_helper_->finish();
+  delete this->sha1_helper_;
+  return rval;
+}
+
+// Write the checksum string to the export data.
+
+void
+Export::Stream::write_checksum(const std::string& s)
+{
+  this->do_write(s.data(), s.length());
+}
+
+// Class Stream_to_section.
+
+Stream_to_section::Stream_to_section(Backend* backend)
+    : backend_(backend)
+{
+}
+
+// Write data to a section.
+
+void
+Stream_to_section::do_write(const char* bytes, size_t length)
+{
+  this->backend_->write_export_data (bytes, length);
+}