On Mon, Aug 2, 2021 at 3:23 PM Ian Lance Taylor <i...@golang.org> wrote:
>
> The upcoming Go 1.17 release adds two new functions to the unsafe
> package: unsafe.Add and unsafe.Slice.  These functions must be
> implemented in the compiler.  This patch implements them for gccgo.
> Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
> to mainline.

This patch makes unsafe.Add and unsafe.Slice work when they appear in
functions that can be inlined across packages.  Bootstrapped and ran
Go testsuite on x86_64-pc-linux-gnu.  Committed to mainline.

Ian
b00c45d66d337e18a02674075ca792fd38094acf
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE
index 9ed527f7eb4..b983fdab35c 100644
--- a/gcc/go/gofrontend/MERGE
+++ b/gcc/go/gofrontend/MERGE
@@ -1,4 +1,4 @@
-747f3a2d78c073e9b03dd81914d0edb7ddc5be14
+d5d51242efc432fa62d4e9b141b01c280af32d19
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc
index e99c680f709..3d11334884d 100644
--- a/gcc/go/gofrontend/export.cc
+++ b/gcc/go/gofrontend/export.cc
@@ -106,11 +106,12 @@ class Collect_export_references : public Traverse
 {
  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 @@ class Collect_export_references : public Traverse
 
   // 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 @@ Collect_export_references::expression(Expression** pexpr)
       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 @@ Export::export_globals(const std::string& package_name,
   // 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 @@ Export::package_index(const Package* pkg) const
   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/gcc/go/gofrontend/export.h b/gcc/go/gofrontend/export.h
index c93bced4eb4..1f613434cab 100644
--- a/gcc/go/gofrontend/export.h
+++ b/gcc/go/gofrontend/export.h
@@ -216,6 +216,11 @@ class Export : public String_dump
   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 @@ class Export_function_body : public String_dump
   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/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc
index 33177a709ab..f462b0e2a34 100644
--- a/gcc/go/gofrontend/expressions.cc
+++ b/gcc/go/gofrontend/expressions.cc
@@ -11039,6 +11039,14 @@ 
Builtin_call_expression::do_export(Export_function_body* efb) const
       // 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/gcc/go/gofrontend/expressions.h b/gcc/go/gofrontend/expressions.h
index 79a8785b69d..9f8f4e9255b 100644
--- a/gcc/go/gofrontend/expressions.h
+++ b/gcc/go/gofrontend/expressions.h
@@ -732,6 +732,10 @@ class Expression
   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 @@ class Call_expression : public Expression
 
   // 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 @@ class Builtin_call_expression : public Call_expression
     };
 
   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 @@ Call_expression::builtin_call_expression()
           : 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/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h
index c49bc92b3e0..9ffd120290d 100644
--- a/gcc/go/gofrontend/gogo.h
+++ b/gcc/go/gofrontend/gogo.h
@@ -533,6 +533,10 @@ class Gogo
   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/gcc/go/gofrontend/import.cc b/gcc/go/gofrontend/import.cc
index f671416a423..6a5491be949 100644
--- a/gcc/go/gofrontend/import.cc
+++ b/gcc/go/gofrontend/import.cc
@@ -497,6 +497,9 @@ Import::read_one_import()
   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 @@ Import::read_one_indirect_import()
   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/gcc/go/gofrontend/unsafe.cc b/gcc/go/gofrontend/unsafe.cc
index 18bd99eca15..c4a9346ca3e 100644
--- a/gcc/go/gofrontend/unsafe.cc
+++ b/gcc/go/gofrontend/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 @@ Gogo::import_unsafe(const std::string& local_name, bool 
is_local_name_exported,
   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 @@ Gogo::import_unsafe(const std::string& local_name, bool 
is_local_name_exported,
       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 @@ Gogo::import_unsafe(const std::string& local_name, bool 
is_local_name_exported,
   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 @@ Gogo::import_unsafe(const std::string& local_name, bool 
is_local_name_exported,
   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 @@ Gogo::import_unsafe(const std::string& local_name, bool 
is_local_name_exported,
   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_)
     {

Reply via email to