Commit 31587386 by Ian Lance Taylor

compiler: implement go:linkname compiler directive

    
    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
parent dc29c7de
d3636ca659ed7eed6d2e1cedfa0adccc6d81c07d 85a9c6992d9660e36972c279a5252fd9591bb765
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.
...@@ -52,6 +52,7 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, ...@@ -52,6 +52,7 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
{ {
go_assert(filename_count > 0); go_assert(filename_count > 0);
Lex::Linknames all_linknames;
for (unsigned int i = 0; i < filename_count; ++i) for (unsigned int i = 0; i < filename_count; ++i)
{ {
if (i > 0) if (i > 0)
...@@ -76,6 +77,21 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, ...@@ -76,6 +77,21 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
if (strcmp(filename, "-") != 0) if (strcmp(filename, "-") != 0)
fclose(file); 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(); ::gogo->linemap()->stop();
...@@ -86,6 +102,13 @@ go_parse_input_files(const char** filenames, unsigned int filename_count, ...@@ -86,6 +102,13 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
// define them now. // define them now.
::gogo->define_global_names(); ::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. // Finalize method lists and build stub methods for named types.
::gogo->finalize_methods(); ::gogo->finalize_methods();
......
...@@ -32,6 +32,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size) ...@@ -32,6 +32,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
file_block_names_(), file_block_names_(),
imports_(), imports_(),
imported_unsafe_(false), imported_unsafe_(false),
current_file_imported_unsafe_(false),
packages_(), packages_(),
init_functions_(), init_functions_(),
var_deps_(), var_deps_(),
...@@ -449,6 +450,7 @@ Gogo::import_package(const std::string& filename, ...@@ -449,6 +450,7 @@ Gogo::import_package(const std::string& filename,
if (filename == "unsafe") if (filename == "unsafe")
{ {
this->import_unsafe(local_name, is_local_name_exported, location); this->import_unsafe(local_name, is_local_name_exported, location);
this->current_file_imported_unsafe_ = true;
return; return;
} }
...@@ -2000,6 +2002,29 @@ Gogo::add_dot_import_object(Named_object* no) ...@@ -2000,6 +2002,29 @@ Gogo::add_dot_import_object(Named_object* no)
this->current_bindings()->add_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 // Mark all local variables used. This is used when some types of
// parse error occur. // parse error occur.
...@@ -2183,6 +2208,8 @@ Gogo::clear_file_scope() ...@@ -2183,6 +2208,8 @@ Gogo::clear_file_scope()
} }
package->clear_used(); package->clear_used();
} }
this->current_file_imported_unsafe_ = false;
} }
// Queue up a type specific function for later writing. These are // 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) ...@@ -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 // If a function calls the predeclared recover function, we
// can't inline it, because recover behaves differently in a // can't inline it, because recover behaves differently in a
// function passed directly to defer. If this is a recover // function passed directly to defer. If this is a recover
......
...@@ -428,6 +428,12 @@ class Gogo ...@@ -428,6 +428,12 @@ class Gogo
add_file_block_name(const std::string& name, Location location) add_file_block_name(const std::string& name, Location location)
{ this->file_block_names_[name] = 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 // Mark all local variables in current bindings as used. This is
// used when there is a parse error to avoid useless errors. // used when there is a parse error to avoid useless errors.
void void
...@@ -463,6 +469,11 @@ class Gogo ...@@ -463,6 +469,11 @@ class Gogo
set_need_init_fn() set_need_init_fn()
{ this->need_init_fn_ = true; } { 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 // Clear out all names in file scope. This is called when we start
// parsing a new file. // parsing a new file.
void void
...@@ -760,6 +771,8 @@ class Gogo ...@@ -760,6 +771,8 @@ class Gogo
Imports imports_; Imports imports_;
// Whether the magic unsafe package was imported. // Whether the magic unsafe package was imported.
bool imported_unsafe_; 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 // Mapping from package names we have seen to packages. This does
// not include the package we are compiling. // not include the package we are compiling.
Packages packages_; Packages packages_;
...@@ -975,6 +988,11 @@ class Function ...@@ -975,6 +988,11 @@ class Function
results_are_named() const results_are_named() const
{ return this->results_are_named_; } { 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. // Set the pragmas for this function.
void void
set_pragmas(unsigned int pragmas) set_pragmas(unsigned int pragmas)
...@@ -1229,6 +1247,9 @@ class Function ...@@ -1229,6 +1247,9 @@ class Function
Labels labels_; Labels labels_;
// The number of local types defined in this function. // The number of local types defined in this function.
unsigned int local_type_count_; 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. // The function descriptor, if any.
Expression* descriptor_; Expression* descriptor_;
// The function decl. // The function decl.
......
...@@ -443,7 +443,7 @@ Lex::Lex(const char* input_file_name, FILE* input_file, Linemap* linemap) ...@@ -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), : input_file_name_(input_file_name), input_file_(input_file),
linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0), linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0),
lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0), lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0),
extern_() extern_(), linknames_(NULL)
{ {
this->linebuf_ = new char[this->linebufsize_]; this->linebuf_ = new char[this->linebufsize_];
this->linemap_->start_file(input_file_name, 0); this->linemap_->start_file(input_file_name, 0);
...@@ -1676,6 +1676,7 @@ Lex::skip_cpp_comment() ...@@ -1676,6 +1676,7 @@ Lex::skip_cpp_comment()
// //extern comment. // //extern comment.
this->extern_.clear(); this->extern_.clear();
Location loc = this->location();
size_t lineoff = this->lineoff_; size_t lineoff = this->lineoff_;
const char* p = this->linebuf_ + lineoff; const char* p = this->linebuf_ + lineoff;
...@@ -1777,7 +1778,8 @@ Lex::skip_cpp_comment() ...@@ -1777,7 +1778,8 @@ Lex::skip_cpp_comment()
{ {
// As in the gc compiler, set the external link name for a Go symbol. // As in the gc compiler, set the external link name for a Go symbol.
std::string go_name; std::string go_name;
std::string c_name; std::string ext_name;
bool is_exported = false;
if (ps < pend) if (ps < pend)
{ {
while (ps < pend && (*ps == ' ' || *ps == '\t')) while (ps < pend && (*ps == ' ' || *ps == '\t'))
...@@ -1785,6 +1787,12 @@ Lex::skip_cpp_comment() ...@@ -1785,6 +1787,12 @@ Lex::skip_cpp_comment()
if (ps < pend) if (ps < pend)
{ {
const char* pg = ps; 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') while (ps < pend && *ps != ' ' && *ps != '\t')
++ps; ++ps;
if (ps < pend) if (ps < pend)
...@@ -1798,18 +1806,22 @@ Lex::skip_cpp_comment() ...@@ -1798,18 +1806,22 @@ Lex::skip_cpp_comment()
while (ps < pend && *ps != ' ' && *ps != '\t') while (ps < pend && *ps != ' ' && *ps != '\t')
++ps; ++ps;
if (ps <= pend) if (ps <= pend)
c_name = std::string(pc, ps - pc); ext_name = std::string(pc, ps - pc);
} }
if (ps != pend) if (ps != pend)
{ {
go_name.clear(); go_name.clear();
c_name.clear(); ext_name.clear();
} }
} }
if (go_name.empty() || c_name.empty()) if (go_name.empty() || ext_name.empty())
error_at(this->location(), "usage: //go:linkname localname linkname"); error_at(loc, "usage: //go:linkname localname linkname");
else 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") else if (verb == "go:nointerface")
{ {
......
...@@ -375,6 +375,33 @@ class Lex ...@@ -375,6 +375,33 @@ class Lex
return ret; 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 // Return whether the identifier NAME should be exported. NAME is a
// mangled name which includes only ASCII characters. // mangled name which includes only ASCII characters.
static bool static bool
...@@ -514,8 +541,8 @@ class Lex ...@@ -514,8 +541,8 @@ class Lex
// The external name to use for a function declaration, from a magic // The external name to use for a function declaration, from a magic
// //extern comment. // //extern comment.
std::string extern_; std::string extern_;
// The list of //go:linkname comments. // The list of //go:linkname comments, if any.
std::map<std::string, std::string> linknames_; Linknames* linknames_;
}; };
#endif // !defined(GO_LEX_H) #endif // !defined(GO_LEX_H)
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