diff options
author | Ian Lance Taylor <iant@golang.org> | 2020-11-16 20:06:53 -0800 |
---|---|---|
committer | Ian Lance Taylor <iant@golang.org> | 2020-11-20 12:44:35 -0800 |
commit | a01dda3c23b836754814fab1cab949a1bbc641e8 (patch) | |
tree | 826310b88323c0f636baf89393557fde6a56fdeb /gcc | |
parent | 90bf60c3c24c6c99ebbecf9d08a6d0d916d73721 (diff) | |
download | gcc-a01dda3c23b836754814fab1cab949a1bbc641e8.zip gcc-a01dda3c23b836754814fab1cab949a1bbc641e8.tar.gz gcc-a01dda3c23b836754814fab1cab949a1bbc641e8.tar.bz2 |
compiler, libgo: change mangling scheme
Overhaul the mangling scheme to avoid ambiguities if the package path
contains a dot. Instead of using dot both to separate components and
to mangle characters, use dot only to separate components and use
underscore to mangle characters.
For golang/go#41862
Reviewed-on: https://go-review.googlesource.com/c/gofrontend/+/271726
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/go/gofrontend/MERGE | 2 | ||||
-rw-r--r-- | gcc/go/gofrontend/ast-dump.cc | 6 | ||||
-rw-r--r-- | gcc/go/gofrontend/export.cc | 8 | ||||
-rw-r--r-- | gcc/go/gofrontend/expressions.cc | 38 | ||||
-rw-r--r-- | gcc/go/gofrontend/go-encode-id.cc | 289 | ||||
-rw-r--r-- | gcc/go/gofrontend/go-encode-id.h | 13 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.cc | 252 | ||||
-rw-r--r-- | gcc/go/gofrontend/gogo.h | 173 | ||||
-rw-r--r-- | gcc/go/gofrontend/names.cc | 759 | ||||
-rw-r--r-- | gcc/go/gofrontend/runtime.def | 34 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.cc | 105 | ||||
-rw-r--r-- | gcc/go/gofrontend/types.h | 20 |
12 files changed, 986 insertions, 713 deletions
diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index 1f3c25c..9545a59 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -c948c2d770122932a05b62f653efa2c51f72d3ae +b483d0e0a289ba5fcdbd0388cbc75393367ca870 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/ast-dump.cc b/gcc/go/gofrontend/ast-dump.cc index a3cbda9..eca0bf1 100644 --- a/gcc/go/gofrontend/ast-dump.cc +++ b/gcc/go/gofrontend/ast-dump.cc @@ -226,7 +226,11 @@ Ast_dump_context::dump_type(const Type* t) // FIXME: write a type pretty printer instead of // using mangled names. if (this->gogo_ != NULL) - this->ostream() << "(" << t->mangled_name(this->gogo_) << ")"; + { + Backend_name bname; + t->backend_name(this->gogo_, &bname); + this->ostream() << "(" << bname.name() << ")"; + } } // Dump a textual representation of a block to the diff --git a/gcc/go/gofrontend/export.cc b/gcc/go/gofrontend/export.cc index 90a5f6d..e99c680 100644 --- a/gcc/go/gofrontend/export.cc +++ b/gcc/go/gofrontend/export.cc @@ -461,7 +461,7 @@ should_export(Named_object* no) return false; // We don't export various special functions. - if (Gogo::is_special_name(no->name())) + if (Gogo::special_name_pos(no->name()) != std::string::npos) return false; // Methods are exported with the type, not here. @@ -524,7 +524,11 @@ struct Sort_types if (t1->classification() != t2->classification()) return t1->classification() < t2->classification(); Gogo* gogo = go_get_gogo(); - return gogo->type_descriptor_name(t1, NULL).compare(gogo->type_descriptor_name(t2, NULL)) < 0; + Backend_name b1; + gogo->type_descriptor_backend_name(t1, NULL, &b1); + Backend_name b2; + gogo->type_descriptor_backend_name(t2, NULL, &b2); + return b1.name() < b2.name(); } }; diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index e76bc69..6bc9348 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -1596,7 +1596,8 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) return context->backend()->var_expression(this->dvar_, loc); Gogo* gogo = context->gogo(); - std::string var_name(gogo->function_descriptor_name(no)); + Backend_name bname; + gogo->function_descriptor_backend_name(no, &bname); bool is_descriptor = false; if (no->is_function_declaration() && !no->func_declaration_value()->asm_name().empty() @@ -1616,10 +1617,11 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) Btype* btype = this->type()->get_backend(gogo); Bvariable* bvar; - std::string asm_name(go_selectively_encode_id(var_name)); if (no->package() != NULL || is_descriptor) - bvar = context->backend()->immutable_struct_reference(var_name, asm_name, - btype, loc); + bvar = + context->backend()->immutable_struct_reference(bname.name(), + bname.optional_asm_name(), + btype, loc); else { Location bloc = Linemap::predeclared_location(); @@ -1644,7 +1646,8 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) if (no->is_function() && no->func_value()->is_referenced_by_inline()) is_hidden = false; - bvar = context->backend()->immutable_struct(var_name, asm_name, + bvar = context->backend()->immutable_struct(bname.name(), + bname.optional_asm_name(), is_hidden, false, btype, bloc); Expression_list* vals = new Expression_list(); @@ -1654,8 +1657,9 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) Translate_context bcontext(gogo, NULL, NULL, NULL); bcontext.set_is_const(); Bexpression* binit = init->get_backend(&bcontext); - context->backend()->immutable_struct_set_init(bvar, var_name, is_hidden, - false, btype, bloc, binit); + context->backend()->immutable_struct_set_init(bvar, bname.name(), + is_hidden, false, btype, + bloc, binit); } this->dvar_ = bvar; @@ -5190,11 +5194,9 @@ Unary_expression::do_get_backend(Translate_context* context) copy_to_heap = (context->function() != NULL || context->is_const()); } - std::string asm_name(go_selectively_encode_id(var_name)); Bvariable* implicit = - gogo->backend()->implicit_variable(var_name, asm_name, - btype, true, copy_to_heap, - false, 0); + gogo->backend()->implicit_variable(var_name, "", btype, true, + copy_to_heap, false, 0); gogo->backend()->implicit_variable_set_init(implicit, var_name, btype, true, copy_to_heap, false, bexpr); @@ -5219,10 +5221,9 @@ Unary_expression::do_get_backend(Translate_context* context) && this->expr_->is_static_initializer()) { std::string var_name(gogo->initializer_name()); - std::string asm_name(go_selectively_encode_id(var_name)); Bvariable* decl = - gogo->backend()->immutable_struct(var_name, asm_name, - true, false, btype, loc); + gogo->backend()->immutable_struct(var_name, "", true, false, + btype, loc); gogo->backend()->immutable_struct_set_init(decl, var_name, true, false, btype, loc, bexpr); bexpr = gogo->backend()->var_expression(decl, loc); @@ -5230,9 +5231,8 @@ Unary_expression::do_get_backend(Translate_context* context) else if (this->expr_->is_constant()) { std::string var_name(gogo->initializer_name()); - std::string asm_name(go_selectively_encode_id(var_name)); Bvariable* decl = - gogo->backend()->implicit_variable(var_name, asm_name, btype, + gogo->backend()->implicit_variable(var_name, "", btype, true, true, false, 0); gogo->backend()->implicit_variable_set_init(decl, var_name, btype, true, true, false, @@ -18251,9 +18251,8 @@ Interface_mtable_expression::do_get_backend(Translate_context* context) { // The interface conversion table is defined elsewhere. Btype* btype = this->type()->get_backend(gogo); - std::string asm_name(go_selectively_encode_id(mangled_name)); this->bvar_ = - gogo->backend()->immutable_struct_reference(mangled_name, asm_name, + gogo->backend()->immutable_struct_reference(mangled_name, "", btype, loc); return gogo->backend()->var_expression(this->bvar_, this->location()); } @@ -18323,8 +18322,7 @@ Interface_mtable_expression::do_get_backend(Translate_context* context) Bexpression* ctor = gogo->backend()->constructor_expression(btype, ctor_bexprs, loc); - std::string asm_name(go_selectively_encode_id(mangled_name)); - this->bvar_ = gogo->backend()->immutable_struct(mangled_name, asm_name, false, + this->bvar_ = gogo->backend()->immutable_struct(mangled_name, "", false, !is_public, btype, loc); gogo->backend()->immutable_struct_set_init(this->bvar_, mangled_name, false, !is_public, btype, loc, ctor); diff --git a/gcc/go/gofrontend/go-encode-id.cc b/gcc/go/gofrontend/go-encode-id.cc index 550da97..7ab65f5 100644 --- a/gcc/go/gofrontend/go-encode-id.cc +++ b/gcc/go/gofrontend/go-encode-id.cc @@ -12,8 +12,8 @@ #include "go-encode-id.h" #include "lex.h" -// Return whether the character c is OK to use in the assembler. We -// only permit ASCII alphanumeric characters, underscore, and dot. +// Return whether the character c can appear in a name that we are +// encoding. We only permit ASCII alphanumeric characters. static bool char_needs_encoding(char c) @@ -32,7 +32,6 @@ char_needs_encoding(char c) case 'y': case 'z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - case '_': case '.': return false; default: return true; @@ -53,6 +52,52 @@ go_id_needs_encoding(const std::string& str) return false; } +// Map from characters to the underscore encoding for them. + +class Special_char_code +{ + public: + Special_char_code(); + + // Return the simple underscore encoding for C, or 0 if none. + char + code_for(unsigned int c) const + { + if (c <= 127) + return this->codes_[c]; + return 0; + } + + private: + // Encodings for characters. + char codes_[128]; +}; + +// Construct the underscore encoding map. + +Special_char_code::Special_char_code() +{ + memset(this->codes_, 0, sizeof this->codes_); + this->codes_['_'] = '_'; + this->codes_['.'] = '0'; + this->codes_['/'] = '1'; + this->codes_['*'] = '2'; + this->codes_[','] = '3'; + this->codes_['{'] = '4'; + this->codes_['}'] = '5'; + this->codes_['['] = '6'; + this->codes_[']'] = '7'; + this->codes_['('] = '8'; + this->codes_[')'] = '9'; + this->codes_['"'] = 'a'; + this->codes_[' '] = 'b'; + this->codes_[';'] = 'c'; +} + +// The singleton Special_char_code. + +static const Special_char_code special_char_code; + // Pull the next UTF-8 character out of P and store it in *PC. Return // the number of bytes read. @@ -82,10 +127,9 @@ fetch_utf8_char(const char* p, unsigned int* pc) return len; } -// Encode an identifier using assembler-friendly characters. The encoding is -// described in detail near the end of the long comment at the start of -// names.cc. Short version: translate all non-ASCII-alphanumeric characters into -// ..uXXXX or ..UXXXXXXXX, translate ASCII non-alphanumerics into ".zXX". +// Encode an identifier using assembler-friendly characters. The +// encoding is described in detail near the end of the long comment at +// the start of names.cc. std::string go_encode_id(const std::string &id) @@ -96,50 +140,57 @@ go_encode_id(const std::string &id) return id; } - // The encoding is only unambiguous if the input string does not - // contain ..z, ..u or ..U. - go_assert(id.find("..z") == std::string::npos); - go_assert(id.find("..u") == std::string::npos); - go_assert(id.find("..U") == std::string::npos); - std::string ret; const char* p = id.c_str(); const char* pend = p + id.length(); - // A leading ".0" is a space introduced before a mangled type name - // that starts with a 'u' or 'U', to avoid confusion with the - // mangling used here. We don't need a leading ".0", and we don't - // want symbols that start with '.', so remove it. - if (p[0] == '.' && p[1] == '0') - p += 2; + // We encode a leading digit, to ensure that no identifier starts + // with a digit. + if (pend > p && p[0] >= '0' && p[0] <= '9') + { + char buf[8]; + snprintf(buf, sizeof buf, "_x%02x", p[0]); + ret.append(buf); + ++p; + } while (p < pend) { unsigned int c; size_t len = fetch_utf8_char(p, &c); - if (len == 1 && !char_needs_encoding(c)) + if (len == 1) { - ret += c; + if (!char_needs_encoding(c)) + ret.push_back(c); + else + { + char code = special_char_code.code_for(c); + if (code != 0) + { + ret.push_back('_'); + ret.push_back(code); + } + else + { + char buf[8]; + snprintf(buf, sizeof buf, "_x%02x", c); + ret.append(buf); + } + } } else { char buf[16]; - if (len == 1) - snprintf(buf, sizeof buf, "..z%02x", c); - else if (c < 0x10000) - snprintf(buf, sizeof buf, "..u%04x", c); + if (c < 0x10000) + snprintf(buf, sizeof buf, "_u%04x", c); else - snprintf(buf, sizeof buf, "..U%08x", c); - - // We don't want a symbol to start with '.', so add a prefix - // if needed. - if (ret.empty()) - ret += '_'; - - ret += buf; + snprintf(buf, sizeof buf, "_U%08x", c); + ret.append(buf); } + p += len; } + return ret; } @@ -170,64 +221,116 @@ go_decode_id(const std::string &encoded) const char* pend = p + encoded.length(); const Location loc = Linemap::predeclared_location(); - // Special case for initial "_", in case it was introduced - // as a way to prevent encoded symbol starting with ".". - if (*p == '_' && (strncmp(p+1, "..u", 3) == 0 || strncmp(p+1, "..U", 3) == 0)) - p++; - while (p < pend) { - if (strncmp(p, "..z", 3) == 0) - { - const char* digits = p+3; - if (strlen(digits) < 2) - return ""; - unsigned rune = hex_digits_to_unicode_codepoint(digits, 2); - Lex::append_char(rune, true, &ret, loc); - p += 5; - } - else if (strncmp(p, "..u", 3) == 0) - { - const char* digits = p+3; - if (strlen(digits) < 4) - return ""; - unsigned rune = hex_digits_to_unicode_codepoint(digits, 4); - Lex::append_char(rune, true, &ret, loc); - p += 7; - } - else if (strncmp(p, "..U", 3) == 0) - { - const char* digits = p+3; - if (strlen(digits) < 8) - return ""; - unsigned rune = hex_digits_to_unicode_codepoint(digits, 8); - Lex::append_char(rune, true, &ret, loc); - p += 11; - } - else - { - ret += *p; - p += 1; - } + if (*p != '_' || p + 1 == pend) + { + ret.push_back(*p); + p++; + continue; + } + + switch (p[1]) + { + case '_': + ret.push_back('_'); + p += 2; + break; + case '0': + ret.push_back('.'); + p += 2; + break; + case '1': + ret.push_back('/'); + p += 2; + break; + case '2': + ret.push_back('*'); + p += 2; + break; + case '3': + ret.push_back(','); + p += 2; + break; + case '4': + ret.push_back('{'); + p += 2; + break; + case '5': + ret.push_back('}'); + p += 2; + break; + case '6': + ret.push_back('['); + p += 2; + break; + case '7': + ret.push_back(']'); + p += 2; + break; + case '8': + ret.push_back('('); + p += 2; + break; + case '9': + ret.push_back(')'); + p += 2; + break; + case 'a': + ret.push_back('"'); + p += 2; + break; + case 'b': + ret.push_back(' '); + p += 2; + break; + case 'c': + ret.push_back(';'); + p += 2; + break; + case 'x': + { + const char* digits = p + 2; + if (strlen(digits) < 2) + return ""; + unsigned int rune = hex_digits_to_unicode_codepoint(digits, 2); + Lex::append_char(rune, true, &ret, loc); + p += 4; + } + break; + case 'u': + { + const char* digits = p + 2; + if (strlen(digits) < 4) + return ""; + unsigned int rune = hex_digits_to_unicode_codepoint(digits, 4); + Lex::append_char(rune, true, &ret, loc); + p += 6; + } + break; + case 'U': + { + const char* digits = p + 2; + if (strlen(digits) < 8) + return ""; + unsigned int rune = hex_digits_to_unicode_codepoint(digits, 8); + Lex::append_char(rune, true, &ret, loc); + p += 10; + } + break; + default: + return ""; + } } return ret; } -std::string -go_selectively_encode_id(const std::string &id) -{ - if (go_id_needs_encoding(id)) - return go_encode_id(id); - return std::string(); -} - // Encode a struct field tag. This is only used when we need to // create a type descriptor for an anonymous struct type with field -// tags. This mangling is applied before go_encode_id. We skip -// alphanumerics and underscore, replace every other single byte -// character with .xNN, and leave larger UTF-8 characters for -// go_encode_id. +// tags. Underscore encoding will be applied to the returned string. +// The tag will appear between curly braces, so that is all we have to +// avoid. std::string go_mangle_struct_tag(const std::string& tag) @@ -241,28 +344,14 @@ go_mangle_struct_tag(const std::string& tag) size_t len = fetch_utf8_char(p, &c); if (len > 1) ret.append(p, len); - else if (!char_needs_encoding(c) && c != '.') - ret += c; + else if (c != '{' && c != '}' && c != '\\') + ret.push_back(c); else { - char buf[16]; - snprintf(buf, sizeof buf, ".x%02x", c); - ret += buf; + ret.push_back('\\'); + ret.push_back(c); } p += len; } return ret; } - -// Encode a package path. - -std::string -go_mangle_pkgpath(const std::string& pkgpath) -{ - std::string s = pkgpath; - for (size_t i = s.find('.'); - i != std::string::npos; - i = s.find('.', i + 1)) - s.replace(i, 1, ".x2e"); // 0x2e is the ASCII encoding for '.' - return s; -} diff --git a/gcc/go/gofrontend/go-encode-id.h b/gcc/go/gofrontend/go-encode-id.h index 2d09b0c..cbafd54 100644 --- a/gcc/go/gofrontend/go-encode-id.h +++ b/gcc/go/gofrontend/go-encode-id.h @@ -25,21 +25,8 @@ go_encode_id(const std::string &id); extern std::string go_decode_id(const std::string &id); -// Returns the empty string if the specified name needs encoding, -// otherwise invokes go_encode_id on the name and returns the result. -extern std::string -go_selectively_encode_id(const std::string &id); - // Encodes a struct tag that appears in a type literal encoding. extern std::string go_mangle_struct_tag(const std::string& tag); -// Encode a package path. A package path can contain any arbitrary -// character, including '.'. go_encode_id expects that any '.' will -// be inserted by name mangling in a controlled manner. So first -// translate any '.' using the same .x encoding as used by -// go_mangle_struct_tag. -extern std::string -go_mangle_pkgpath(const std::string& pkgpath); - #endif // !defined(GO_ENCODE_ID_H) diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index f40f131..93a4a57 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -299,7 +299,7 @@ void Gogo::set_pkgpath(const std::string& arg) { go_assert(!this->pkgpath_set_); - this->pkgpath_ = go_mangle_pkgpath(arg); + this->pkgpath_ = arg; this->pkgpath_set_ = true; this->pkgpath_from_option_ = true; } @@ -324,32 +324,6 @@ Gogo::set_prefix(const std::string& arg) this->prefix_from_option_ = true; } -// Given a name which may or may not have been hidden, append the -// appropriate version of the name to the result string. Take care -// to avoid creating a sequence that will be rejected by go_encode_id -// (avoid ..u, ..U, ..z). -void -Gogo::append_possibly_hidden_name(std::string *result, const std::string& name) -{ - // FIXME: This adds in pkgpath twice for hidden symbols, which is - // less than ideal. - if (!Gogo::is_hidden_name(name)) - (*result) += name; - else - { - std::string n = "."; - std::string pkgpath = Gogo::hidden_name_pkgpath(name); - char lastR = result->at(result->length() - 1); - char firstP = pkgpath.at(0); - if (lastR == '.' && (firstP == 'u' || firstP == 'U' || firstP == 'z')) - n = "_."; - n.append(pkgpath); - n.append(1, '.'); - n.append(Gogo::unpack_hidden_name(name)); - (*result) += n; - } -} - // Munge name for use in an error message. std::string @@ -397,8 +371,7 @@ Gogo::set_package_name(const std::string& package_name, { if (!this->prefix_from_option_) this->prefix_ = "go"; - this->pkgpath_ = (go_mangle_pkgpath(this->prefix_) + '.' - + package_name); + this->pkgpath_ = this->prefix_ + '.' + package_name; this->pkgpath_symbol_ = (Gogo::pkgpath_for_symbol(this->prefix_) + '.' + Gogo::pkgpath_for_symbol(package_name)); } @@ -997,9 +970,9 @@ Gogo::register_type_descriptors(std::vector<Bstatement*>& init_stmts, it != this->imported_init_fns_.end(); ++it) { - std::string pkgpath = - this->pkgpath_from_init_fn_name((*it)->init_name()); - list_names.push_back(this->type_descriptor_list_symbol(pkgpath)); + std::string pkgpath_symbol = + this->pkgpath_symbol_from_init_fn_name((*it)->init_name()); + list_names.push_back(this->type_descriptor_list_symbol(pkgpath_symbol)); } // Add the main package itself. list_names.push_back(this->type_descriptor_list_symbol("main")); @@ -2746,8 +2719,7 @@ Gogo::clear_file_scope() // parse tree is lowered. void -Gogo::queue_hash_function(Type* type, int64_t size, - const std::string& hash_name, +Gogo::queue_hash_function(Type* type, int64_t size, Backend_name* bname, Function_type* hash_fntype) { go_assert(!this->specific_type_functions_are_written_); @@ -2755,7 +2727,7 @@ Gogo::queue_hash_function(Type* type, int64_t size, Specific_type_function::Specific_type_function_kind kind = Specific_type_function::SPECIFIC_HASH; Specific_type_function* tsf = new Specific_type_function(type, NULL, size, - kind, hash_name, + kind, bname, hash_fntype); this->specific_type_functions_.push_back(tsf); } @@ -2766,15 +2738,14 @@ Gogo::queue_hash_function(Type* type, int64_t size, void Gogo::queue_equal_function(Type* type, Named_type* name, int64_t size, - const std::string& equal_name, - Function_type* equal_fntype) + Backend_name* bname, Function_type* equal_fntype) { go_assert(!this->specific_type_functions_are_written_); go_assert(!this->in_global_scope()); Specific_type_function::Specific_type_function_kind kind = Specific_type_function::SPECIFIC_EQUAL; Specific_type_function* tsf = new Specific_type_function(type, name, size, - kind, equal_name, + kind, bname, equal_fntype); this->specific_type_functions_.push_back(tsf); } @@ -2872,11 +2843,11 @@ Gogo::write_specific_type_functions() Specific_type_function* tsf = this->specific_type_functions_.back(); this->specific_type_functions_.pop_back(); if (tsf->kind == Specific_type_function::SPECIFIC_HASH) - tsf->type->write_hash_function(this, tsf->size, tsf->fnname, + tsf->type->write_hash_function(this, tsf->size, &tsf->bname, tsf->fntype); else tsf->type->write_equal_function(this, tsf->name, tsf->size, - tsf->fnname, tsf->fntype); + &tsf->bname, tsf->fntype); delete tsf; } this->specific_type_functions_are_written_ = true; @@ -6218,6 +6189,56 @@ Function::import_func(Import* imp, std::string* pname, return true; } +// Get the backend name. + +void +Function::backend_name(Gogo* gogo, Named_object* no, Backend_name *bname) +{ + if (!this->asm_name_.empty()) + bname->set_asm_name(this->asm_name_); + else if (no->package() == NULL && no->name() == gogo->get_init_fn_name()) + { + // These names appear in the export data and are used + // directly in the assembler code. If we change this here + // we need to change Gogo::init_imports. + bname->set_asm_name(no->name()); + } + else if (this->enclosing_ != NULL) + { + // Rewrite the nested name to use the enclosing function name. + // We don't do this earlier because we just store simple names + // in a Named_object, not Backend_names. + + // The name was set by nested_function_name, which always + // appends ..funcNNN. We want that to be our suffix. + size_t pos = no->name().find("..func"); + go_assert(pos != std::string::npos); + + Named_object* enclosing = this->enclosing_; + while (true) + { + Named_object* parent = enclosing->func_value()->enclosing(); + if (parent == NULL) + break; + enclosing = parent; + } + + Type* rtype = NULL; + if (enclosing->func_value()->type()->is_method()) + rtype = enclosing->func_value()->type()->receiver()->type(); + gogo->function_backend_name(enclosing->name(), enclosing->package(), + rtype, bname); + bname->append_suffix(no->name().substr(pos)); + } + else + { + Type* rtype = NULL; + if (this->type_->is_method()) + rtype = this->type_->receiver()->type(); + gogo->function_backend_name(no->name(), no->package(), rtype, bname); + } +} + // Get the backend representation. Bfunction* @@ -6226,7 +6247,6 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) if (this->fndecl_ == NULL) { unsigned int flags = 0; - bool is_init_fn = false; if (no->package() != NULL) { // Functions defined in other packages must be visible. @@ -6238,10 +6258,7 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) && !this->type_->is_method()) ; else if (no->name() == gogo->get_init_fn_name()) - { - flags |= Backend::function_is_visible; - is_init_fn = true; - } + flags |= Backend::function_is_visible; else if (Gogo::unpack_hidden_name(no->name()) == "main" && gogo->is_main_package()) flags |= Backend::function_is_visible; @@ -6255,29 +6272,13 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) flags |= Backend::function_is_visible; } - Type* rtype = NULL; - if (this->type_->is_method()) - rtype = this->type_->receiver()->type(); - - std::string asm_name; if (!this->asm_name_.empty()) { - asm_name = this->asm_name_; - // If an assembler name is explicitly specified, there must // be some reason to refer to the symbol from a different // object file. flags |= Backend::function_is_visible; } - else if (is_init_fn) - { - // These names appear in the export data and are used - // directly in the assembler code. If we change this here - // we need to change Gogo::init_imports. - asm_name = no->name(); - } - else - asm_name = gogo->function_asm_name(no->name(), no->package(), rtype); // If an inline body refers to this function, then it // needs to be visible in the symbol table. @@ -6337,13 +6338,36 @@ Function::get_or_make_decl(Gogo* gogo, Named_object* no) flags |= Backend::function_only_inline; Btype* functype = this->type_->get_backend_fntype(gogo); - this->fndecl_ = - gogo->backend()->function(functype, no->get_id(gogo), asm_name, - flags, this->location()); + + Backend_name bname; + this->backend_name(gogo, no, &bname); + + this->fndecl_ = gogo->backend()->function(functype, + bname.name(), + bname.optional_asm_name(), + flags, + this->location()); } return this->fndecl_; } +// Get the backend name. + +void +Function_declaration::backend_name(Gogo* gogo, Named_object* no, + Backend_name* bname) +{ + if (!this->asm_name_.empty()) + bname->set_asm_name(this->asm_name_); + else + { + Type* rtype = NULL; + if (this->fntype_->is_method()) + rtype = this->fntype_->receiver()->type(); + gogo->function_backend_name(no->name(), no->package(), rtype, bname); + } +} + // Get the backend representation. Bfunction* @@ -6375,21 +6399,16 @@ Function_declaration::get_or_make_decl(Gogo* gogo, Named_object* no) flags |= Backend::function_does_not_return; } - std::string asm_name; - if (this->asm_name_.empty()) - { - Type* rtype = NULL; - if (this->fntype_->is_method()) - rtype = this->fntype_->receiver()->type(); - asm_name = gogo->function_asm_name(no->name(), no->package(), rtype); - } - else if (go_id_needs_encoding(no->get_id(gogo))) - asm_name = go_encode_id(no->get_id(gogo)); - Btype* functype = this->fntype_->get_backend_fntype(gogo); - this->fndecl_ = - gogo->backend()->function(functype, no->get_id(gogo), asm_name, - flags, this->location()); + + Backend_name bname; + this->backend_name(gogo, no, &bname); + + this->fndecl_ = gogo->backend()->function(functype, + bname.name(), + bname.optional_asm_name(), + flags, + this->location()); } return this->fndecl_; @@ -7993,7 +8012,6 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function, type = Type::make_pointer_type(type); } - const std::string n = Gogo::unpack_hidden_name(name); Btype* btype = type->get_backend(gogo); Bvariable* bvar; @@ -8001,19 +8019,14 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function, bvar = Map_type::backend_zero_value(gogo); else if (this->is_global_) { - std::string var_name(package != NULL - ? package->package_name() - : gogo->package_name()); - var_name.push_back('.'); - var_name.append(n); - - std::string asm_name(gogo->global_var_asm_name(name, package)); + Backend_name bname; + gogo->global_var_backend_name(name, package, &bname); bool is_hidden = Gogo::is_hidden_name(name); // Hack to export runtime.writeBarrier. FIXME. // This is because go:linkname doesn't work on variables. if (gogo->compiling_runtime() - && var_name == "runtime.writeBarrier") + && bname.name() == "runtime.writeBarrier") is_hidden = false; // If an inline body refers to this variable, then it @@ -8028,8 +8041,12 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function, if (package != NULL) is_hidden = false; - bvar = backend->global_variable(var_name, - asm_name, + // For some reason asm_name can't be the empty string + // for global_variable, so we call asm_name rather than + // optional_asm_name here. FIXME. + + bvar = backend->global_variable(bname.name(), + bname.asm_name(), btype, package != NULL, is_hidden, @@ -8043,6 +8060,7 @@ Variable::get_backend_variable(Gogo* gogo, Named_object* function, } else { + const std::string n = Gogo::unpack_hidden_name(name); Bfunction* bfunction = function->func_value()->get_decl(); bool is_address_taken = (this->is_non_escaping_address_taken_ && !this->is_in_heap()); @@ -8216,7 +8234,13 @@ Named_constant::get_backend(Gogo* gogo, Named_object* const_no) if (type != NULL && type->is_numeric_type()) { Btype* btype = type->get_backend(gogo); - std::string name = const_no->get_id(gogo); + std::string name; + if (const_no->package() == NULL) + name = gogo->pkgpath(); + else + name = const_no->package()->pkgpath(); + name.push_back('.'); + name.append(Gogo::unpack_hidden_name(const_no->name())); const_decl = gogo->backend()->named_constant_expression(btype, name, const_decl, loc); @@ -8637,54 +8661,6 @@ Named_object::get_backend_variable(Gogo* gogo, Named_object* function) go_unreachable(); } -// Return the external identifier for this object. - -std::string -Named_object::get_id(Gogo* gogo) -{ - go_assert(!this->is_variable() - && !this->is_result_variable() - && !this->is_type()); - std::string decl_name; - if (this->is_function_declaration() - && !this->func_declaration_value()->asm_name().empty()) - decl_name = this->func_declaration_value()->asm_name(); - else - { - std::string package_name; - if (this->package_ == NULL) - package_name = gogo->package_name(); - else - package_name = this->package_->package_name(); - - // Note that this will be misleading if this is an unexported - // method generated for an embedded imported type. In that case - // the unexported method should have the package name of the - // package from which it is imported, but we are going to give - // it our package name. Fixing this would require knowing the - // package name, but we only know the package path. It might be - // better to use package paths here anyhow. This doesn't affect - // the assembler code, because we always set that name in - // Function::get_or_make_decl anyhow. FIXME. - - decl_name = package_name + '.' + Gogo::unpack_hidden_name(this->name_); - - Function_type* fntype; - if (this->is_function()) - fntype = this->func_value()->type(); - else if (this->is_function_declaration()) - fntype = this->func_declaration_value()->type(); - else - fntype = NULL; - if (fntype != NULL && fntype->is_method()) - { - decl_name.push_back('.'); - decl_name.append(fntype->receiver()->type()->mangled_name(gogo)); - } - } - return decl_name; -} - void debug_go_named_object(Named_object* no) { diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index 45be173..f22d476 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -57,6 +57,101 @@ class Node; // This file declares the basic classes used to hold the internal // representation of Go which is built by the parser. +// The name of some backend object. Backend objects have a +// user-visible name and an assembler name. The user visible name +// might include arbitrary Unicode characters. The assembler name +// will not. + +class Backend_name +{ + public: + Backend_name() + : prefix_(NULL), components_(), count_(0), suffix_(), + is_asm_name_(false) + {} + + // Set the prefix. Prefixes are always constant strings. + void + set_prefix(const char* p) + { + go_assert(this->prefix_ == NULL && !this->is_asm_name_); + this->prefix_ = p; + } + + // Set the suffix. + void + set_suffix(const std::string& s) + { + go_assert(this->suffix_.empty() && !this->is_asm_name_); + this->suffix_ = s; + } + + // Append to the suffix. + void + append_suffix(const std::string& s) + { + if (this->is_asm_name_) + this->components_[0].append(s); + else + this->suffix_.append(s); + } + + // Add a component. + void + add(const std::string& c) + { + go_assert(this->count_ < Backend_name::max_components + && !this->is_asm_name_); + this->components_[this->count_] = c; + ++this->count_; + } + + // Set an assembler name specified by the user. This overrides both + // the user-visible name and the assembler name. No further + // encoding is applied. + void + set_asm_name(const std::string& n) + { + go_assert(this->prefix_ == NULL + && this->count_ == 0 + && this->suffix_.empty() + && !this->is_asm_name_); + this->components_[0] = n; + this->is_asm_name_ = true; + } + + // Get the user visible name. + std::string + name() const; + + // Get the assembler name. This may be the same as the user visible + // name. + std::string + asm_name() const; + + // Get an optional assembler name: if it would be the same as the + // user visible name, this returns the empty string. + std::string + optional_asm_name() const; + + private: + // The maximum number of components. + static const int max_components = 4; + + // An optional prefix that does not require encoding. + const char *prefix_; + // Up to four components. The name will include these components + // separated by dots. Each component will be underscore-encoded + // (see the long comment near the top of names.cc). + std::string components_[Backend_name::max_components]; + // Number of components. + int count_; + // An optional suffix that does not require encoding. + std::string suffix_; + // True if components_[0] is an assembler name specified by the user. + bool is_asm_name_; +}; + // An initialization function for an imported package. This is a // magic function which initializes variables and runs the "init" // function. @@ -613,7 +708,7 @@ class Gogo // is used when a type-specific hash function is needed when not at // top level. void - queue_hash_function(Type* type, int64_t size, const std::string& hash_name, + queue_hash_function(Type* type, int64_t size, Backend_name*, Function_type* hash_fntype); // Queue up a type-specific equal function to be written out. This @@ -621,8 +716,7 @@ class Gogo // top level. void queue_equal_function(Type* type, Named_type* name, int64_t size, - const std::string& equal_name, - Function_type* equal_fntype); + Backend_name*, Function_type* equal_fntype); // Write out queued specific type functions. void @@ -869,31 +963,32 @@ class Gogo Expression* allocate_memory(Type *type, Location); - // Return the assembler name to use for an exported function, a - // method, or a function/method declaration. - std::string - function_asm_name(const std::string& go_name, const Package*, - const Type* receiver); + // Get the backend name to use for an exported function, a method, + // or a function/method declaration. + void + function_backend_name(const std::string& go_name, const Package*, + const Type* receiver, Backend_name*); // Return the name to use for a function descriptor. - std::string - function_descriptor_name(Named_object*); + void + function_descriptor_backend_name(Named_object*, Backend_name*); // Return the name to use for a generated stub method. std::string stub_method_name(const Package*, const std::string& method_name); - // Return the name of the hash function for TYPE. - std::string - hash_function_name(const Type*); + // Get the backend name of the hash function for TYPE. + void + hash_function_name(const Type*, Backend_name*); - // Return the name of the equal function for TYPE. - std::string - equal_function_name(const Type*, const Named_type*); + // Get the backend name of the equal function for TYPE. + void + equal_function_name(const Type*, const Named_type*, Backend_name*); - // Return the assembler name to use for a global variable. - std::string - global_var_asm_name(const std::string& go_name, const Package*); + // Get the backend name to use for a global variable. + void + global_var_backend_name(const std::string& go_name, const Package*, + Backend_name*); // Return a name to use for an error case. This should only be used // after reporting an error, and is used to avoid useless knockon @@ -961,13 +1056,14 @@ class Gogo // Return the package path symbol from an init function name, which // can be a real init function or a dummy one. std::string - pkgpath_from_init_fn_name(std::string); + pkgpath_symbol_from_init_fn_name(std::string); - // Return the name for a type descriptor symbol. - std::string - type_descriptor_name(const Type*, Named_type*); + // Get the backend name for a type descriptor symbol. + void + type_descriptor_backend_name(const Type*, Named_type*, Backend_name*); // Return the name of the type descriptor list symbol of a package. + // The argument is an encoded pkgpath, as with pkgpath_symbol. std::string type_descriptor_list_symbol(const std::string&); @@ -987,11 +1083,11 @@ class Gogo std::string interface_method_table_name(Interface_type*, Type*, bool is_pointer); - // Return whether NAME is a special name that can not be passed to - // unpack_hidden_name. This is needed because various special names - // use "..SUFFIX", but unpack_hidden_name just looks for '.'. - static bool - is_special_name(const std::string& name); + // If NAME is a special name used as a Go identifier, return the + // position within the string where the special part of the name + // occurs. + static size_t + special_name_pos(const std::string& name); private: // During parsing, we keep a stack of functions. Each function on @@ -1056,6 +1152,9 @@ class Gogo Named_object* write_barrier_variable(); + static bool + is_digits(const std::string&); + // Type used to map import names to packages. typedef std::map<std::string, Package*> Imports; @@ -1079,15 +1178,15 @@ class Gogo Named_type* name; int64_t size; Specific_type_function_kind kind; - std::string fnname; + Backend_name bname; Function_type* fntype; Specific_type_function(Type* atype, Named_type* aname, int64_t asize, Specific_type_function_kind akind, - const std::string& afnname, + Backend_name* abname, Function_type* afntype) : type(atype), name(aname), size(asize), kind(akind), - fnname(afnname), fntype(afntype) + bname(*abname), fntype(afntype) { } }; @@ -1631,6 +1730,10 @@ class Function Bstatement* return_value(Gogo*, Named_object*, Location) const; + // Get the backend name of this function. + void + backend_name(Gogo*, Named_object*, Backend_name*); + // Get an expression for the variable holding the defer stack. Expression* defer_stack(Location); @@ -1872,6 +1975,10 @@ class Function_declaration void build_backend_descriptor(Gogo*); + // Get the backend name of this function declaration. + void + backend_name(Gogo*, Named_object*, Backend_name*); + // Export a function declaration. void export_func(Export* exp, const Named_object* no) const @@ -2863,10 +2970,6 @@ class Named_object Bvariable* get_backend_variable(Gogo*, Named_object* function); - // Return the external identifier for this object. - std::string - get_id(Gogo*); - // Get the backend representation of this object. void get_backend(Gogo*, std::vector<Bexpression*>&, std::vector<Btype*>&, diff --git a/gcc/go/gofrontend/names.cc b/gcc/go/gofrontend/names.cc index 1f0a545..0097417 100644 --- a/gcc/go/gofrontend/names.cc +++ b/gcc/go/gofrontend/names.cc @@ -15,53 +15,68 @@ // assembly code. This is not used for names that appear only in the // debug info. -// Our external names contain only ASCII alphanumeric characters, +// Our external names may contain only ASCII alphanumeric characters, // underscore, and dot. (According to the GCC sources, dot is not // permitted in assembler symbols on VxWorks and MMIX. We will not -// support those systems.) Go names can not contain dot, so we rely -// on using dot to encode Unicode characters, and to separate Go -// symbols by package, and so forth. We assume that none of the -// non-Go symbols in the final link will contain a dot, so we don't -// worry about conflicts. +// support those systems.) Go identifiers cannot contain dot, but Go +// package paths can. Both Go identifiers and package paths can, of +// course, contain all sorts of Unicode characters. +// +// The gc compiler uses names like "pkg.F", and it seems convenient to +// emulate that. Therefore, we will use dot to separate different +// components of names. +// +// Since package paths can contain dot, to avoid ambiguity we must +// encode package paths such that they do not contain any dot. The +// natural way to do this is to encode forbidden characters, including +// dot, using a notation based on underscore. We will, of course, +// have to encode underscore itself. +// +// Since we will be using an underscore encoding for the package path, +// it seems reasonable to use the same encoding for Go identifiers. +// This has the disadvantage that encoded Go identifiers will appear +// to be valid Go identifiers with funny spellings, but it seems like +// the best available approach. +// +// Therefore, in the following discussion we may assume that none of +// the names under discussion contain a dot. All of the names we +// generate for Go identifiers (that don't use //export or +// //go:linkname) will contain at least one dot, as discussed below. +// We assume that none of the non-Go symbols in the final link will +// contain a dot, so we don't worry about conflicts. // // We first describe the basic symbol names, used to represent Go -// functions and variables. These never start with a dot, never end -// with a dot, never contain two consecutive dots, and never contain a -// dot followed by a digit. +// functions and variables. // // The external name for a normal Go symbol NAME, a function or // variable, is simply "PKGPATH.NAME". Note that NAME is not the // packed form used for the "hidden" name internally in the compiler; -// it is the name that appears in the source code. PKGPATH is the -// -fgo-pkgpath option as adjusted by Gogo::pkgpath_for_symbol. Note -// that PKGPATH can not contain a dot and neither can NAME. Also, -// NAME may not begin with a digit. NAME may require further encoding -// for non-ASCII characters as described below, but until that -// encoding these symbols contain exactly one dot, and they do not -// start with a dot. +// it is the name that appears in the source code. Both PKGPATH and +// NAME will be encoded as described below. The encoding process +// ensures that neither encoded string can contain a dot, and neither +// will start with a digit (NAME is a Go identifier that can't contain +// a dot or start with a digit anyhow). The encoding process means +// that these external names contain exactly one dot and do not start +// with a dot. // // The external name for a method NAME for a named type TYPE is -// "PKGPATH.TYPE.NAME". Unlike the gc compiler, the external name -// does not indicate whether this is a pointer method or a value -// method; a named type can not have both a pointer and value method -// with the same name, so there is no ambiguity. PKGPATH is the -// package path of the package in which TYPE is defined. Here none of -// PKGPATH, TYPE, or NAME can be empty or contain a dot, and neither -// TYPE nor NAME may begin with a digit. Before encoding these names -// contain exactly two dots, not consecutive, and they do not start -// with a dot. +// "PKGPATH.TYPE.NAME". Both NAME and TYPE are simple Go identifiers. +// Unlike the gc compiler, the external name does not indicate whether +// this is a pointer method or a value method; a named type can not +// have both a pointer and value method with the same name, so there +// is no ambiguity. PKGPATH is the package path of the package in +// which TYPE is defined. PKGPATH, TYPE, and NAME are encoded, and +// cannot be empty or contain a dot or start with a digit. These +// external names contain exactly two dots, not consecutive, and they +// do not start with a dot. // // It's uncommon, but the use of type literals with embedded fields // can cause us to have methods on unnamed types. The external names -// for these are also PKGPATH.TYPE.NAME, where TYPE is an +// for these are also PKGPATH.TYPELIT.NAME, where TYPELIT is an // approximately readable version of the type literal, described -// below. As the type literal encoding always contains multiple dots, -// these names always contain more than two dots. Although the type -// literal encoding contains dots, neither PKGPATH nor NAME can -// contain a dot, and neither TYPE nor NAME can begin with a digit. -// The effect is that PKGPATH is always the portion of the name before -// the first dot and NAME is always the portion after the last dot. -// There is no ambiguity as long as encoded type literals are +// below. A TYPELIT will always contain characters that cannot appear +// in a Go identifier, so TYPELIT can never be confused with a TYPE +// name. There is no ambiguity as long as encoded type literals are // unambiguous. // // Also uncommon is an external name that must refer to a named type @@ -91,46 +106,51 @@ // the function with an added suffix "..f". // // A thunk for a go or defer statement is treated as a function whose -// name is ".thunkNN" where NN is a sequence of digits (these -// functions are never globally visible). Thus the final name of a -// thunk will be PKGPATH..thunkNN. +// name is ".thunkNN", unencoded, where NN is a sequence of digits +// (these functions are never globally visible). Thus the final name +// of a thunk will be PKGPATH..thunkNN (PKGPATH is encoded). // -// An init function is treated as a function whose name is ".initNN" -// where NN is a sequence of digits (these functions are never -// globally visible). Thus the final name of an init function will be -// PKGPATH..initNN. +// An init function is treated as a function whose name is ".initNN", +// unencoded, where NN is a sequence of digits (these functions are +// never globally visible). Thus the final name of an init function +// will be PKGPATH..initNN (PKGPATH is encoded). // // A nested function is given the name of outermost enclosing function -// or method with an added suffix "..funcNN" where NN is a sequence of -// digits. Note that the function descriptor of a nested function, if -// needed, will end with "..funcNN..f". +// or method with an added suffix "..funcNN", unencoded, where NN is a +// sequence of digits. Note that the function descriptor of a nested +// function, if needed, will end with "..funcNN..f". // // A recover thunk is the same as the name of the function with an // added suffix "..r". // -// The name of a type descriptor for a named type is PKGPATH.TYPE..d. +// The name of a type descriptor for a named type is +// PKGPATH.TYPENAME..d (PKGPATH and TYPENAME are encoded). // -// The name of a type descriptor for an unnamed type is type..TYPE. -// That is, the string "type.." followed by the type literal encoding. +// The name of a type descriptor for a pointer to a named type is +// PKGPATH.TYPENAME..p (PKGPATH and TYPENAME are encoded). +// +// The name of a type descriptor for an unnamed type is type..TYPELIT. +// That is, the string "type.." followed by the encoded type literal. // These names are common symbols, in the linker's sense of the word // common: in the final executable there is only one instance of the -// type descriptor for a given unnamed type. The type literal -// encoding can never start with a digit or with 'u' or 'U'. +// type descriptor for a given unnamed type. // -// The name of the GC symbol for a named type is PKGPATH.TYPE..g. +// The name of the GC symbol for a named type is PKGPATH.TYPE..g +// (PKGPATH and TYPE are encoded). // -// The name of the GC symbol for an unnamed type is typeg..TYPE. +// The name of the GC symbol for an unnamed type is type..TYPELIT..g. // These are common symbols. // // The name of a ptrmask symbol is gcbits..B32 where B32 is an -// encoding of the ptrmask bits using only ASCII letters without 'u' -// or 'U'. These are common symbols. +// encoding of the ptrmask bits using only ASCII letters. These are +// common symbols. // // An interface method table for assigning the non-interface type TYPE // to the interface type ITYPE is named imt..ITYPE..TYPE. If ITYPE or -// TYPE is a named type, they are written as PKGPATH.TYPE. Otherwise -// they are written as a type literal. An interface method table for -// a pointer method set uses pimt instead of imt. +// TYPE is a named type, they are written as PKGPATH.TYPE (where both +// PKGPATH and TYPE are encoded). Otherwise they are written as a +// type literal. An interface method table for a pointer method set +// uses pimt instead of imt. // // The names of composite literal initializers, including the GC root // variable, are not referenced. They must not conflict with any C @@ -147,7 +167,7 @@ // PKGPATH..import. If a package doesn't need an init function, it // will have a dummy one, named ~PKGPATH. // -// In each pacakge there is a list of all the type descriptors defined +// In each package there is a list of all the type descriptors defined // in this package. The name of the list is PKGPATH..types. // // In the main package it gathers all the type descriptor lists in a @@ -161,109 +181,109 @@ // The type literal encoding is not quite valid Go, as some aspects of // compiler generated types can not be represented. For example, // incomparable struct types have an extra field "{x}". Struct tags -// are quoted inside curly braces, rather than introduce an encoding -// for quotes. Struct tags can contain any character, so any single -// byte Unicode character that is not alphanumeric or underscore is -// replaced with .xNN where NN is the hex encoding. +// can contain any character, which will be underscore encoded as +// usual. In the unusual case of a curly brace or a backslash in a +// struct tag, the brace or backslash will be backslash quoted, before +// underscore encoding. +// +// The underscore encoding is, naturally, an underscore followed by +// other characters. As there are various characters that commonly +// appear in type literals and in package paths, we have a set of +// short encodings. Then we have general encodings for other +// characters. // -// There is a simple encoding for glue characters in type literals: -// .0 - ' ' -// .1 - '*' -// .2 - ';' -// .3 - ',' -// .4 - '{' -// .5 - '}' -// .6 - '[' -// .7 - ']' -// .8 - '(' -// .9 - ')' -// This is unambiguous as, although the type literal can contain a dot -// as shown above, those dots are always followed by a name and names -// can not begin with a digit. A dot is always followed by a name or -// a digit, and a type literal can neither start nor end with a dot, -// so this never introduces consecutive dots. +// __ - '_' +// _0 - '.' +// _1 - '/' +// _2 - '*' +// _3 - ',' +// _4 - '{' +// _5 - '}' +// _6 - '[' +// _7 - ']' +// _8 - '(' +// _9 - ')' +// _a - '"' +// _b - ' ' +// _c - ';' // -// Struct tags can contain any character, so they need special -// treatment. Alphanumerics, underscores, and Unicode characters that -// require more than a single byte are left alone (Unicode characters -// will be encoded later, as described below). Other single bytes -// characters are replace with .xNN where NN is the hex encoding. +// Other non-alphanumeric ASCII characters are encoded as _xNN, where +// NN is the hex value for the character. If an encoded name would +// otherwise start with a digit, this encoding is also used for the +// leading digit. // -// Since Go identifiers can contain Unicode characters, we must encode -// them into ASCII. We do this last, after the name is generated as -// described above and after type literals are encoded. To make the -// encoding unambiguous, we introduce it with two consecutive dots. -// This is followed by the letter u and four hex digits or the letter -// U and eight digits, just as in the language only using ..u and ..U -// instead of \u and \U. The compiler also produces identifiers that -// are qualified by package path, which means that there may also be ASCII -// characters that are not assembler-friendly (ex: '=', '/'). The encoding -// scheme translates such characters into the "..zNN" where NN is the -// hex value for the character. Since before this encoding names can never -// contain consecutive dots followed by 'z', 'u' or 'U', and after this -// encoding "..z", "..u" and "..U" are followed by a known number of -// characters, this is unambiguous. +// Non-ASCII Unicode characters are encoded as _u and four hex digits +// or _U and eight digits, just as in the language only using _u and +// _U instead of \u and \U. // // Demangling these names is straightforward: -// - replace ..zXX with an ASCII character -// - replace ..uXXXX with a unicode character -// - replace ..UXXXXXXXX with a unicode character -// - replace .D, where D is a digit, with the character from the above +// - replace _xXX with an ASCII character +// - replace _uXXXX with a unicode character +// - replace _UXXXXXXXX with a unicode character +// - replace _C per the table above // That will get you as close as possible to a readable name. -// Return the assembler name to use for an exported function, a -// method, or a function/method declaration. This is not called if -// the function has been given an explicit name via a magic //extern -// or //go:linkname comment. GO_NAME is the name that appears in the -// Go code. PACKAGE is the package where the function is defined, and -// is NULL for the package being compiled. For a method, RTYPE is +// Set BNAME to the name to use for an exported function, a method, or +// a function/method declaration. GO_NAME is the name that appears in +// the Go code. PACKAGE is the package where the function is defined, +// and is NULL for the package being compiled. For a method, RTYPE is // the method's receiver type; for a function, RTYPE is NULL. -std::string -Gogo::function_asm_name(const std::string& go_name, const Package* package, - const Type* rtype) +void +Gogo::function_backend_name(const std::string& go_name, + const Package* package, const Type* rtype, + Backend_name* bname) { - std::string ret; if (rtype != NULL) - ret = rtype->deref()->mangled_name(this); + rtype->deref()->backend_name(this, bname); else if (package == NULL) - ret = this->pkgpath(); + bname->add(this->pkgpath()); else - ret = package->pkgpath(); - ret.push_back('.'); - // Check for special names that will break if we use - // Gogo::unpack_hidden_name. - if (Gogo::is_special_name(go_name)) - ret.append(go_name); + bname->add(package->pkgpath()); + + size_t pos = Gogo::special_name_pos(go_name); + if (pos == std::string::npos) + bname->add(Gogo::unpack_hidden_name(go_name)); else - ret.append(Gogo::unpack_hidden_name(go_name)); - return go_encode_id(ret); + { + if (pos > 0) + bname->add(go_name.substr(0, pos)); + bname->set_suffix(go_name.substr(pos)); + } } -// Return the name to use for a function descriptor. These symbols -// are globally visible. +// Set BNAME to the name to use for a function descriptor. These +// symbols are globally visible. -std::string -Gogo::function_descriptor_name(Named_object* no) +void +Gogo::function_descriptor_backend_name(Named_object* no, + Backend_name* bname) { - if (no->is_function() && !no->func_value()->asm_name().empty()) - return no->func_value()->asm_name() + "..f"; - else if (no->is_function_declaration() - && !no->func_declaration_value()->asm_name().empty()) - return no->func_declaration_value()->asm_name() + "..f"; - std::string ret = this->function_asm_name(no->name(), no->package(), NULL); - ret.append("..f"); - return ret; + if (no->is_function()) + no->func_value()->backend_name(this, no, bname); + else if (no->is_function_declaration()) + no->func_declaration_value()->backend_name(this, no, bname); + else + go_unreachable(); + bname->append_suffix("..f"); } -// Return the name to use for a generated stub method. MNAME is the -// method name. PACKAGE is the package where the type that needs this -// stub method is defined. These functions are globally visible. -// Note that this is the function name that corresponds to the name -// used for the method in Go source code, if this stub method were -// written in Go. The assembler name will be generated by -// Gogo::function_asm_name, and because this is a method that name -// will include the receiver type. +// Return the name to use for a generated stub method. A stub method +// is used as the method table entry for a promoted method of an +// embedded type. MNAME is the method name. PACKAGE is the package +// where the type that needs this stub method is defined. These +// functions are globally visible. +// +// This returns a name that acts like a Go identifier, as though the +// stub method were written in Go as an explicitly defined method that +// simply calls the promoted method. The name we return here will +// eventually be passed to function_backend_name, which will return a +// name that includes the receiver type. +// +// We construct a unique method name and append "..stub". +// function_backend_name will look for the "..stub" and turn that into +// an unencoded suffix. The rest of the name will be encoded as +// usual. std::string Gogo::stub_method_name(const Package* package, const std::string& mname) @@ -279,56 +299,70 @@ Gogo::stub_method_name(const Package* package, const std::string& mname) return Gogo::unpack_hidden_name(mname) + "..stub"; // We are creating a stub method for an unexported method of an - // imported embedded type. We need to disambiguate the method name. - std::string ret = mpkgpath; + // imported embedded type. A single type can have multiple promoted + // methods with the same unexported name, if it embeds types from + // different packages. We need to disambiguate the method name. + // This produces an unambiguous name because even though MPKGPATH + // can be anything, we know that MNAME does not contain a dot. The + // dot we return here, between MPKGPATH and MNAME, will wind up + // being underscore encoded. + std::string ret(mpkgpath); ret.push_back('.'); ret.append(Gogo::unpack_hidden_name(mname)); ret.append("..stub"); return ret; } -// Return the name of the hash function for TYPE. +// Set BNAME to the name of the hash function for TYPE. -std::string -Gogo::hash_function_name(const Type* type) +void +Gogo::hash_function_name(const Type* type, Backend_name* bname) { - std::string tname = type->mangled_name(this); - return tname + "..hash"; + if (type->named_type() != NULL) + type->backend_name(this, bname); + else + { + bname->add(this->pkgpath()); + type->backend_name(this, bname); + } + bname->set_suffix("..hash"); } -// Return the name of the equal function for TYPE. If NAME is not -// NULL it is the name of the type. +// Set BNAME to the name of the equal function for TYPE. If NAME is +// not NULL it is the name of the type. -std::string -Gogo::equal_function_name(const Type* type, const Named_type* name) +void +Gogo::equal_function_name(const Type* type, const Named_type* name, + Backend_name* bname) { - const Type* rtype = type; if (name != NULL) - rtype = name; - std::string tname = rtype->mangled_name(this); - return tname + "..eq"; + name->backend_name(this, bname); + else + { + bname->add(this->pkgpath()); + type->backend_name(this, bname); + } + bname->set_suffix("..eq"); } -// Return the assembler name to use for a global variable. GO_NAME is -// the name that appears in the Go code. PACKAGE is the package where -// the variable is defined, and is NULL for the package being -// compiled. +// Set BNAME to the name to use for a global variable. GO_NAME is the +// name that appears in the Go code. PACKAGE is the package where the +// variable is defined, and is NULL for the package being compiled. -std::string -Gogo::global_var_asm_name(const std::string& go_name, const Package* package) +void +Gogo::global_var_backend_name(const std::string& go_name, + const Package* package, + Backend_name* bname) { - std::string ret; if (package == NULL) - ret = this->pkgpath(); + bname->add(this->pkgpath()); else - ret = package->pkgpath(); - ret.append(1, '.'); - ret.append(Gogo::unpack_hidden_name(go_name)); - return go_encode_id(ret); + bname->add(package->pkgpath()); + bname->add(Gogo::unpack_hidden_name(go_name)); } // Return an erroneous name that indicates that an error has already -// been reported. +// been reported. This name will act like a Go identifier. std::string Gogo::erroneous_name() @@ -349,7 +383,10 @@ Gogo::is_erroneous_name(const std::string& name) return name.compare(0, 10, ".erroneous") == 0; } -// Return a name for a thunk object. +// Return a name for a thunk object. This name will act like a Go +// identifier. The name returned here will eventually be passed to +// function_backend_name, which will pull off the ..thunk as an +// unencoded suffix. std::string Gogo::thunk_name() @@ -358,7 +395,12 @@ Gogo::thunk_name() char thunk_name[50]; snprintf(thunk_name, sizeof thunk_name, "..thunk%d", thunk_count); ++thunk_count; - std::string ret = this->pkgpath(); + // We don't want to return a name that starts with a dot, as that + // will confuse Gogo::is_hidden_name. And we don't want to change + // ..thunk, which fits our general theme and is used by code like + // runtime.Callers. But the prefix doesn't matter, as the actual + // name will include the package path. + std::string ret = "go"; return ret + thunk_name; } @@ -368,13 +410,10 @@ bool Gogo::is_thunk(const Named_object* no) { const std::string& name(no->name()); - size_t i = name.find("..thunk"); + size_t i = name.rfind("..thunk"); if (i == std::string::npos) return false; - for (i += 7; i < name.size(); ++i) - if (name[i] < '0' || name[i] > '9') - return false; - return true; + return Gogo::is_digits(name.substr(i + 7)); } // Return the name to use for an init function. There can be multiple @@ -387,11 +426,12 @@ Gogo::init_function_name() char buf[30]; snprintf(buf, sizeof buf, "..init%d", init_count); ++init_count; - std::string ret = this->pkgpath(); - return ret + buf; + return this->pkgpath() + buf; } -// Return the name to use for a nested function. +// Return the name to use for a nested function. This name acts like +// a Go identifier. This name will be rewritten by +// Function::backend_name. std::string Gogo::nested_function_name(Named_object* enclosing) @@ -420,7 +460,9 @@ Gogo::nested_function_name(Named_object* enclosing) enclosing->func_value()->type()->receiver(); if (rcvr != NULL) { - prefix = rcvr->type()->mangled_name(this); + Backend_name bname; + rcvr->type()->backend_name(this, &bname); + prefix = bname.name(); prefix.push_back('.'); } prefix.append(Gogo::unpack_hidden_name(enclosing->name())); @@ -460,7 +502,8 @@ Gogo::redefined_function_name() } // Return the name to use for a recover thunk for the function NAME. -// If the function is a method, RTYPE is the receiver type. +// If the function is a method, RTYPE is the receiver type. This is a +// name that acts like a Go identifier. std::string Gogo::recover_thunk_name(const std::string& name, const Type* rtype) @@ -468,10 +511,12 @@ Gogo::recover_thunk_name(const std::string& name, const Type* rtype) std::string ret; if (rtype != NULL) { - ret = rtype->mangled_name(this); + Backend_name bname; + rtype->backend_name(this, &bname); + ret = bname.name(); ret.append(1, '.'); } - if (Gogo::is_special_name(name)) + if (Gogo::special_name_pos(name) != std::string::npos) ret.append(name); else ret.append(Gogo::unpack_hidden_name(name)); @@ -504,8 +549,8 @@ Gogo::initializer_name() return buf; } -// Return the name of the variable used to represent the zero value of -// a map. This is a globally visible common symbol. +// Return the assembler name of the variable used to represent the +// zero value of a map. This is a globally visible common symbol. std::string Gogo::map_zero_value_name() @@ -513,7 +558,9 @@ Gogo::map_zero_value_name() return "go..zerovalue"; } -// Return the name to use for the import control function. +// Return the name to use for the import control function. This name +// is handled specially by Function::backend_name. It is not encoded +// further. const std::string& Gogo::get_init_fn_name() @@ -550,7 +597,7 @@ Gogo::dummy_init_fn_name() // can be a real init function or a dummy one. std::string -Gogo::pkgpath_from_init_fn_name(std::string name) +Gogo::pkgpath_symbol_from_init_fn_name(std::string name) { go_assert(!name.empty()); if (name[0] == '~') @@ -561,60 +608,38 @@ Gogo::pkgpath_from_init_fn_name(std::string name) go_unreachable(); } -// Return a mangled name for a type. These names appear in symbol -// names in the assembler file for things like type descriptors and -// methods. +// Set BNAME to a name for a type to use in a symbol. Return a name +// for a type to use in a symbol. These names appear in symbol names +// in the assembler file for things like type descriptors and methods. -std::string -Type::mangled_name(Gogo* gogo) const +void +Type::backend_name(Gogo* gogo, Backend_name* bname) const { - std::string ret; - - // The do_mangled_name virtual function will set RET to the mangled - // name before glue character mapping. - this->do_mangled_name(gogo, &ret); - - // Type descriptor names and interface method table names use a ".." - // before the mangled name of a type, so to avoid ambiguity the - // mangled name must not start with 'u' or 'U' or a digit. - go_assert((ret[0] < '0' || ret[0] > '9') && ret[0] != ' '); - if (ret[0] == 'u' || ret[0] == 'U') - ret = " " + ret; - - // Map glue characters as described above. - - // The mapping is only unambiguous if there is no .DIGIT in the - // string, so check that. - for (size_t i = ret.find('.'); - i != std::string::npos; - i = ret.find('.', i + 1)) + // Special case top level named types to get nicer name encodings + // for this common case. + const Named_type* nt = this->unalias()->named_type(); + if (nt != NULL && !nt->is_builtin()) { - if (i + 1 < ret.size()) + unsigned int index; + if (nt->in_function(&index) == NULL) { - char c = ret[i + 1]; - go_assert(c < '0' || c > '9'); + const Named_object* no = nt->named_object(); + if (no->package() == NULL) + bname->add(gogo->pkgpath()); + else + bname->add(no->package()->pkgpath()); + bname->add(Gogo::unpack_hidden_name(no->name())); + return; } } - // The order of these characters is the replacement code. - const char * const replace = " *;,{}[]()"; + std::string name; - const size_t rlen = strlen(replace); - char buf[2]; - buf[0] = '.'; - for (size_t ri = 0; ri < rlen; ++ri) - { - buf[1] = '0' + ri; - while (true) - { - size_t i = ret.find(replace[ri]); - if (i == std::string::npos) - break; - ret.replace(i, 1, buf, 2); - } - } + // The do_symbol_name virtual function will set RET to the mangled + // name before encoding. + this->do_mangled_name(gogo, &name); - return ret; + bname->add(name); } // The mangled name is implemented as a method on each instance of @@ -701,12 +726,7 @@ Function_type::do_mangled_name(Gogo* gogo, std::string* ret) const else ret->push_back(','); if (this->is_varargs_ && p + 1 == params->end()) - { - // We can't use "..." here because the mangled name - // might start with 'u' or 'U', which would be ambiguous - // with the encoding of Unicode characters. - ret->append(",,,"); - } + ret->append("..."); this->append_mangled_name(p->type(), gogo, ret); } } @@ -776,15 +796,15 @@ Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const if (p->is_anonymous() && p->type()->named_type() != NULL && p->type()->named_type()->is_alias()) - p->type()->named_type()->append_mangled_type_name(gogo, true, ret); + p->type()->named_type()->append_symbol_type_name(gogo, true, ret); else this->append_mangled_name(p->type(), gogo, ret); if (p->has_tag()) { // Use curly braces around a struct tag, since they are - // unambiguous here and we have no encoding for - // quotation marks. + // unambiguous here and struct tags rarely contain curly + // braces. ret->push_back('{'); ret->append(go_mangle_struct_tag(p->tag())); ret->push_back('}'); @@ -837,10 +857,10 @@ void Channel_type::do_mangled_name(Gogo* gogo, std::string* ret) const { if (!this->may_send_) - ret->append("{}"); + ret->append("<-"); ret->append("chan"); if (!this->may_receive_) - ret->append("{}"); + ret->append("<-"); ret->push_back(' '); this->append_mangled_name(this->element_type_, gogo, ret); } @@ -883,7 +903,7 @@ Interface_type::do_mangled_name(Gogo* gogo, std::string* ret) const void Named_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - this->append_mangled_type_name(gogo, false, ret); + this->append_symbol_type_name(gogo, false, ret); } void @@ -903,13 +923,13 @@ Forward_declaration_type::do_mangled_name(Gogo* gogo, std::string* ret) const } } -// Append the mangled name for a named type to RET. For an alias we +// Append the symbol name for a named type to RET. For an alias we // normally use the real name, but if USE_ALIAS is true we use the // alias name itself. void -Named_type::append_mangled_type_name(Gogo* gogo, bool use_alias, - std::string* ret) const +Named_type::append_symbol_type_name(Gogo* gogo, bool use_alias, + std::string* ret) const { if (this->is_error_) return; @@ -933,7 +953,11 @@ Named_type::append_mangled_type_name(Gogo* gogo, bool use_alias, const Typed_identifier* rcvr = this->in_function_->func_value()->type()->receiver(); if (rcvr != NULL) - ret->append(rcvr->type()->deref()->mangled_name(gogo)); + { + Backend_name bname; + rcvr->type()->deref()->backend_name(gogo, &bname); + ret->append(bname.name()); + } else if (this->in_function_->package() == NULL) ret->append(gogo->pkgpath()); else @@ -956,23 +980,46 @@ Named_type::append_mangled_type_name(Gogo* gogo, bool use_alias, if (this->in_function_ != NULL && this->in_function_index_ > 0) { char buf[30]; - snprintf(buf, sizeof buf, "..i%u", this->in_function_index_); + snprintf(buf, sizeof buf, ".i%u", this->in_function_index_); ret->append(buf); } } -// Return the name for the type descriptor symbol for TYPE. This can -// be a global, common, or local symbol, depending. NT is not NULL if -// it is the name to use. +// Given a name which may or may not have been hidden, append the +// appropriate version of the name to the result string. -std::string -Gogo::type_descriptor_name(const Type* type, Named_type* nt) +void +Gogo::append_possibly_hidden_name(std::string *result, const std::string& name) +{ + if (!Gogo::is_hidden_name(name)) + *result += name; + else + *result += name.substr(1); +} + +// Set BNAME to the name for the type descriptor symbol for TYPE. +// This can be a global, common, or local symbol, depending. NT is +// not NULL if it is the name to use. + +void +Gogo::type_descriptor_backend_name(const Type* type, Named_type* nt, + Backend_name* bname) { // The type descriptor symbol for the unsafe.Pointer type is defined // in libgo/runtime/go-unsafe-pointer.c, so just use a reference to // that symbol for all unsafe pointer types. if (type->is_unsafe_pointer_type()) - return "unsafe.Pointer..d"; + { + bname->set_asm_name("unsafe.Pointer..d"); + return; + } + + bool is_pointer = false; + if (nt == NULL && type->points_to() != NULL) + { + nt = type->points_to()->named_type(); + is_pointer = true; + } if (nt == NULL) { @@ -981,63 +1028,28 @@ Gogo::type_descriptor_name(const Type* type, Named_type* nt) // using a named type, like "int". go_assert(!type->is_basic_type()); - return "type.." + type->mangled_name(this); + type->backend_name(this, bname); + bname->set_prefix("type.."); } - - std::string ret; - Named_object* no = nt->named_object(); - unsigned int index; - const Named_object* in_function = nt->in_function(&index); - if (nt->is_builtin()) - go_assert(in_function == NULL); else { - if (in_function != NULL) - { - const Typed_identifier* rcvr = - in_function->func_value()->type()->receiver(); - if (rcvr != NULL) - ret.append(rcvr->type()->deref()->mangled_name(this)); - else if (in_function->package() == NULL) - ret.append(this->pkgpath()); - else - ret.append(in_function->package()->pkgpath()); - ret.push_back('.'); - ret.append(Gogo::unpack_hidden_name(in_function->name())); - ret.push_back('.'); - } - - if (no->package() == NULL) - ret.append(this->pkgpath()); - else - ret.append(no->package()->pkgpath()); - ret.push_back('.'); + nt->backend_name(this, bname); + bname->set_suffix(is_pointer ? "..p" : "..d"); } - - Gogo::append_possibly_hidden_name(&ret, no->name()); - - if (in_function != NULL && index > 0) - { - char buf[30]; - snprintf(buf, sizeof buf, "..i%u", index); - ret.append(buf); - } - - ret.append("..d"); - - return ret; } // Return the name of the type descriptor list symbol of a package. +// This is passed directly to the backend without further encoding. std::string -Gogo::type_descriptor_list_symbol(const std::string& pkgpath) +Gogo::type_descriptor_list_symbol(const std::string& pkgpath_symbol) { - return pkgpath + "..types"; + return pkgpath_symbol + "..types"; } // Return the name of the list of all type descriptor lists. This is -// only used in the main package. +// only used in the main package. This is passed directly to the +// backend without further encoding. std::string Gogo::typelists_symbol() @@ -1045,24 +1057,30 @@ Gogo::typelists_symbol() return "go..typelists"; } -// Return the name for the GC symbol for a type. This is used to -// initialize the gcdata field of a type descriptor. This is a local -// name never referenced outside of this assembly file. (Note that -// some type descriptors will initialize the gcdata field with a name -// generated by ptrmask_symbol_name rather than this method.) +// Return the assembler name for the GC symbol for a type. This is +// used to initialize the gcdata field of a type descriptor. This is +// a local name never referenced outside of this assembly file. (Note +// that some type descriptors will initialize the gcdata field with a +// name generated by ptrmask_symbol_name rather than this method.) +// This is passed directly to the backend without further encoding. std::string Gogo::gc_symbol_name(Type* type) { - return this->type_descriptor_name(type, type->named_type()) + "..g"; + Backend_name bname; + this->type_descriptor_backend_name(type, type->named_type(), &bname); + bname.append_suffix("..g"); + return bname.asm_name(); } -// Return the name for a ptrmask variable. PTRMASK_SYM_NAME is a -// base32 string encoding the ptrmask (as returned by Ptrmask::symname -// in types.cc). This name is used to intialize the gcdata field of a -// type descriptor. These names are globally visible. (Note that -// some type descriptors will initialize the gcdata field with a name -// generated by gc_symbol_name rather than this method.) +// Return the assembler name for a ptrmask variable. PTRMASK_SYM_NAME +// is a base32 string encoding the ptrmask (as returned by +// Ptrmask::symname in types.cc). This name is used to intialize the +// gcdata field of a type descriptor. These names are globally +// visible. (Note that some type descriptors will initialize the +// gcdata field with a name generated by gc_symbol_name rather than +// this method.) This is passed directly to the backend without +// further encoding. std::string Gogo::ptrmask_symbol_name(const std::string& ptrmask_sym_name) @@ -1070,34 +1088,123 @@ Gogo::ptrmask_symbol_name(const std::string& ptrmask_sym_name) return "gcbits.." + ptrmask_sym_name; } -// Return the name to use for an interface method table used for the -// ordinary type TYPE converted to the interface type ITYPE. +// Return the assembler name to use for an interface method table used +// for the ordinary type TYPE converted to the interface type ITYPE. // IS_POINTER is true if this is for the method set for a pointer -// receiver. +// receiver. This is passed directly to the backend without further +// encoding. std::string Gogo::interface_method_table_name(Interface_type* itype, Type* type, bool is_pointer) { + Backend_name iname; + itype->backend_name(this, &iname); + Backend_name tname; + type->backend_name(this, &tname); return ((is_pointer ? "pimt.." : "imt..") - + itype->mangled_name(this) + + iname.asm_name() + ".." - + type->mangled_name(this)); + + tname.asm_name()); } -// Return whether NAME is a special name that can not be passed to -// unpack_hidden_name. This is needed because various special names -// use "..SUFFIX", but unpack_hidden_name just looks for '.'. +// If NAME is a special name with a ".." suffix, return the position +// of that suffix. This is needed because various special names use +// "..SUFFIX", but unpack_hidden_name just looks for '.', and because +// we don't want to encode the suffix. + +size_t +Gogo::special_name_pos(const std::string& name) +{ + size_t pos = name.rfind(".."); + if (pos == std::string::npos) + return pos; + std::string suffix(name.substr(pos)); + if (suffix == "..hash" + || suffix == "..eq" + || suffix == "..stub" + || suffix == "..d" + || suffix == "..f" + || suffix == "..r" + || suffix == "..import") + return pos; + if ((suffix.compare(2, 4, "func") == 0 + || suffix.compare(2, 4, "init") == 0) + && Gogo::is_digits(suffix.substr(6))) + return pos; + if (suffix.compare(2, 5, "thunk") == 0 + && Gogo::is_digits(suffix.substr(7))) + return pos; + return std::string::npos; +} + +// Return whether the string is non-empty and contains only digits. bool -Gogo::is_special_name(const std::string& name) +Gogo::is_digits(const std::string& s) +{ + if (s.empty()) + return false; + for (size_t i = 0; i < s.size(); ++i) + if (s[i] < '0' || s[i] > '9') + return false; + return true; +} + +// Class Backend_name. + +// Get the user visible name. + +std::string +Backend_name::name() const +{ + if (this->is_asm_name_) + return this->components_[0]; + std::string ret; + if (this->prefix_ != NULL) + ret.append(this->prefix_); + for (int i = 0; i < this->count_; i++) + { + if (i > 0) + ret.push_back('.'); + ret.append(this->components_[i]); + } + if (!this->suffix_.empty()) + ret.append(this->suffix_); + return ret; +} + +// Get the assembler name. + +std::string +Backend_name::asm_name() const +{ + if (this->is_asm_name_) + return this->components_[0]; + std::string ret; + if (this->prefix_ != NULL) + ret.append(this->prefix_); + for (int i = 0; i < this->count_; i++) + { + if (i > 0) + ret.push_back('.'); + ret.append(go_encode_id(this->components_[i])); + } + if (!this->suffix_.empty()) + ret.append(this->suffix_); + return ret; +} + +// Get the assembler name, or the empty string if it is the same as +// the user visible name. + +std::string +Backend_name::optional_asm_name() const { - return (name.find("..hash") != std::string::npos - || name.find("..eq") != std::string::npos - || name.find("..stub") != std::string::npos - || name.find("..func") != std::string::npos - || name.find("..r") != std::string::npos - || name.find("..init") != std::string::npos - || name.find("..thunk") != std::string::npos - || name.find("..import") != std::string::npos); + if (this->is_asm_name_) + return ""; + for (int i = 0; i < this->count_; i++) + if (go_id_needs_encoding(this->components_[i])) + return this->asm_name(); + return ""; } diff --git a/gcc/go/gofrontend/runtime.def b/gcc/go/gofrontend/runtime.def index 0796cba..b608766 100644 --- a/gcc/go/gofrontend/runtime.def +++ b/gcc/go/gofrontend/runtime.def @@ -83,7 +83,7 @@ DEF_GO_RUNTIME(MAKEMAP64, "runtime.makemap64", P3(TYPE, INT64, POINTER), R1(MAP)) // Make a map with no hint, or a small constant hint. -DEF_GO_RUNTIME(MAKEMAP_SMALL, "runtime.makemap_small", P0(), R1(MAP)) +DEF_GO_RUNTIME(MAKEMAP_SMALL, "runtime.makemap__small", P0(), R1(MAP)) // Build a map from a composite literal. DEF_GO_RUNTIME(CONSTRUCT_MAP, "__go_construct_map", @@ -95,19 +95,19 @@ DEF_GO_RUNTIME(MAPACCESS1, "runtime.mapaccess1", P3(TYPE, MAP, POINTER), R1(POINTER)) // Look up a uint32 key in a map. -DEF_GO_RUNTIME(MAPACCESS1_FAST32, "runtime.mapaccess1_fast32", +DEF_GO_RUNTIME(MAPACCESS1_FAST32, "runtime.mapaccess1__fast32", P3(TYPE, MAP, UINT32), R1(POINTER)) // Look up a uint64 key in a map. -DEF_GO_RUNTIME(MAPACCESS1_FAST64, "runtime.mapaccess1_fast64", +DEF_GO_RUNTIME(MAPACCESS1_FAST64, "runtime.mapaccess1__fast64", P3(TYPE, MAP, UINT64), R1(POINTER)) // Look up a string key in a map. -DEF_GO_RUNTIME(MAPACCESS1_FASTSTR, "runtime.mapaccess1_faststr", +DEF_GO_RUNTIME(MAPACCESS1_FASTSTR, "runtime.mapaccess1__faststr", P3(TYPE, MAP, STRING), R1(POINTER)) // Look up a key in a map when the value is large. -DEF_GO_RUNTIME(MAPACCESS1_FAT, "runtime.mapaccess1_fat", +DEF_GO_RUNTIME(MAPACCESS1_FAT, "runtime.mapaccess1__fat", P4(TYPE, MAP, POINTER, POINTER), R1(POINTER)) // Look up a key in a map returning the value and whether it is @@ -117,22 +117,22 @@ DEF_GO_RUNTIME(MAPACCESS2, "runtime.mapaccess2", P3(TYPE, MAP, POINTER), // Look up a uint32 key in a map returning the value and whether // it is present. -DEF_GO_RUNTIME(MAPACCESS2_FAST32, "runtime.mapaccess2_fast32", +DEF_GO_RUNTIME(MAPACCESS2_FAST32, "runtime.mapaccess2__fast32", P3(TYPE, MAP, UINT32), R2(POINTER, BOOL)) // Look up a uint64 key in a map returning the value and whether // it is present. -DEF_GO_RUNTIME(MAPACCESS2_FAST64, "runtime.mapaccess2_fast64", +DEF_GO_RUNTIME(MAPACCESS2_FAST64, "runtime.mapaccess2__fast64", P3(TYPE, MAP, UINT64), R2(POINTER, BOOL)) // Look up a string key in a map returning the value and whether // it is present. -DEF_GO_RUNTIME(MAPACCESS2_FASTSTR, "runtime.mapaccess2_faststr", +DEF_GO_RUNTIME(MAPACCESS2_FASTSTR, "runtime.mapaccess2__faststr", P3(TYPE, MAP, STRING), R2(POINTER, BOOL)) // Look up a key in a map, returning the value and whether it is // present, when the value is large. -DEF_GO_RUNTIME(MAPACCESS2_FAT, "runtime.mapaccess2_fat", +DEF_GO_RUNTIME(MAPACCESS2_FAT, "runtime.mapaccess2__fat", P4(TYPE, MAP, POINTER, POINTER), R2(POINTER, BOOL)) // Assignment to a key in a map. @@ -140,38 +140,38 @@ DEF_GO_RUNTIME(MAPASSIGN, "runtime.mapassign", P3(TYPE, MAP, POINTER), R1(POINTER)) // Assignment to a uint32 key in a map. -DEF_GO_RUNTIME(MAPASSIGN_FAST32, "runtime.mapassign_fast32", +DEF_GO_RUNTIME(MAPASSIGN_FAST32, "runtime.mapassign__fast32", P3(TYPE, MAP, UINT32), R1(POINTER)) // Assignment to a uint64 key in a map. -DEF_GO_RUNTIME(MAPASSIGN_FAST64, "runtime.mapassign_fast64", +DEF_GO_RUNTIME(MAPASSIGN_FAST64, "runtime.mapassign__fast64", P3(TYPE, MAP, UINT64), R1(POINTER)) // Assignment to a 32-bit pointer key in a map. -DEF_GO_RUNTIME(MAPASSIGN_FAST32PTR, "runtime.mapassign_fast32ptr", +DEF_GO_RUNTIME(MAPASSIGN_FAST32PTR, "runtime.mapassign__fast32ptr", P3(TYPE, MAP, POINTER), R1(POINTER)) // Assignment to a 64-bit pointer key in a map. -DEF_GO_RUNTIME(MAPASSIGN_FAST64PTR, "runtime.mapassign_fast64ptr", +DEF_GO_RUNTIME(MAPASSIGN_FAST64PTR, "runtime.mapassign__fast64ptr", P3(TYPE, MAP, POINTER), R1(POINTER)) // Assignment to a string key in a map. -DEF_GO_RUNTIME(MAPASSIGN_FASTSTR, "runtime.mapassign_faststr", +DEF_GO_RUNTIME(MAPASSIGN_FASTSTR, "runtime.mapassign__faststr", P3(TYPE, MAP, STRING), R1(POINTER)) // Delete a key from a map. DEF_GO_RUNTIME(MAPDELETE, "runtime.mapdelete", P3(TYPE, MAP, POINTER), R0()) // Delete a uint32 key from a map. -DEF_GO_RUNTIME(MAPDELETE_FAST32, "runtime.mapdelete_fast32", +DEF_GO_RUNTIME(MAPDELETE_FAST32, "runtime.mapdelete__fast32", P3(TYPE, MAP, UINT32), R0()) // Delete a uint64 key from a map. -DEF_GO_RUNTIME(MAPDELETE_FAST64, "runtime.mapdelete_fast64", +DEF_GO_RUNTIME(MAPDELETE_FAST64, "runtime.mapdelete__fast64", P3(TYPE, MAP, UINT64), R0()) // Delete a string key from a map. -DEF_GO_RUNTIME(MAPDELETE_FASTSTR, "runtime.mapdelete_faststr", +DEF_GO_RUNTIME(MAPDELETE_FASTSTR, "runtime.mapdelete__faststr", P3(TYPE, MAP, STRING), R0()) // Begin a range over a map. diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index f3bcf2e..c4570b4 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -1342,19 +1342,21 @@ Type::make_type_descriptor_var(Gogo* gogo) Type* td_type = Type::make_type_descriptor_type(); Btype* td_btype = td_type->get_backend(gogo); - std::string name = gogo->type_descriptor_name(this, nt); - std::string asm_name(go_selectively_encode_id(name)); + Backend_name bname; + gogo->type_descriptor_backend_name(this, nt, &bname); this->type_descriptor_var_ = - gogo->backend()->immutable_struct_reference(name, asm_name, - td_btype, - bloc); + gogo->backend()->immutable_struct_reference(bname.name(), + bname.optional_asm_name(), + td_btype, + bloc); if (phash != NULL) *phash = this->type_descriptor_var_; return; } - std::string var_name = gogo->type_descriptor_name(this, nt); + Backend_name bname; + gogo->type_descriptor_backend_name(this, nt, &bname); // Build the contents of the type descriptor. Expression* initializer = this->do_type_descriptor(gogo, NULL); @@ -1366,11 +1368,11 @@ Type::make_type_descriptor_var(Gogo* gogo) const Package* dummy; if (this->type_descriptor_defined_elsewhere(nt, &dummy)) { - std::string asm_name(go_selectively_encode_id(var_name)); this->type_descriptor_var_ = - gogo->backend()->immutable_struct_reference(var_name, asm_name, - initializer_btype, - loc); + gogo->backend()->immutable_struct_reference(bname.name(), + bname.optional_asm_name(), + initializer_btype, + loc); if (phash != NULL) *phash = this->type_descriptor_var_; return; @@ -1399,10 +1401,10 @@ Type::make_type_descriptor_var(Gogo* gogo) // ensure that type_descriptor_pointer will work if called while // converting INITIALIZER. - std::string asm_name(go_selectively_encode_id(var_name)); this->type_descriptor_var_ = - gogo->backend()->immutable_struct(var_name, asm_name, false, is_common, - initializer_btype, loc); + gogo->backend()->immutable_struct(bname.name(), bname.optional_asm_name(), + false, is_common, initializer_btype, + loc); if (phash != NULL) *phash = this->type_descriptor_var_; @@ -1411,7 +1413,7 @@ Type::make_type_descriptor_var(Gogo* gogo) Bexpression* binitializer = initializer->get_backend(&context); gogo->backend()->immutable_struct_set_init(this->type_descriptor_var_, - var_name, false, is_common, + bname.name(), false, is_common, initializer_btype, loc, binitializer); @@ -1924,19 +1926,20 @@ Type::build_hash_function(Gogo* gogo, int64_t size, Function_type* hash_fntype) return ins.first->second; } - std::string hash_name = gogo->hash_function_name(type); + Backend_name bname; + gogo->hash_function_name(type, &bname); Location bloc = Linemap::predeclared_location(); - Named_object* hash_fn = gogo->declare_package_function(hash_name, + Named_object* hash_fn = gogo->declare_package_function(bname.name(), hash_fntype, bloc); ins.first->second = hash_fn; if (gogo->in_global_scope()) - type->write_hash_function(gogo, size, hash_name, hash_fntype); + type->write_hash_function(gogo, size, &bname, hash_fntype); else - gogo->queue_hash_function(type, size, hash_name, hash_fntype); + gogo->queue_hash_function(type, size, &bname, hash_fntype); return hash_fn; } @@ -1944,8 +1947,7 @@ Type::build_hash_function(Gogo* gogo, int64_t size, Function_type* hash_fntype) // Write the hash function for a type that needs it written specially. void -Type::write_hash_function(Gogo* gogo, int64_t size, - const std::string& hash_name, +Type::write_hash_function(Gogo* gogo, int64_t size, const Backend_name* bname, Function_type* hash_fntype) { Location bloc = Linemap::predeclared_location(); @@ -1958,8 +1960,9 @@ Type::write_hash_function(Gogo* gogo, int64_t size, go_assert(this->is_comparable()); - Named_object* hash_fn = gogo->start_function(hash_name, hash_fntype, false, - bloc); + Named_object* hash_fn = gogo->start_function(bname->name(), hash_fntype, + false, bloc); + hash_fn->func_value()->set_asm_name(bname->asm_name()); hash_fn->func_value()->set_is_type_specific_function(); gogo->start_block(bloc); @@ -2245,7 +2248,8 @@ Type::build_equal_function(Gogo* gogo, Named_type* name, int64_t size, return ins.first->second; } - std::string equal_name = gogo->equal_function_name(this, name); + Backend_name bname; + gogo->equal_function_name(this, name, &bname); Location bloc = Linemap::predeclared_location(); @@ -2255,19 +2259,21 @@ Type::build_equal_function(Gogo* gogo, Named_type* name, int64_t size, Named_object* equal_fn; if (is_defined_elsewhere) - equal_fn = Named_object::make_function_declaration(equal_name, package, + equal_fn = Named_object::make_function_declaration(bname.name(), package, equal_fntype, bloc); else - equal_fn = gogo->declare_package_function(equal_name, equal_fntype, bloc); + equal_fn = gogo->declare_package_function(bname.name(), equal_fntype, bloc); ins.first->second = equal_fn; - if (!is_defined_elsewhere) + if (is_defined_elsewhere) + equal_fn->func_declaration_value()->set_asm_name(bname.asm_name()); + else { if (gogo->in_global_scope()) - this->write_equal_function(gogo, name, size, equal_name, equal_fntype); + this->write_equal_function(gogo, name, size, &bname, equal_fntype); else - gogo->queue_equal_function(this, name, size, equal_name, equal_fntype); + gogo->queue_equal_function(this, name, size, &bname, equal_fntype); } return equal_fn; @@ -2278,7 +2284,7 @@ Type::build_equal_function(Gogo* gogo, Named_type* name, int64_t size, void Type::write_equal_function(Gogo* gogo, Named_type* name, int64_t size, - const std::string& equal_name, + const Backend_name* bname, Function_type* equal_fntype) { Location bloc = Linemap::predeclared_location(); @@ -2291,8 +2297,9 @@ Type::write_equal_function(Gogo* gogo, Named_type* name, int64_t size, go_assert(this->is_comparable()); - Named_object* equal_fn = gogo->start_function(equal_name, equal_fntype, + Named_object* equal_fn = gogo->start_function(bname->name(), equal_fntype, false, bloc); + equal_fn->func_value()->set_asm_name(bname->asm_name()); equal_fn->func_value()->set_is_type_specific_function(); gogo->start_block(bloc); @@ -2671,9 +2678,8 @@ Type::make_gc_symbol_var(Gogo* gogo) const Package* dummy; if (this->type_descriptor_defined_elsewhere(nt, &dummy)) { - std::string asm_name(go_selectively_encode_id(sym_name)); this->gc_symbol_var_ = - gogo->backend()->implicit_variable_reference(sym_name, asm_name, + gogo->backend()->implicit_variable_reference(sym_name, "", sym_btype); if (phash != NULL) *phash = this->gc_symbol_var_; @@ -2699,10 +2705,9 @@ Type::make_gc_symbol_var(Gogo* gogo) // Since we are building the GC symbol in this package, we must create the // variable before converting the initializer to its backend representation // because the initializer may refer to the GC symbol for this type. - std::string asm_name(go_selectively_encode_id(sym_name)); this->gc_symbol_var_ = - gogo->backend()->implicit_variable(sym_name, asm_name, - sym_btype, false, true, is_common, 0); + gogo->backend()->implicit_variable(sym_name, "", sym_btype, false, true, + is_common, 0); if (phash != NULL) *phash = this->gc_symbol_var_; @@ -2876,14 +2881,17 @@ Ptrmask::set_from(Gogo* gogo, Type* type, int64_t ptrsize, int64_t offset) } } -// Return a symbol name for this ptrmask. This is used to coalesce identical -// ptrmasks, which are common. The symbol name must use only characters that are -// valid in symbols. It's nice if it's short. For smaller ptrmasks, we convert -// it to a string that uses only 32 characters, avoiding digits and u and U. For -// longer pointer masks, apply the same process to the SHA1 digest of the bits, -// so as to avoid pathologically long symbol names (see related Go issues #32083 -// and #11583 for more on this). To avoid collisions between the two encoding -// schemes, use a prefix ("X") for the SHA form to disambiguate. +// Return a symbol name for this ptrmask. This is used to coalesce +// identical ptrmasks, which are common. The symbol name must use +// only characters that are valid in symbols. It's nice if it's +// short. For smaller ptrmasks, we convert it to a string that uses +// only 32 characters. For longer pointer masks, apply the same +// process to the SHA1 digest of the bits, so as to avoid +// pathologically long symbol names (see related Go issues #32083 and +// #11583 for more on this). To avoid collisions between the two +// encoding schemes, use a prefix ("X") for the SHA form to +// disambiguate. + std::string Ptrmask::symname() const { @@ -2911,7 +2919,7 @@ Ptrmask::symname() const bits = &shabits; } - const char chars[33] = "abcdefghijklmnopqrstvwxyzABCDEFG"; + const char chars[33] = "abcdefghijklmnopqrstuvwxyzABCDEF"; go_assert(chars[32] == '\0'); std::string ret(prefix); unsigned int b = 0; @@ -2995,9 +3003,8 @@ Type::gc_ptrmask_var(Gogo* gogo, int64_t ptrsize, int64_t ptrdata) context.set_is_const(); Bexpression* bval = val->get_backend(&context); - std::string asm_name(go_selectively_encode_id(sym_name)); Btype *btype = val->type()->get_backend(gogo); - Bvariable* ret = gogo->backend()->implicit_variable(sym_name, asm_name, + Bvariable* ret = gogo->backend()->implicit_variable(sym_name, "", btype, false, true, true, 0); gogo->backend()->implicit_variable_set_init(ret, sym_name, btype, false, @@ -8092,11 +8099,9 @@ Map_type::backend_zero_value(Gogo* gogo) Btype* barray_type = gogo->backend()->array_type(buint8_type, blength); std::string zname = Map_type::zero_value->name(); - std::string asm_name(go_selectively_encode_id(zname)); Bvariable* zvar = - gogo->backend()->implicit_variable(zname, asm_name, - barray_type, false, false, true, - Map_type::zero_value_align); + gogo->backend()->implicit_variable(zname, "", barray_type, false, false, + true, Map_type::zero_value_align); gogo->backend()->implicit_variable_set_init(zvar, zname, barray_type, false, false, true, NULL); return zvar; diff --git a/gcc/go/gofrontend/types.h b/gcc/go/gofrontend/types.h index 9ac8516..5965d5a 100644 --- a/gcc/go/gofrontend/types.h +++ b/gcc/go/gofrontend/types.h @@ -49,6 +49,7 @@ class Function; class Translate_context; class Export; class Import; +class Backend_name; class Btype; class Bexpression; class Bvariable; @@ -1008,11 +1009,11 @@ class Type std::string reflection(Gogo*) const; - // Return a mangled name for the type. This is a name which can be - // used in assembler code. Identical types should have the same - // manged name. - std::string - mangled_name(Gogo*) const; + // Add the backend name for the type to BNAME. This will add one or + // two name components. Identical types should have the same + // backend name. + void + backend_name(Gogo*, Backend_name* bname) const; // If the size of the type can be determined, set *PSIZE to the size // in bytes and return true. Otherwise, return false. This queries @@ -1066,12 +1067,11 @@ class Type // Write the equal function for a type. void write_equal_function(Gogo*, Named_type*, int64_t size, - const std::string& equal_name, - Function_type* equal_fntype); + const Backend_name*, Function_type* equal_fntype); // Write the hash function for a type. void - write_hash_function(Gogo*, int64_t size, const std::string& hash_name, + write_hash_function(Gogo*, int64_t size, const Backend_name*, Function_type* hash_fntype); // Return the alignment required by the memequalN function. @@ -3557,10 +3557,10 @@ class Named_type : public Type void append_reflection_type_name(Gogo*, bool use_alias, std::string*) const; - // Append the mangled type name as for Type::append_mangled_name, + // Append the symbol type name as for Type::append_mangled_name, // but if USE_ALIAS use the alias name rather than the alias target. void - append_mangled_type_name(Gogo*, bool use_alias, std::string*) const; + append_symbol_type_name(Gogo*, bool use_alias, std::string*) const; // Import a named type. static void |