From: Ian Lance Taylor Date: Tue, 9 Aug 2016 23:08:32 +0000 (+0000) Subject: compiler: implement go:linkname compiler directive X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=315873869d984b23c4bf0e1a47ce030c2a575184;p=gcc.git 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 --- diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index b23a1f55bd1..d4c7a90d8b7 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -d3636ca659ed7eed6d2e1cedfa0adccc6d81c07d +85a9c6992d9660e36972c279a5252fd9591bb765 The first line of this file holds the git revision number of the last merge done from the gofrontend repository. diff --git a/gcc/go/gofrontend/go.cc b/gcc/go/gofrontend/go.cc index a0df2b801bd..ee87141efd2 100644 --- a/gcc/go/gofrontend/go.cc +++ b/gcc/go/gofrontend/go.cc @@ -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(); diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index b1dbb850200..a72ef9520ec 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -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 diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index bac9eb0c168..d2262e39111 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -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. diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc index 94809d52b23..9498c7dfc0b 100644 --- a/gcc/go/gofrontend/lex.cc +++ b/gcc/go/gofrontend/lex.cc @@ -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") { diff --git a/gcc/go/gofrontend/lex.h b/gcc/go/gofrontend/lex.h index 2f14136a525..5c4afb611b0 100644 --- a/gcc/go/gofrontend/lex.h +++ b/gcc/go/gofrontend/lex.h @@ -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 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 linknames_; + // The list of //go:linkname comments, if any. + Linknames* linknames_; }; #endif // !defined(GO_LEX_H)