compiler: support export/import of unsafe.Add/Slice

For golang/go#19367
For golang/go#40481

Change-Id: Id1aefd0696131842d480d9f9a5330c5ab221245a
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/340549
Trust: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
diff --git a/go/export.cc b/go/export.cc
index e99c680..3d11334 100644
--- a/go/export.cc
+++ b/go/export.cc
@@ -106,11 +106,12 @@
 {
  public:
   Collect_export_references(Export* exp,
+			    const std::map<std::string, Package*>& packages,
                             Unordered_set(Named_object*)* exports,
                             Unordered_set(const Package*)* imports)
     : Traverse(traverse_expressions
                | traverse_types),
-      exp_(exp), exports_(exports), imports_(imports),
+      exp_(exp), packages_(packages), exports_(exports), imports_(imports),
       inline_fcn_worklist_(NULL), exports_finalized_(false)
   { }
 
@@ -150,6 +151,8 @@
 
   // The exporter.
   Export* exp_;
+  // The list of packages known to this compilation.
+  const std::map<std::string, Package*>& packages_;
   // The set of named objects to export.
   Unordered_set(Named_object*)* exports_;
   // Set containing all directly and indirectly imported packages.
@@ -257,6 +260,24 @@
       return TRAVERSE_CONTINUE;
     }
 
+  const Call_expression* call = expr->call_expression();
+  if (call != NULL)
+    {
+      const Builtin_call_expression* bce = call->builtin_call_expression();
+      if (bce != NULL
+	  && (bce->code() == Builtin_call_expression::BUILTIN_ADD
+	      || bce->code() == Builtin_call_expression::BUILTIN_SLICE))
+	{
+	  // This is a reference to unsafe.Add or unsafe.Slice.  Make
+	  // sure we list the "unsafe" package in the imports and give
+	  // it a package index.
+	  const std::map<std::string, Package*>::const_iterator p =
+	    this->packages_.find("unsafe");
+	  go_assert(p != this->packages_.end());
+	  this->imports_->insert(p->second);
+	}
+    }
+
   return TRAVERSE_CONTINUE;
 }
 
@@ -589,7 +610,7 @@
   // Track all imported packages mentioned in export data.
   Unordered_set(const Package*) all_imports;
 
-  Collect_export_references collect(this, &exports, &all_imports);
+  Collect_export_references collect(this, packages, &exports, &all_imports);
 
   // Walk the set of inlinable routine bodies collected above. This
   // can potentially expand the exports set.
@@ -1274,6 +1295,25 @@
   return index;
 }
 
+// Return the index of the "unsafe" package.
+
+int
+Export::unsafe_package_index() const
+{
+  for (Unordered_map(const Package*, int)::const_iterator p =
+	 this->packages_.begin();
+       p != this->packages_.end();
+       ++p)
+    {
+      if (p->first->pkgpath() == "unsafe")
+	{
+	  go_assert(p->second != 0);
+	  return p->second;
+	}
+    }
+  go_unreachable();
+}
+
 // Return the index of a type.
 
 int
diff --git a/go/export.h b/go/export.h
index c93bced..1f61343 100644
--- a/go/export.h
+++ b/go/export.h
@@ -216,6 +216,11 @@
   int
   package_index(const Package* p) const;
 
+  // Return the index of the "unsafe" package, which must be one of
+  // the exported packages.
+  int
+  unsafe_package_index() const;
+
  private:
   Export(const Export&);
   Export& operator=(const Export&);
@@ -377,6 +382,11 @@
   package_index(const Package* p) const
   { return this->exp_->package_index(p); }
 
+  // Return the index of the "unsafe" package.
+  int
+  unsafe_package_index() const
+  { return this->exp_->unsafe_package_index(); }
+
   // Record a temporary statement and return its index.
   unsigned int
   record_temporary(const Temporary_statement*);
diff --git a/go/expressions.cc b/go/expressions.cc
index 33177a7..f462b0e 100644
--- a/go/expressions.cc
+++ b/go/expressions.cc
@@ -11039,6 +11039,14 @@
       // A trailing space lets us reliably identify the end of the number.
       efb->write_c_string(" ");
     }
+  else if (this->code_ == BUILTIN_ADD || this->code_ == BUILTIN_SLICE)
+    {
+      char buf[50];
+      snprintf(buf, sizeof buf, "<p%d>%s", efb->unsafe_package_index(),
+	       (this->code_ == BUILTIN_ADD ? "Add" : "Slice"));
+      efb->write_c_string(buf);
+      this->export_arguments(efb);
+    }
   else
     {
       const char *s = NULL;
diff --git a/go/expressions.h b/go/expressions.h
index 79a8785..9f8f4e9 100644
--- a/go/expressions.h
+++ b/go/expressions.h
@@ -732,6 +732,10 @@
   call_expression()
   { return this->convert<Call_expression, EXPRESSION_CALL>(); }
 
+  const Call_expression*
+  call_expression() const
+  { return this->convert<const Call_expression, EXPRESSION_CALL>(); }
+
   // If this is a call_result expression, return the Call_result_expression
   // structure.  Otherwise, return NULL.  This is a controlled dynamic
   // cast.
@@ -2460,13 +2464,16 @@
 
   // Whether this is a call to builtin function.
   virtual bool
-  is_builtin()
+  is_builtin() const
   { return false; }
 
   // Convert to a Builtin_call_expression, or return NULL.
   inline Builtin_call_expression*
   builtin_call_expression();
 
+  inline const Builtin_call_expression*
+  builtin_call_expression() const;
+
  protected:
   int
   do_traverse(Traverse*);
@@ -2625,12 +2632,12 @@
     };
 
   Builtin_function_code
-  code()
+  code() const
   { return this->code_; }
 
   // This overrides Call_expression::is_builtin.
   bool
-  is_builtin()
+  is_builtin() const
   { return true; }
 
   // Return whether EXPR, of array type, is a constant if passed to
@@ -2726,6 +2733,14 @@
           : NULL);
 }
 
+inline const Builtin_call_expression*
+Call_expression::builtin_call_expression() const
+{
+  return (this->is_builtin()
+          ? static_cast<const Builtin_call_expression*>(this)
+          : NULL);
+}
+
 // A single result from a call which returns multiple results.
 
 class Call_result_expression : public Expression
diff --git a/go/gogo.h b/go/gogo.h
index c49bc92..9ffd120 100644
--- a/go/gogo.h
+++ b/go/gogo.h
@@ -533,6 +533,10 @@
   register_package(const std::string& pkgpath,
 		   const std::string& pkgpath_symbol, Location);
 
+  // Add the unsafe bindings to the unsafe package.
+  void
+  add_unsafe_bindings(Package*);
+
   // Look up a package by pkgpath, and return its pkgpath_symbol.
   std::string
   pkgpath_symbol_for_package(const std::string&);
diff --git a/go/import.cc b/go/import.cc
index f671416..6a5491b 100644
--- a/go/import.cc
+++ b/go/import.cc
@@ -497,6 +497,9 @@
   p->set_package_name(package_name, this->location());
 
   this->packages_.push_back(p);
+
+  if (pkgpath == "unsafe")
+    this->gogo_->add_unsafe_bindings(p);
 }
 
 // Read an indirectimport line.
@@ -515,6 +518,9 @@
   p->set_package_name(package_name, this->location());
 
   this->packages_.push_back(p);
+
+  if (pkgpath == "unsafe")
+    this->gogo_->add_unsafe_bindings(p);
 }
 
 // Read the list of import control functions and/or init graph.
diff --git a/go/unsafe.cc b/go/unsafe.cc
index 18bd99e..c4a9346 100644
--- a/go/unsafe.cc
+++ b/go/unsafe.cc
@@ -10,15 +10,12 @@
 #include "types.h"
 #include "gogo.h"
 
-// Set up the builtin unsafe package.  This should probably be driven
-// by a table.
+// Set up the builtin unsafe package.
 
 void
 Gogo::import_unsafe(const std::string& local_name, bool is_local_name_exported,
 		    Location location)
 {
-  Location bloc = Linemap::predeclared_location();
-
   bool add_to_globals;
   Package* package = this->add_imported_package("unsafe", local_name,
 						is_local_name_exported,
@@ -34,10 +31,40 @@
   package->set_location(location);
   this->imports_.insert(std::make_pair("unsafe", package));
 
+  this->add_unsafe_bindings(package);
+
+  Named_object* pointer_no = package->bindings()->lookup_local("Pointer");
+  pointer_no->type_value()->set_is_visible();
+
+  if (add_to_globals)
+    {
+      Bindings* bindings = package->bindings();
+      for (Bindings::const_declarations_iterator p =
+	     bindings->begin_declarations();
+	   p != bindings->end_declarations();
+	   ++p)
+	this->add_dot_import_object(p->second);
+    }
+}
+
+// Add the unsafe bindings to the Package object.  This should
+// probably be driven by a table.
+
+void
+Gogo::add_unsafe_bindings(Package* package)
+{
   Bindings* bindings = package->bindings();
 
+  if (bindings->lookup_local("Sizeof") != NULL)
+    {
+      // Already done by an earlier import.
+      return;
+    }
+
+  Location bloc = Linemap::predeclared_location();
+
   // The type may have already been created by an import.
-  Named_object* no = package->bindings()->lookup("Pointer");
+  Named_object* no = bindings->lookup("Pointer");
   if (no == NULL)
     {
       Type* type = Type::make_pointer_type(Type::make_void_type());
@@ -49,11 +76,12 @@
       go_assert(no->package() == package);
       go_assert(no->is_type());
       go_assert(no->type_value()->is_unsafe_pointer_type());
-      no->type_value()->set_is_visible();
     }
   Named_type* pointer_type = no->type_value();
-  if (add_to_globals)
-    this->add_named_type(pointer_type);
+
+  // This may be called during an import, so the type may not be
+  // visible yet.
+  pointer_type->clear_is_visible();
 
   Type* uintptr_type = Type::lookup_integer_type("uintptr");
 
@@ -62,9 +90,7 @@
   results->push_back(Typed_identifier("", uintptr_type, bloc));
   Function_type* fntype = Type::make_function_type(NULL, NULL, results, bloc);
   fntype->set_is_builtin();
-  no = bindings->add_function_declaration("Sizeof", package, fntype, bloc);
-  if (add_to_globals)
-    this->add_dot_import_object(no);
+  bindings->add_function_declaration("Sizeof", package, fntype, bloc);
 
   // Offsetof.
   results = new Typed_identifier_list;
@@ -72,9 +98,7 @@
   fntype = Type::make_function_type(NULL, NULL, results, bloc);
   fntype->set_is_varargs();
   fntype->set_is_builtin();
-  no = bindings->add_function_declaration("Offsetof", package, fntype, bloc);
-  if (add_to_globals)
-    this->add_dot_import_object(no);
+  bindings->add_function_declaration("Offsetof", package, fntype, bloc);
 
   // Alignof.
   results = new Typed_identifier_list;
@@ -82,25 +106,19 @@
   fntype = Type::make_function_type(NULL, NULL, results, bloc);
   fntype->set_is_varargs();
   fntype->set_is_builtin();
-  no = bindings->add_function_declaration("Alignof", package, fntype, bloc);
-  if (add_to_globals)
-    this->add_dot_import_object(no);
+  bindings->add_function_declaration("Alignof", package, fntype, bloc);
 
   // Add.
   results = new Typed_identifier_list;
   results->push_back(Typed_identifier("", pointer_type, bloc));
   fntype = Type::make_function_type(NULL, NULL, results, bloc);
   fntype->set_is_builtin();
-  no = bindings->add_function_declaration("Add", package, fntype, bloc);
-  if (add_to_globals)
-    this->add_dot_import_object(no);
+  bindings->add_function_declaration("Add", package, fntype, bloc);
 
   // Slice.
   fntype = Type::make_function_type(NULL, NULL, NULL, bloc);
   fntype->set_is_builtin();
-  no = bindings->add_function_declaration("Slice", package, fntype, bloc);
-  if (add_to_globals)
-    this->add_dot_import_object(no);
+  bindings->add_function_declaration("Slice", package, fntype, bloc);
 
   if (!this->imported_unsafe_)
     {