Commit b87060ce by Ian Lance Taylor

compiler: rewrite compiler directive support

    
    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
parent a00f4c6b
3b9c57a35370f26e6cf5839084e367e75e45ec97
58be5c6c7d92182dec50a62c8e319d2d7aab12a4
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -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_ =
......
......@@ -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.
......
......@@ -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;
}
}
......
......@@ -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)
......@@ -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;
}
}
......
......@@ -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*,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment