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
merge done from the gofrontend repository.
......@@ -52,6 +52,7 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
{
go_assert(filename_count > 0);
Lex::Linknames all_linknames;
for (unsigned int i = 0; i < filename_count; ++i)
{
if (i > 0)
......@@ -76,6 +77,21 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
if (strcmp(filename, "-") != 0)
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();
......@@ -86,6 +102,13 @@ go_parse_input_files(const char** filenames, unsigned int filename_count,
// define them now.
::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.
::gogo->finalize_methods();
......
......@@ -32,6 +32,7 @@ Gogo::Gogo(Backend* backend, Linemap* linemap, int, int pointer_size)
file_block_names_(),
imports_(),
imported_unsafe_(false),
current_file_imported_unsafe_(false),
packages_(),
init_functions_(),
var_deps_(),
......@@ -449,6 +450,7 @@ Gogo::import_package(const std::string& filename,
if (filename == "unsafe")
{
this->import_unsafe(local_name, is_local_name_exported, location);
this->current_file_imported_unsafe_ = true;
return;
}
......@@ -2000,6 +2002,29 @@ Gogo::add_dot_import_object(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
// parse error occur.
......@@ -2183,6 +2208,8 @@ Gogo::clear_file_scope()
}
package->clear_used();
}
this->current_file_imported_unsafe_ = false;
}
// 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)
}
}
if (!this->asm_name_.empty())
{
asm_name = this->asm_name_;
is_visible = true;
}
// If a function calls the predeclared recover function, we
// can't inline it, because recover behaves differently in a
// function passed directly to defer. If this is a recover
......
......@@ -428,6 +428,12 @@ class Gogo
add_file_block_name(const std::string& name, Location 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
// used when there is a parse error to avoid useless errors.
void
......@@ -463,6 +469,11 @@ class Gogo
set_need_init_fn()
{ 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
// parsing a new file.
void
......@@ -760,6 +771,8 @@ class Gogo
Imports imports_;
// Whether the magic unsafe package was imported.
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
// not include the package we are compiling.
Packages packages_;
......@@ -975,6 +988,11 @@ class Function
results_are_named() const
{ 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.
void
set_pragmas(unsigned int pragmas)
......@@ -1229,6 +1247,9 @@ class Function
Labels labels_;
// The number of local types defined in this function.
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.
Expression* descriptor_;
// The function decl.
......
......@@ -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),
linemap_(linemap), linebuf_(NULL), linebufsize_(120), linesize_(0),
lineoff_(0), lineno_(0), add_semi_at_eol_(false), pragmas_(0),
extern_()
extern_(), linknames_(NULL)
{
this->linebuf_ = new char[this->linebufsize_];
this->linemap_->start_file(input_file_name, 0);
......@@ -1676,6 +1676,7 @@ Lex::skip_cpp_comment()
// //extern comment.
this->extern_.clear();
Location loc = this->location();
size_t lineoff = this->lineoff_;
const char* p = this->linebuf_ + lineoff;
......@@ -1777,7 +1778,8 @@ Lex::skip_cpp_comment()
{
// As in the gc compiler, set the external link name for a Go symbol.
std::string go_name;
std::string c_name;
std::string ext_name;
bool is_exported = false;
if (ps < pend)
{
while (ps < pend && (*ps == ' ' || *ps == '\t'))
......@@ -1785,6 +1787,12 @@ Lex::skip_cpp_comment()
if (ps < pend)
{
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')
++ps;
if (ps < pend)
......@@ -1798,18 +1806,22 @@ Lex::skip_cpp_comment()
while (ps < pend && *ps != ' ' && *ps != '\t')
++ps;
if (ps <= pend)
c_name = std::string(pc, ps - pc);
ext_name = std::string(pc, ps - pc);
}
if (ps != pend)
{
go_name.clear();
c_name.clear();
ext_name.clear();
}
}
if (go_name.empty() || c_name.empty())
error_at(this->location(), "usage: //go:linkname localname linkname");
if (go_name.empty() || ext_name.empty())
error_at(loc, "usage: //go:linkname localname linkname");
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")
{
......
......@@ -375,6 +375,33 @@ class Lex
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
// mangled name which includes only ASCII characters.
static bool
......@@ -514,8 +541,8 @@ class Lex
// 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_;
// The list of //go:linkname comments, if any.
Linknames* linknames_;
};
#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