-3b9c57a35370f26e6cf5839084e367e75e45ec97
+58be5c6c7d92182dec50a62c8e319d2d7aab12a4
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
: 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),
(*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*
// 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_ =
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.
// 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;
public:
Function_declaration(Function_type* fntype, Location location)
: fntype_(fntype), location_(location), asm_name_(), descriptor_(NULL),
- fndecl_(NULL)
+ fndecl_(NULL), pragmas_(0)
{ }
Function_type*
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
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.
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_];
// //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));
p = plend;
}
}
+ return;
}
// As a special gccgo extension, a C++ comment at the start of the
// 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;
}
}
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
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;
}
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)
{
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();
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");
// __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();
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())
{
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)
{
}
}
- 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
{
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;
}
}
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*,