compiler: rewrite compiler directive support
authorIan Lance Taylor <ian@gcc.gnu.org>
Tue, 9 Aug 2016 14:05:17 +0000 (14:05 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 9 Aug 2016 14:05:17 +0000 (14:05 +0000)
    Rewrite the compiler directive support to recognize all the compiler
    directives implemented by the current gc compiler.  The directives other
    than go:linkname are now turned into GOPRAGMA flags attached to a
    function or function declaration.  The go:linkname directive is turned
    into a map attached to the Lex object.  No new directives are actually
    implemented yet, they are just recognized.

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

From-SVN: r239282

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

index 8a0c76f7fe6b4139b25029d9397e8b528dfd40b2..f14fdec06dc40e2ba949944b4ff071acaa706469 100644 (file)
@@ -1,4 +1,4 @@
-3b9c57a35370f26e6cf5839084e367e75e45ec97
+58be5c6c7d92182dec50a62c8e319d2d7aab12a4
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index 37760a75e6cde02417a7846a7a9168dd59cecfa8..b1dbb850200fe4736f3ef0c365bbbb818edb7ea3 100644 (file)
@@ -4439,7 +4439,7 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block,
   : type_(type), enclosing_(enclosing), results_(NULL),
     closure_var_(NULL), block_(block), location_(location), labels_(),
     local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL),
-    is_sink_(false), results_are_named_(false), nointerface_(false),
+    pragmas_(0), is_sink_(false), results_are_named_(false),
     is_unnamed_type_stub_method_(false), calls_recover_(false),
     is_recover_thunk_(false), has_recover_thunk_(false),
     calls_defer_retaddr_(false), is_type_specific_function_(false),
@@ -4511,6 +4511,24 @@ Function::update_result_variables()
     (*p)->result_var_value()->set_function(this);
 }
 
+// Whether this method should not be included in the type descriptor.
+
+bool
+Function::nointerface() const
+{
+  go_assert(this->is_method());
+  return (this->pragmas_ & GOPRAGMA_NOINTERFACE) != 0;
+}
+
+// Record that this method should not be included in the type
+// descriptor.
+
+void
+Function::set_nointerface()
+{
+  this->pragmas_ |= GOPRAGMA_NOINTERFACE;
+}
+
 // Return the closure variable, creating it if necessary.
 
 Named_object*
@@ -5042,7 +5060,8 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no)
       // We want to put a nointerface function into a unique section
       // because there is a good chance that the linker garbage
       // collection can discard it.
-      bool in_unique_section = this->in_unique_section_ || this->nointerface_;
+      bool in_unique_section = (this->in_unique_section_
+                               || (this->is_method() && this->nointerface()));
 
       Btype* functype = this->type_->get_backend_fntype(gogo);
       this->fndecl_ =
index 07234fbb06c7bd0c99b99d67041bd4fefcf646fa..bac9eb0c168afb55db206c78769073883d9f76dd 100644 (file)
@@ -975,23 +975,22 @@ class Function
   results_are_named() const
   { return this->results_are_named_; }
 
+  // Set the pragmas for this function.
+  void
+  set_pragmas(unsigned int pragmas)
+  {
+    this->pragmas_ = pragmas;
+  }
+
   // Whether this method should not be included in the type
   // descriptor.
   bool
-  nointerface() const
-  {
-    go_assert(this->is_method());
-    return this->nointerface_;
-  }
+  nointerface() const;
 
   // Record that this method should not be included in the type
   // descriptor.
   void
-  set_nointerface()
-  {
-    go_assert(this->is_method());
-    this->nointerface_ = true;
-  }
+  set_nointerface();
 
   // Record that this function is a stub method created for an unnamed
   // type.
@@ -1238,12 +1237,12 @@ class Function
   // distinguish the defer stack for one function from another.  This
   // is NULL unless we actually need a defer stack.
   Temporary_statement* defer_stack_;
+  // Pragmas for this function.  This is a set of GOPRAGMA bits.
+  unsigned int pragmas_;
   // True if this function is sink-named.  No code is generated.
   bool is_sink_ : 1;
   // True if the result variables are named.
   bool results_are_named_ : 1;
-  // True if this method should not be included in the type descriptor.
-  bool nointerface_ : 1;
   // True if this function is a stub method created for an unnamed
   // type.
   bool is_unnamed_type_stub_method_ : 1;
@@ -1305,7 +1304,7 @@ class Function_declaration
  public:
   Function_declaration(Function_type* fntype, Location location)
     : fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL),
-      fndecl_(NULL)
+      fndecl_(NULL), pragmas_(0)
   { }
 
   Function_type*
@@ -1325,6 +1324,13 @@ class Function_declaration
   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)
+  {
+    this->pragmas_ = pragmas;
+  }
+
   // Return an expression for the function descriptor, given the named
   // object for this function.  This may only be called for functions
   // without a closure.  This will be an immutable struct with one
@@ -1367,6 +1373,8 @@ class Function_declaration
   Expression* descriptor_;
   // The function decl if needed.
   Bfunction* fndecl_;
+  // Pragmas for this function.  This is a set of GOPRAGMA bits.
+  unsigned int pragmas_;
 };
 
 // A variable.
index 34a0811abef3f6e0e78c16f639ed370863fdb39e..94809d52b23d10eb4228293748490fbe7ab0fd6c 100644 (file)
@@ -442,7 +442,7 @@ Token::print(FILE* file) const
 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), saw_nointerface_(false),
+    lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0),
     extern_()
 {
   this->linebuf_ = new char[this->linebufsize_];
@@ -1676,29 +1676,47 @@ Lex::skip_cpp_comment()
   // //extern comment.
   this->extern_.clear();
 
-  const char* p = this->linebuf_ + this->lineoff_;
+  size_t lineoff = this->lineoff_;
+
+  const char* p = this->linebuf_ + lineoff;
   const char* pend = this->linebuf_ + this->linesize_;
 
-  // By convention, a C++ comment at the start of the line of the form
+  const char* pcheck = p;
+  bool saw_error = false;
+  while (pcheck < pend)
+    {
+      this->lineoff_ = pcheck - this->linebuf_;
+      unsigned int c;
+      bool issued_error;
+      pcheck = this->advance_one_utf8_char(pcheck, &c, &issued_error);
+      if (issued_error)
+       saw_error = true;
+    }
+
+  if (saw_error)
+    return;
+
+  // Recognize various magic comments at the start of a line.
+
+  if (lineoff != 2)
+    {
+      // Not at the start of the line.  (lineoff == 2 because of the
+      // two characters in "//").
+      return;
+    }
+
+  while (pend > p
+        && (pend[-1] == ' ' || pend[-1] == '\t'
+            || pend[-1] == '\r' || pend[-1] == '\n'))
+    --pend;
+
+  // A C++ comment at the start of the line of the form
   //   //line FILE:LINENO
   // is interpreted as setting the file name and line number of the
   // next source line.
-
-  if (this->lineoff_ == 2
-      && pend - p > 5
-      && memcmp(p, "line ", 5) == 0)
+  if (pend - p > 5 && memcmp(p, "line ", 5) == 0)
     {
       p += 5;
-
-      // Before finding FILE:LINENO, make sure line has valid characters.
-      const char* pcheck = p;
-      while (pcheck < pend)
-        {
-          unsigned int c;
-          bool issued_error;
-          pcheck = this->advance_one_utf8_char(pcheck, &c, &issued_error);
-        }
-
       while (p < pend && *p == ' ')
        ++p;
       const char* pcolon = static_cast<const char*>(memchr(p, ':', pend - p));
@@ -1726,6 +1744,7 @@ Lex::skip_cpp_comment()
              p = plend;
            }
        }
+      return;
     }
 
   // As a special gccgo extension, a C++ comment at the start of the
@@ -1734,37 +1753,129 @@ Lex::skip_cpp_comment()
   // which immediately precedes a function declaration means that the
   // external name of the function declaration is NAME.  This is
   // normally used to permit Go code to call a C function.
-  if (this->lineoff_ == 2
-      && pend - p > 7
-      && memcmp(p, "extern ", 7) == 0)
+  if (pend - p > 7 && memcmp(p, "extern ", 7) == 0)
     {
       p += 7;
       while (p < pend && (*p == ' ' || *p == '\t'))
        ++p;
-      const char* plend = pend;
-      while (plend > p
-            && (plend[-1] == ' ' || plend[-1] == '\t' || plend[-1] == '\n'))
-       --plend;
-      if (plend > p)
-       this->extern_ = std::string(p, plend - p);
+      if (pend > p)
+       this->extern_ = std::string(p, pend - p);
+      return;
     }
 
-  // For field tracking analysis: a //go:nointerface comment means
-  // that the next interface method should not be stored in the type
-  // descriptor.  This permits it to be discarded if it is not needed.
-  if (this->lineoff_ == 2
-      && pend - p > 14
-      && memcmp(p, "go:nointerface", 14) == 0)
-    this->saw_nointerface_ = true;
+  // All other special comments start with "go:".
 
-  while (p < pend)
+  if (pend - p < 4 || memcmp(p, "go:", 3) != 0)
+    return;
+
+  const char *ps = p + 3;
+  while (ps < pend && *ps != ' ' && *ps != '\t')
+    ++ps;
+  std::string verb = std::string(p, ps - p);
+
+  if (verb == "go:linkname")
     {
-      this->lineoff_ = p - this->linebuf_;
-      unsigned int c;
-      bool issued_error;
-      p = this->advance_one_utf8_char(p, &c, &issued_error);
-      if (issued_error)
-       this->extern_.clear();
+      // As in the gc compiler, set the external link name for a Go symbol.
+      std::string go_name;
+      std::string c_name;
+      if (ps < pend)
+       {
+         while (ps < pend && (*ps == ' ' || *ps == '\t'))
+           ++ps;
+         if (ps < pend)
+           {
+             const char* pg = ps;
+             while (ps < pend && *ps != ' ' && *ps != '\t')
+               ++ps;
+             if (ps < pend)
+               go_name = std::string(pg, ps - pg);
+             while (ps < pend && (*ps == ' ' || *ps == '\t'))
+               ++ps;
+           }
+         if (ps < pend)
+           {
+             const char* pc = ps;
+             while (ps < pend && *ps != ' ' && *ps != '\t')
+               ++ps;
+             if (ps <= pend)
+               c_name = std::string(pc, ps - pc);
+           }
+         if (ps != pend)
+           {
+             go_name.clear();
+             c_name.clear();
+           }
+       }
+      if (go_name.empty() || c_name.empty())
+       error_at(this->location(), "usage: //go:linkname localname linkname");
+      else
+       this->linknames_[go_name] = c_name;
+    }
+  else if (verb == "go:nointerface")
+    {
+      // For field tracking analysis: a //go:nointerface comment means
+      // that the next interface method should not be stored in the
+      // type descriptor.  This permits it to be discarded if it is
+      // not needed.
+      this->pragmas_ |= GOPRAGMA_NOINTERFACE;
+    }
+  else if (verb == "go:noescape")
+    {
+      // Applies to the next function declaration.  Any arguments do
+      // not escape.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_NOESCAPE;
+    }
+  else if (verb == "go:nosplit")
+    {
+      // Applies to the next function.  Do not split the stack when
+      // entering the function.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_NOSPLIT;
+    }
+  else if (verb == "go:noinline")
+    {
+      // Applies to the next function.  Do not inline the function.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_NOINLINE;
+    }
+  else if (verb == "go:systemstack")
+    {
+      // Applies to the next function.  It must run on the system stack.
+      // FIXME: Should only work when compiling the runtime package.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_SYSTEMSTACK;
+    }
+  else if (verb == "go:nowritebarrier")
+    {
+      // Applies to the next function.  If the function needs to use
+      // any write barriers, it should emit an error instead.
+      // FIXME: Should only work when compiling the runtime package.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_NOWRITEBARRIER;
+    }
+  else if (verb == "go:nowritebarrierrec")
+    {
+      // Applies to the next function.  If the function, or any
+      // function that it calls, needs to use any write barriers, it
+      // should emit an error instead.
+      // FIXME: Should only work when compiling the runtime package.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_NOWRITEBARRIERREC;
+    }
+  else if (verb == "go:cgo_unsafe_args")
+    {
+      // Applies to the next function.  Taking the address of any
+      // argument implies taking the address of all arguments.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_CGOUNSAFEARGS;
+    }
+  else if (verb == "go:uintptrescapes")
+    {
+      // Applies to the next function.  If an argument is a pointer
+      // converted to uintptr, then the pointer escapes.
+      // FIXME: Not implemented.
+      this->pragmas_ |= GOPRAGMA_UINTPTRESCAPES;
     }
 }
 
index 3e0152aaf2d76ce4e056a0a6d4dc1b4474900c5a..2f14136a525f14a0261cfc8021a2d59c3a8e443b 100644 (file)
@@ -49,6 +49,24 @@ enum Keyword
   KEYWORD_VAR
 };
 
+// Pragmas built from magic comments and recorded for functions.
+// These are used as bits in a bitmask.
+// The set of values is intended to be the same as the gc compiler.
+
+enum GoPragma
+{
+  GOPRAGMA_NOINTERFACE = 1 << 0,       // Method not in type descriptor.
+  GOPRAGMA_NOESCAPE = 1 << 1,          // Args do not escape.
+  GOPRAGMA_NORACE = 1 << 2,            // No race detector.
+  GOPRAGMA_NOSPLIT = 1 << 3,           // Do not split stack.
+  GOPRAGMA_NOINLINE = 1 << 4,          // Do not inline.
+  GOPRAGMA_SYSTEMSTACK = 1 << 5,       // Must run on system stack.
+  GOPRAGMA_NOWRITEBARRIER = 1 << 6,    // No write barriers.
+  GOPRAGMA_NOWRITEBARRIERREC = 1 << 7, // No write barriers here or callees.
+  GOPRAGMA_CGOUNSAFEARGS = 1 << 8,     // Pointer to arg is pointer to all.
+  GOPRAGMA_UINTPTRESCAPES = 1 << 9     // uintptr(p) escapes.
+};
+
 // A token returned from the lexer.
 
 class Token
@@ -348,13 +366,12 @@ class Lex
   extern_name() const
   { return this->extern_; }
 
-  // Return whether we have seen a //go:nointerface comment, clearing
-  // the flag.
-  bool
-  get_and_clear_nointerface()
+  // Return the current set of pragmas, and clear them.
+  unsigned int
+  get_and_clear_pragmas()
   {
-    bool ret = this->saw_nointerface_;
-    this->saw_nointerface_ = false;
+    unsigned int ret = this->pragmas_;
+    this->pragmas_ = 0;
     return ret;
   }
 
@@ -492,11 +509,13 @@ class Lex
   size_t lineno_;
   // Whether to add a semicolon if we see a newline now.
   bool add_semi_at_eol_;
-  // Whether we just saw a magic go:nointerface comment.
-  bool saw_nointerface_;
+  // Pragmas for the next function, from magic comments.
+  unsigned int pragmas_;
   // 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_;
 };
 
 #endif // !defined(GO_LEX_H)
index d9f20401defbdcfd1e9ee6f06070ef8e8d5bfbc4..cb7f9664aaf547fff59f59d2a07f794448683f79 100644 (file)
@@ -1305,10 +1305,10 @@ Parse::declaration()
 {
   const Token* token = this->peek_token();
 
-  bool saw_nointerface = this->lex_->get_and_clear_nointerface();
-  if (saw_nointerface && !token->is_keyword(KEYWORD_FUNC))
+  unsigned int pragmas = this->lex_->get_and_clear_pragmas();
+  if (pragmas != 0 && !token->is_keyword(KEYWORD_FUNC))
     warning_at(token->location(), 0,
-              "ignoring magic //go:nointerface comment before non-method");
+              "ignoring magic comment before non-function");
 
   if (token->is_keyword(KEYWORD_CONST))
     this->const_decl();
@@ -1317,7 +1317,7 @@ Parse::declaration()
   else if (token->is_keyword(KEYWORD_VAR))
     this->var_decl();
   else if (token->is_keyword(KEYWORD_FUNC))
-    this->function_decl(saw_nointerface);
+    this->function_decl(pragmas);
   else
     {
       error_at(this->location(), "expected declaration");
@@ -2236,13 +2236,12 @@ Parse::simple_var_decl_or_assignment(const std::string& name,
 //                    __asm__ "(" string_lit ")" .
 // This extension means a function whose real name is the identifier
 // inside the asm.  This extension will be removed at some future
-// date.  It has been replaced with //extern comments.
-
-// SAW_NOINTERFACE is true if we saw a magic //go:nointerface comment,
-// which means that we omit the method from the type descriptor.
+// date.  It has been replaced with //extern or //go:linkname comments.
+//
+// PRAGMAS is a bitset of magic comments.
 
 void
-Parse::function_decl(bool saw_nointerface)
+Parse::function_decl(unsigned int pragmas)
 {
   go_assert(this->peek_token()->is_keyword(KEYWORD_FUNC));
   Location location = this->location();
@@ -2257,12 +2256,6 @@ Parse::function_decl(bool saw_nointerface)
       rec = this->receiver();
       token = this->peek_token();
     }
-  else if (saw_nointerface)
-    {
-      warning_at(location, 0,
-                "ignoring magic //go:nointerface comment before non-method");
-      saw_nointerface = false;
-    }
 
   if (!token->is_identifier())
     {
@@ -2320,7 +2313,69 @@ Parse::function_decl(bool saw_nointerface)
                                                     semi_loc));
     }
 
-  if (!this->peek_token()->is_op(OPERATOR_LCURLY))
+  static struct {
+    unsigned int bit;
+    const char* name;
+    bool decl_ok;
+    bool func_ok;
+    bool method_ok;
+  } pragma_check[] =
+      {
+       { GOPRAGMA_NOINTERFACE, "nointerface", false, false, true },
+       { GOPRAGMA_NOESCAPE, "noescape", true, false, false },
+       { GOPRAGMA_NORACE, "norace", false, true, true },
+       { GOPRAGMA_NOSPLIT, "nosplit", false, true, true },
+       { GOPRAGMA_NOINLINE, "noinline", false, true, true },
+       { GOPRAGMA_SYSTEMSTACK, "systemstack", false, true, true },
+       { GOPRAGMA_NOWRITEBARRIER, "nowritebarrier", false, true, true },
+       { GOPRAGMA_NOWRITEBARRIERREC, "nowritebarrierrec", false, true, true },
+       { GOPRAGMA_CGOUNSAFEARGS, "cgo_unsafe_args", false, true, true },
+       { GOPRAGMA_UINTPTRESCAPES, "uintptrescapes", true, true, true },
+      };
+
+  bool is_decl = !this->peek_token()->is_op(OPERATOR_LCURLY);
+  if (pragmas != 0)
+    {
+      for (size_t i = 0;
+          i < sizeof(pragma_check) / sizeof(pragma_check[0]);
+          ++i)
+       {
+         if ((pragmas & pragma_check[i].bit) == 0)
+           continue;
+
+         if (is_decl)
+           {
+             if (pragma_check[i].decl_ok)
+               continue;
+             warning_at(location, 0,
+                        ("ignoring magic //go:%s comment "
+                         "before declaration"),
+                        pragma_check[i].name);
+           }
+         else if (rec == NULL)
+           {
+             if (pragma_check[i].func_ok)
+               continue;
+             warning_at(location, 0,
+                        ("ignoring magic //go:%s comment "
+                         "before function definition"),
+                        pragma_check[i].name);
+           }
+         else
+           {
+             if (pragma_check[i].method_ok)
+               continue;
+             warning_at(location, 0,
+                        ("ignoring magic //go:%s comment "
+                         "before method definition"),
+                        pragma_check[i].name);
+           }
+
+         pragmas &= ~ pragma_check[i].bit;
+       }
+    }
+
+  if (is_decl)
     {
       if (named_object == NULL)
        {
@@ -2353,10 +2408,8 @@ Parse::function_decl(bool saw_nointerface)
            }
        }
 
-      if (saw_nointerface)
-       warning_at(location, 0,
-                  ("ignoring magic //go:nointerface comment "
-                   "before declaration"));
+      if (pragmas != 0 && named_object->is_function_declaration())
+       named_object->func_declaration_value()->set_pragmas(pragmas);
     }
   else
     {
@@ -2372,10 +2425,11 @@ Parse::function_decl(bool saw_nointerface)
       named_object = this->gogo_->start_function(name, fntype, true, location);
       Location end_loc = this->block();
       this->gogo_->finish_function(end_loc);
-      if (saw_nointerface
+
+      if (pragmas != 0
          && !this->is_erroneous_function_
          && named_object->is_function())
-       named_object->func_value()->set_nointerface();
+       named_object->func_value()->set_pragmas(pragmas);
       this->is_erroneous_function_ = hold_is_erroneous_function;
     }
 }
index 734071a73773a8a9f0a4ab9ae589f7a5d7d164be..a7118e77c17575b25d2709f27307980d96ab5eda 100644 (file)
@@ -209,7 +209,7 @@ class Parse
   void simple_var_decl_or_assignment(const std::string&, Location,
                                     bool may_be_composite_lit,
                                     Range_clause*, Type_switch*);
-  void function_decl(bool saw_nointerface);
+  void function_decl(unsigned int pragmas);
   Typed_identifier* receiver();
   Expression* operand(bool may_be_sink, bool *is_parenthesized);
   Expression* enclosing_var_reference(Named_object*, Named_object*,