compiler: implement go:linkname compiler directive
authorIan Lance Taylor <ian@gcc.gnu.org>
Tue, 9 Aug 2016 23:08:32 +0000 (23:08 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 9 Aug 2016 23:08:32 +0000 (23:08 +0000)
    Implement the go:linkname compiler directive for functions (both
    function definitions and function declarations).  At least for now, give
    an error for code that tries to use go:linkname with a non-function.

    Reviewed-on: https://go-review.googlesource.com/26651

From-SVN: r239311

gcc/go/gofrontend/MERGE
gcc/go/gofrontend/go.cc
gcc/go/gofrontend/gogo.cc
gcc/go/gofrontend/gogo.h
gcc/go/gofrontend/lex.cc
gcc/go/gofrontend/lex.h

index b23a1f55bd11096dc2e38dccb9bf5b82358d05d9..d4c7a90d8b75345ea94fb5d0ec7d7b9cd81dfd21 100644 (file)
@@ -1,4 +1,4 @@
-d3636ca659ed7eed6d2e1cedfa0adccc6d81c07d
+85a9c6992d9660e36972c279a5252fd9591bb765
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index a0df2b801bddbac95b36ff16e21c279b42e456ab..ee87141efd20f08c2c46dfd698f2520654e90a7f 100644 (file)
@@ -52,6 +52,7 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
 {
   go_assert(filename_count > 0);
 
+  Lex::Linknames all_linknames;
   for (unsigned int i = 0; i < filename_count; ++i)
     {
       if (i > 0)
@@ -76,6 +77,21 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
 
       if (strcmp(filename, "-") != 0)
        fclose(file);
+
+      Lex::Linknames* linknames = lexer.get_and_clear_linknames();
+      if (linknames != NULL)
+       {
+         if (!::gogo->current_file_imported_unsafe())
+           {
+             for (Lex::Linknames::const_iterator p = linknames->begin();
+                  p != linknames->end();
+                  ++p)
+               error_at(p->second.loc,
+                        ("//go:linkname only allowed in Go files that "
+                         "import \"unsafe\""));
+           }
+         all_linknames.insert(linknames->begin(), linknames->end());
+       }
     }
 
   ::gogo->linemap()->stop();
@@ -86,6 +102,13 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
   // define them now.
   ::gogo->define_global_names();
 
+  // Apply any go:linkname directives.
+  for (Lex::Linknames::const_iterator p = all_linknames.begin();
+       p != all_linknames.end();
+       ++p)
+    ::gogo->add_linkname(p->first, p->second.is_exported, p->second.ext_name,
+                        p->second.loc);
+
   // Finalize method lists and build stub methods for named types.
   ::gogo->finalize_methods();
 
index b1dbb850200fe4736f3ef0c365bbbb818edb7ea3..a72ef9520ecc934542ba4728ffab07e1a4a810d9 100644 (file)
@@ -32,6 +32,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
     file_block_names_(),
     imports_(),
     imported_unsafe_(false),
+    current_file_imported_unsafe_(false),
     packages_(),
     init_functions_(),
     var_deps_(),
@@ -449,6 +450,7 @@ Gogo::import_package(const std::string& filename,
   if (filename == "unsafe")
     {
       this->import_unsafe(local_name, is_local_name_exported, location);
+      this->current_file_imported_unsafe_ = true;
       return;
     }
 
@@ -2000,6 +2002,29 @@ Gogo::add_dot_import_object(Named_object* no)
   this->current_bindings()->add_named_object(no);
 }
 
+// Add a linkname.  This implements the go:linkname compiler directive.
+// We only support this for functions and function declarations.
+
+void
+Gogo::add_linkname(const std::string& go_name, bool is_exported,
+                  const std::string& ext_name, Location loc)
+{
+  Named_object* no =
+    this->package_->bindings()->lookup(this->pack_hidden_name(go_name,
+                                                             is_exported));
+  if (no == NULL)
+    error_at(loc, "%s is not defined", go_name.c_str());
+  else if (no->is_function())
+    no->func_value()->set_asm_name(ext_name);
+  else if (no->is_function_declaration())
+    no->func_declaration_value()->set_asm_name(ext_name);
+  else
+    error_at(loc,
+            ("%s is not a function; "
+             "//go:linkname is only supported for functions"),
+            go_name.c_str());
+}
+
 // Mark all local variables used.  This is used when some types of
 // parse error occur.
 
@@ -2183,6 +2208,8 @@ Gogo::clear_file_scope()
         }
       package->clear_used();
     }
+
+  this->current_file_imported_unsafe_ = false;
 }
 
 // Queue up a type specific function for later writing.  These are
@@ -5034,6 +5061,12 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
             }
         }
 
+      if (!this->asm_name_.empty())
+       {
+         asm_name = this->asm_name_;
+         is_visible = true;
+       }
+
       // If a function calls the predeclared recover function, we
       // can't inline it, because recover behaves differently in a
       // function passed directly to defer.  If this is a recover
index bac9eb0c168afb55db206c78769073883d9f76dd..d2262e39111e9a11e70bfe66c60a29dcdc7e3eec 100644 (file)
@@ -428,6 +428,12 @@ class Gogo
   add_file_block_name(const std::string& name, Location location)
   { this->file_block_names_[name] = location; }
 
+  // Add a linkname, from the go:linkname compiler directive.  This
+  // changes the externally visible name of go_name to be ext_name.
+  void
+  add_linkname(const std::string& go_name, bool is_exported,
+              const std::string& ext_name, Location location);
+
   // Mark all local variables in current bindings as used.  This is
   // used when there is a parse error to avoid useless errors.
   void
@@ -463,6 +469,11 @@ class Gogo
   set_need_init_fn()
   { this->need_init_fn_ = true; }
 
+  // Return whether the current file imported the unsafe package.
+  bool
+  current_file_imported_unsafe() const
+  { return this->current_file_imported_unsafe_; }
+
   // Clear out all names in file scope.  This is called when we start
   // parsing a new file.
   void
@@ -760,6 +771,8 @@ class Gogo
   Imports imports_;
   // Whether the magic unsafe package was imported.
   bool imported_unsafe_;
+  // Whether the magic unsafe package was imported by the current file.
+  bool current_file_imported_unsafe_;
   // Mapping from package names we have seen to packages.  This does
   // not include the package we are compiling.
   Packages packages_;
@@ -975,6 +988,11 @@ class Function
   results_are_named() const
   { return this->results_are_named_; }
 
+  // Set the assembler name.
+  void
+  set_asm_name(const std::string& asm_name)
+  { this->asm_name_ = asm_name; }
+
   // Set the pragmas for this function.
   void
   set_pragmas(unsigned int pragmas)
@@ -1229,6 +1247,9 @@ class Function
   Labels labels_;
   // The number of local types defined in this function.
   unsigned int local_type_count_;
+  // The assembler name: this is the name that will be put in the object file.
+  // Set by the go:linkname compiler directive.  This is normally empty.
+  std::string asm_name_;
   // The function descriptor, if any.
   Expression* descriptor_;
   // The function decl.
index 94809d52b23d10eb4228293748490fbe7ab0fd6c..9498c7dfc0b9ecaa1415d72f3621ec3e0d1d76e1 100644 (file)
@@ -443,7 +443,7 @@ Lex::Lex(const char* input_file_name, FILE* input_file, Linemap* linemap)
   : input_file_name_(input_file_name), input_file_(input_file),
     linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0),
     lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0),
-    extern_()
+    extern_(), linknames_(NULL)
 {
   this->linebuf_ = new char[this->linebufsize_];
   this->linemap_->start_file(input_file_name, 0);
@@ -1676,6 +1676,7 @@ Lex::skip_cpp_comment()
   // //extern comment.
   this->extern_.clear();
 
+  Location loc = this->location();
   size_t lineoff = this->lineoff_;
 
   const char* p = this->linebuf_ + lineoff;
@@ -1777,7 +1778,8 @@ Lex::skip_cpp_comment()
     {
       // As in the gc compiler, set the external link name for a Go symbol.
       std::string go_name;
-      std::string c_name;
+      std::string ext_name;
+      bool is_exported = false;
       if (ps < pend)
        {
          while (ps < pend && (*ps == ' ' || *ps == '\t'))
@@ -1785,6 +1787,12 @@ Lex::skip_cpp_comment()
          if (ps < pend)
            {
              const char* pg = ps;
+
+             unsigned int c;
+             bool issued_error;
+             ps = this->advance_one_utf8_char(ps, &c, &issued_error);
+             is_exported = Lex::is_unicode_uppercase(c);
+
              while (ps < pend && *ps != ' ' && *ps != '\t')
                ++ps;
              if (ps < pend)
@@ -1798,18 +1806,22 @@ Lex::skip_cpp_comment()
              while (ps < pend && *ps != ' ' && *ps != '\t')
                ++ps;
              if (ps <= pend)
-               c_name = std::string(pc, ps - pc);
+               ext_name = std::string(pc, ps - pc);
            }
          if (ps != pend)
            {
              go_name.clear();
-             c_name.clear();
+             ext_name.clear();
            }
        }
-      if (go_name.empty() || c_name.empty())
-       error_at(this->location(), "usage: //go:linkname localname linkname");
+      if (go_name.empty() || ext_name.empty())
+       error_at(loc, "usage: //go:linkname localname linkname");
       else
-       this->linknames_[go_name] = c_name;
+       {
+         if (this->linknames_ == NULL)
+           this->linknames_ = new Linknames();
+         (*this->linknames_)[go_name] = Linkname(ext_name, is_exported, loc);
+       }
     }
   else if (verb == "go:nointerface")
     {
index 2f14136a525f14a0261cfc8021a2d59c3a8e443b..5c4afb611b0afc23f6e275e1faf0e114876806d3 100644 (file)
@@ -375,6 +375,33 @@ class Lex
     return ret;
   }
 
+  struct Linkname
+  {
+    std::string ext_name;      // External name.
+    bool is_exported;          // Whether the internal name is exported.
+    Location loc;              // Location of go:linkname directive.
+
+    Linkname()
+      : ext_name(), is_exported(false), loc()
+    { }
+
+    Linkname(const std::string& ext_name_a, bool is_exported_a, Location loc_a)
+      : ext_name(ext_name_a), is_exported(is_exported_a), loc(loc_a)
+    { }
+  };
+
+  typedef std::map<std::string, Linkname> Linknames;
+
+  // Return the linknames seen so far, or NULL if none, and clear the
+  // set.  These are from go:linkname compiler directives.
+  Linknames*
+  get_and_clear_linknames()
+  {
+    Linknames* ret = this->linknames_;
+    this->linknames_ = NULL;
+    return ret;
+  }
+
   // Return whether the identifier NAME should be exported.  NAME is a
   // mangled name which includes only ASCII characters.
   static bool
@@ -514,8 +541,8 @@ class Lex
   // The external name to use for a function declaration, from a magic
   // //extern comment.
   std::string extern_;
-  // The list of //go:linkname comments.
-  std::map<std::string, std::string> linknames_;
+  // The list of //go:linkname comments, if any.
+  Linknames* linknames_;
 };
 
 #endif // !defined(GO_LEX_H)