This patch to the Go frontend rewrites the compiler directive support
to recognize all the compiler directives handled by the current gc
compiler.  They are now turned into flags attached to a function or
function declaration, or, for the case of go:linkname, into a map
attached to the Lex object.  No new directives are handled here, they
are just recognized for later support.  Bootstrapped and ran Go
testsuite on x86_64-pc-linux-gnu.  Committed to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE     (revision 239261)
+++ gcc/go/gofrontend/MERGE     (working copy)
@@ -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: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc   (revision 238653)
+++ gcc/go/gofrontend/gogo.cc   (working copy)
@@ -4439,7 +4439,7 @@ Function::Function(Function_type* type,
   : 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, N
       // 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: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h    (revision 239002)
+++ gcc/go/gofrontend/gogo.h    (working copy)
@@ -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: gcc/go/gofrontend/lex.cc
===================================================================
--- gcc/go/gofrontend/lex.cc    (revision 238653)
+++ gcc/go/gofrontend/lex.cc    (working copy)
@@ -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: gcc/go/gofrontend/lex.h
===================================================================
--- gcc/go/gofrontend/lex.h     (revision 238653)
+++ gcc/go/gofrontend/lex.h     (working copy)
@@ -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: gcc/go/gofrontend/parse.cc
===================================================================
--- gcc/go/gofrontend/parse.cc  (revision 238653)
+++ gcc/go/gofrontend/parse.cc  (working copy)
@@ -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(con
 //                    __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_nointerfac
       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_nointerfac
                                                     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_nointerfac
            }
        }
 
-      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_nointerfac
       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: gcc/go/gofrontend/parse.h
===================================================================
--- gcc/go/gofrontend/parse.h   (revision 238653)
+++ gcc/go/gofrontend/parse.h   (working copy)
@@ -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*,

Reply via email to