diff --git a/gcc/go/gofrontend/MERGE b/gcc/go/gofrontend/MERGE index a100cab84fe5..0430961f7bce 100644 --- a/gcc/go/gofrontend/MERGE +++ b/gcc/go/gofrontend/MERGE @@ -1,4 +1,4 @@ -3488a401e50835de5de5c4f153772ac2798d0e71 +0bbc03f81c862fb35be3edee9824698a7892a17e 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/escape.cc b/gcc/go/gofrontend/escape.cc index 4f95945e2309..e15e85f3082c 100644 --- a/gcc/go/gofrontend/escape.cc +++ b/gcc/go/gofrontend/escape.cc @@ -686,42 +686,33 @@ debug_function_name(Named_object* fn) if (!fn->is_function()) return Gogo::unpack_hidden_name(fn->name()); - if (fn->func_value()->enclosing() == NULL) + + std::string fnname = Gogo::unpack_hidden_name(fn->name()); + if (fn->func_value()->is_method()) { - std::string fnname = Gogo::unpack_hidden_name(fn->name()); - if (fn->func_value()->is_method()) - { - // Methods in gc compiler are named "T.m" or "(*T).m" where - // T is the receiver type. Add the receiver here. - Type* rt = fn->func_value()->type()->receiver()->type(); - switch (rt->classification()) - { - case Type::TYPE_NAMED: - fnname = rt->named_type()->name() + "." + fnname; - break; + // Methods in gc compiler are named "T.m" or "(*T).m" where + // T is the receiver type. Add the receiver here. + Type* rt = fn->func_value()->type()->receiver()->type(); + switch (rt->classification()) + { + case Type::TYPE_NAMED: + fnname = rt->named_type()->name() + "." + fnname; + break; - case Type::TYPE_POINTER: - { - Named_type* nt = rt->points_to()->named_type(); - if (nt != NULL) - fnname = "(*" + nt->name() + ")." + fnname; - break; - } + case Type::TYPE_POINTER: + { + Named_type* nt = rt->points_to()->named_type(); + if (nt != NULL) + fnname = "(*" + nt->name() + ")." + fnname; + break; + } - default: - break; - } - } - return fnname; + default: + break; + } } - // Closures are named ".$nested#" where # is a global counter. Add outer - // function name for better distinguishing. This is also closer to what - // gc compiler prints, "outer.func#". - Named_object* enclosing = fn->func_value()->enclosing(); - std::string name = Gogo::unpack_hidden_name(fn->name()); - std::string outer_name = Gogo::unpack_hidden_name(enclosing->name()); - return outer_name + "." + name; + return fnname; } // Return the name of the current function. diff --git a/gcc/go/gofrontend/expressions.cc b/gcc/go/gofrontend/expressions.cc index 47be82f823ec..0701cb045187 100644 --- a/gcc/go/gofrontend/expressions.cc +++ b/gcc/go/gofrontend/expressions.cc @@ -1310,6 +1310,16 @@ Func_descriptor_expression::do_get_backend(Translate_context* context) && Linemap::is_predeclared_location(no->location())) is_descriptor = true; + // The runtime package implements some functions defined in the + // syscall package. Let the syscall package define the descriptor + // in this case. + if (gogo->compiling_runtime() + && gogo->package_name() == "runtime" + && no->is_function() + && !no->func_value()->asm_name().empty() + && no->func_value()->asm_name().compare(0, 8, "syscall.") == 0) + is_descriptor = true; + Btype* btype = this->type()->get_backend(gogo); Bvariable* bvar; @@ -6845,7 +6855,8 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, if (orig_fntype == NULL || !orig_fntype->is_method()) { - ins.first->second = Named_object::make_erroneous_name(Gogo::thunk_name()); + ins.first->second = + Named_object::make_erroneous_name(gogo->thunk_name()); return ins.first->second; } @@ -6853,8 +6864,8 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, // The type here is wrong--it should be the C function type. But it // doesn't really matter. Type* vt = Type::make_pointer_type(Type::make_void_type()); - sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); - sfl->push_back(Struct_field(Typed_identifier("val.1", + sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val", orig_fntype->receiver()->type(), loc))); Struct_type* st = Type::make_struct_type(sfl, loc); @@ -6863,7 +6874,7 @@ Bound_method_expression::create_thunk(Gogo* gogo, const Method* method, Function_type* new_fntype = orig_fntype->copy_with_names(); - std::string thunk_name = Gogo::thunk_name(); + std::string thunk_name = gogo->thunk_name(); Named_object* new_no = gogo->start_function(thunk_name, new_fntype, false, loc); @@ -7009,10 +7020,10 @@ Bound_method_expression::do_flatten(Gogo* gogo, Named_object*, // away with this. Struct_field_list* fields = new Struct_field_list(); - fields->push_back(Struct_field(Typed_identifier("fn.0", + fields->push_back(Struct_field(Typed_identifier("fn", thunk->func_value()->type(), loc))); - fields->push_back(Struct_field(Typed_identifier("val.1", val->type(), loc))); + fields->push_back(Struct_field(Typed_identifier("val", val->type(), loc))); Struct_type* st = Type::make_struct_type(fields, loc); st->set_is_struct_incomparable(); @@ -11889,25 +11900,25 @@ Interface_field_reference_expression::create_thunk(Gogo* gogo, const Typed_identifier* method_id = type->find_method(name); if (method_id == NULL) - return Named_object::make_erroneous_name(Gogo::thunk_name()); + return Named_object::make_erroneous_name(gogo->thunk_name()); Function_type* orig_fntype = method_id->type()->function_type(); if (orig_fntype == NULL) - return Named_object::make_erroneous_name(Gogo::thunk_name()); + return Named_object::make_erroneous_name(gogo->thunk_name()); Struct_field_list* sfl = new Struct_field_list(); // The type here is wrong--it should be the C function type. But it // doesn't really matter. Type* vt = Type::make_pointer_type(Type::make_void_type()); - sfl->push_back(Struct_field(Typed_identifier("fn.0", vt, loc))); - sfl->push_back(Struct_field(Typed_identifier("val.1", type, loc))); + sfl->push_back(Struct_field(Typed_identifier("fn", vt, loc))); + sfl->push_back(Struct_field(Typed_identifier("val", type, loc))); Struct_type* st = Type::make_struct_type(sfl, loc); st->set_is_struct_incomparable(); Type* closure_type = Type::make_pointer_type(st); Function_type* new_fntype = orig_fntype->copy_with_names(); - std::string thunk_name = Gogo::thunk_name(); + std::string thunk_name = gogo->thunk_name(); Named_object* new_no = gogo->start_function(thunk_name, new_fntype, false, loc); @@ -11995,10 +12006,10 @@ Interface_field_reference_expression::do_get_backend(Translate_context* context) Location loc = this->location(); Struct_field_list* fields = new Struct_field_list(); - fields->push_back(Struct_field(Typed_identifier("fn.0", + fields->push_back(Struct_field(Typed_identifier("fn", thunk->func_value()->type(), loc))); - fields->push_back(Struct_field(Typed_identifier("val.1", + fields->push_back(Struct_field(Typed_identifier("val", this->expr_->type(), loc))); Struct_type* st = Type::make_struct_type(fields, loc); @@ -12247,7 +12258,7 @@ Selector_expression::lower_method_expression(Gogo* gogo) return f; } - Named_object* no = gogo->start_function(Gogo::thunk_name(), fntype, false, + Named_object* no = gogo->start_function(gogo->thunk_name(), fntype, false, location); Named_object* vno = gogo->lookup(receiver_name, NULL); diff --git a/gcc/go/gofrontend/go-encode-id.cc b/gcc/go/gofrontend/go-encode-id.cc index 978f20823d69..ec5c37fc40ec 100644 --- a/gcc/go/gofrontend/go-encode-id.cc +++ b/gcc/go/gofrontend/go-encode-id.cc @@ -4,11 +4,16 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include "go-system.h" + +#include "gogo.h" #include "go-location.h" #include "go-linemap.h" #include "go-encode-id.h" +#include "lex.h" -// Return whether the character c is OK to use in the assembler. +// Return whether the character c is OK to use in the assembler. We +// only permit ASCII alphanumeric characters, underscore, and dot. static bool char_needs_encoding(char c) @@ -27,7 +32,7 @@ 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 '.': case '$': case '/': + case '_': case '.': return false; default: return true; @@ -77,11 +82,25 @@ fetch_utf8_char(const char* p, unsigned int* pc) return len; } -// Encode an identifier using ASCII characters. +// Encode an identifier using ASCII 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. std::string go_encode_id(const std::string &id) { + if (Lex::is_invalid_identifier(id)) + { + go_assert(saw_errors()); + return id; + } + + // The encoding is only unambiguous if the input string does not + // contain ..u or ..U. + 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(); @@ -89,16 +108,25 @@ go_encode_id(const std::string &id) { unsigned int c; size_t len = fetch_utf8_char(p, &c); - if (len == 1 && !char_needs_encoding(c)) - ret += c; + if (len == 1) + { + // At this point we should only be seeing alphanumerics or + // underscore or dot. + go_assert(!char_needs_encoding(c)); + ret += c; + } + else if (c < 0x10000) + { + char buf[16]; + snprintf(buf, sizeof buf, "..u%04x", c); + ret += buf; + } else - { - ret += "$U"; - char buf[30]; - snprintf(buf, sizeof buf, "%x", c); - ret += buf; - ret += "$"; - } + { + char buf[16]; + snprintf(buf, sizeof buf, "..U%08x", c); + ret += buf; + } p += len; } return ret; @@ -111,3 +139,35 @@ go_selectively_encode_id(const std::string &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. + +std::string +go_mangle_struct_tag(const std::string& tag) +{ + std::string ret; + const char* p = tag.c_str(); + const char* pend = p + tag.length(); + while (p < pend) + { + unsigned int c; + 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 + { + char buf[16]; + snprintf(buf, sizeof buf, ".x%02x", c); + ret += buf; + } + p += len; + } + return ret; +} diff --git a/gcc/go/gofrontend/go-encode-id.h b/gcc/go/gofrontend/go-encode-id.h index b95d97dd1ba1..ec81b63a1821 100644 --- a/gcc/go/gofrontend/go-encode-id.h +++ b/gcc/go/gofrontend/go-encode-id.h @@ -9,10 +9,9 @@ #include "backend.h" -// Given an identifier corresponding to a function or variable, -// this helper returns TRUE if the identifier needs special -// encoding to be used as an ASM name (symbol), FALSE if the name -// is OK as is. +// Given an identifier that will appear in assembly code, this helper +// returns TRUE if the identifier needs special encoding to be used as +// an ASM name, FALSE if the name is OK as is. extern bool go_id_needs_encoding(const std::string& str); @@ -22,9 +21,12 @@ extern std::string go_encode_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. +// 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); + #endif // !defined(GO_ENCODE_ID_H) diff --git a/gcc/go/gofrontend/gogo.cc b/gcc/go/gofrontend/gogo.cc index e8bc7aea9bfc..83be6176ae7e 100644 --- a/gcc/go/gofrontend/gogo.cc +++ b/gcc/go/gofrontend/gogo.cc @@ -1758,7 +1758,7 @@ Gogo::start_function(const std::string& name, Function_type* type, else { // Invent a name for a nested function. - nested_name = this->nested_function_name(); + nested_name = this->nested_function_name(enclosing); pname = &nested_name; } @@ -4821,9 +4821,9 @@ Function::Function(Function_type* type, Named_object* enclosing, Block* block, : type_(type), enclosing_(enclosing), results_(NULL), closure_var_(NULL), block_(block), location_(location), labels_(), local_type_count_(0), descriptor_(NULL), fndecl_(NULL), defer_stack_(NULL), - pragmas_(0), is_sink_(false), results_are_named_(false), - is_unnamed_type_stub_method_(false), calls_recover_(false), - is_recover_thunk_(false), has_recover_thunk_(false), + pragmas_(0), nested_functions_(0), is_sink_(false), + results_are_named_(false), is_unnamed_type_stub_method_(false), + calls_recover_(false), is_recover_thunk_(false), has_recover_thunk_(false), calls_defer_retaddr_(false), is_type_specific_function_(false), in_unique_section_(false) { @@ -4948,7 +4948,7 @@ Function::set_closure_type() // The first field of a closure is always a pointer to the function // code. Type* voidptr_type = Type::make_pointer_type(Type::make_void_type()); - st->push_field(Struct_field(Typed_identifier(".$f", voidptr_type, + st->push_field(Struct_field(Typed_identifier(".f", voidptr_type, this->location_))); unsigned int index = 1; diff --git a/gcc/go/gofrontend/gogo.h b/gcc/go/gofrontend/gogo.h index f7f8d602bd16..5135042763d7 100644 --- a/gcc/go/gofrontend/gogo.h +++ b/gcc/go/gofrontend/gogo.h @@ -801,7 +801,7 @@ class Gogo // Return the name to use for a generated stub method. std::string - stub_method_name(const std::string& method_name); + stub_method_name(const Package*, const std::string& method_name); // Return the names of the hash and equality functions for TYPE. void @@ -826,7 +826,7 @@ class Gogo // Return a name to use for a thunk function. A thunk function is // one we create during the compilation, for a go statement or a // defer statement or a method expression. - static std::string + std::string thunk_name(); // Return whether an object is a thunk. @@ -838,8 +838,8 @@ class Gogo init_function_name(); // Return the name to use for a nested function. - static std::string - nested_function_name(); + std::string + nested_function_name(Named_object* enclosing); // Return the name to use for a sink funciton. std::string @@ -887,6 +887,12 @@ 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); + private: // During parsing, we keep a stack of functions. Each function on // the stack is one that we are currently parsing. For each @@ -1233,6 +1239,11 @@ class Function results_are_named() const { return this->results_are_named_; } + // Return the assembler name. + const std::string& + asm_name() const + { return this->asm_name_; } + // Set the assembler name. void set_asm_name(const std::string& asm_name) @@ -1250,6 +1261,14 @@ class Function this->pragmas_ = pragmas; } + // Return the index to use for a nested function. + unsigned int + next_nested_function_index() + { + ++this->nested_functions_; + return this->nested_functions_; + } + // Whether this method should not be included in the type // descriptor. bool @@ -1510,6 +1529,8 @@ class Function Temporary_statement* defer_stack_; // Pragmas for this function. This is a set of GOPRAGMA bits. unsigned int pragmas_; + // Number of nested functions defined within this function. + unsigned int nested_functions_; // True if this function is sink-named. No code is generated. bool is_sink_ : 1; // True if the result variables are named. diff --git a/gcc/go/gofrontend/lex.cc b/gcc/go/gofrontend/lex.cc index e9f11c2c1c0c..560f5f9d34d2 100644 --- a/gcc/go/gofrontend/lex.cc +++ b/gcc/go/gofrontend/lex.cc @@ -2761,16 +2761,19 @@ bool Lex::is_exported_name(const std::string& name) { unsigned char c = name[0]; - if (c != '$') + if (c != '.') return c >= 'A' && c <= 'Z'; else { const char* p = name.data(); size_t len = name.length(); - if (len < 2 || p[1] != 'U') + if (len < 4 || p[1] != '.' || (p[2] != 'u' && p[2] != 'U')) return false; unsigned int ci = 0; - for (size_t i = 2; i < len && p[i] != '$'; ++i) + size_t want = (p[2] == 'u' ? 4 : 8); + if (len < want + 3) + return false; + for (size_t i = 3; i < want; ++i) { c = p[i]; if (!Lex::is_hex_digit(c)) diff --git a/gcc/go/gofrontend/names.cc b/gcc/go/gofrontend/names.cc index 5feea1244435..eb18f81cc168 100644 --- a/gcc/go/gofrontend/names.cc +++ b/gcc/go/gofrontend/names.cc @@ -15,6 +15,190 @@ // assembly code. This is not used for names that appear only in the // debug info. +// Our external names 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. +// +// 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. +// +// 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. +// +// 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. +// +// 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 +// 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 +// unambiguous. +// +// Also uncommon is an external name that must refer to a named type +// defined within a function. While such a type can not have methods +// itself, it can pick up embedded methods, and those methods need +// names. These are treated as a kind of type literal written as, +// before type literal encoding, FNNAME.TYPENAME(INDEX) or, for a +// method, TYPE.MNAME.TYPENAME(INDEX). INDEX is the index of that +// named type within the function, as a single function can have +// multiple types with the same name. This is unambiguous as +// parentheses can not appear in a type literal in this form (they can +// only appear in interface method declarations). +// +// That is the end of the list of basic names. The remaining names +// exist for special purposes, and are differentiated from the basic +// names by containing two consecutive dots. +// +// The hash function for a type is treated as a method whose name is +// ".hash". That is, the method name begins with a dot. The effect +// is that there will be two consecutive dots in the name; the name +// will always end with "..hash". +// +// Similarly the equality function for a type is treated as a method +// whose name is ".eq". +// +// The function descriptor for a function is the same as the name of +// 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. +// +// 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. +// +// 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". +// +// 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 an unnamed type is type..TYPE. +// That is, the string "type.." followed by the type literal encoding. +// 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'. +// +// The name of the GC symbol for a named type is PKGPATH.TYPE..g. +// +// The name of the GC symbol for an unnamed type is typeg..TYPE. +// 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. +// +// 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. +// +// The names of composite literal initializers, including the GC root +// variable, are not referenced. They must not conflict with any C +// language names, but the names are otherwise unimportant. They are +// named "go..CNN" where NN is a sequence of digits. The names do not +// include the PKGPATH. +// +// The map zero value, a common symbol that represents the zero value +// of a map, is named simply "go..zerovalue". The name does not +// include the PKGPATH. +// +// The import function for the main package is referenced by C code, +// and is named __go_init_main. For other packages it is +// PKGPATH..import. +// +// The type literal encoding is essentially a single line version of +// the type literal, such as "struct { pkgpath.i int; J int }". In +// this representation unexported names use their pkgpath, exported +// names omit it. +// +// 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. +// +// 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. +// +// 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. +// +// 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. Since before this encoding names can never +// contain consecutive dots followed by 'u' or 'U', and after this +// encoding "..u" and "..U" are followed by a known number of +// characters, this is unambiguous. +// +// Demangling these names is straightforward: +// - 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 +// 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 @@ -27,30 +211,20 @@ std::string Gogo::function_asm_name(const std::string& go_name, const Package* package, const Type* rtype) { - std::string ret = (package == NULL - ? this->pkgpath_symbol() - : package->pkgpath_symbol()); - - if (rtype != NULL - && Gogo::is_hidden_name(go_name) - && Gogo::hidden_name_pkgpath(go_name) != this->pkgpath()) - { - // This is a method created for an unexported method of an - // imported embedded type. Use the pkgpath of the imported - // package. - std::string p = Gogo::hidden_name_pkgpath(go_name); - ret = this->pkgpath_symbol_for_package(p); - } - - ret.append(1, '.'); - ret.append(Gogo::unpack_hidden_name(go_name)); - + std::string ret; if (rtype != NULL) - { - ret.append(1, '.'); - ret.append(rtype->mangled_name(this)); - } - + ret = rtype->mangled_name(this); + else if (package == NULL) + ret = this->pkgpath_symbol(); + else + ret = package->pkgpath_symbol(); + 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); + else + ret.append(Gogo::unpack_hidden_name(go_name)); return go_encode_id(ret); } @@ -60,41 +234,45 @@ Gogo::function_asm_name(const std::string& go_name, const Package* package, std::string Gogo::function_descriptor_name(Named_object* no) { - std::string var_name; - if (no->is_function_declaration() - && !no->func_declaration_value()->asm_name().empty() - && Linemap::is_predeclared_location(no->location())) - { - if (no->func_declaration_value()->asm_name().substr(0, 8) != "runtime.") - var_name = no->func_declaration_value()->asm_name() + "_descriptor"; - else - var_name = no->func_declaration_value()->asm_name() + "$descriptor"; - } - else - { - if (no->package() == NULL) - var_name = this->pkgpath_symbol(); - else - var_name = no->package()->pkgpath_symbol(); - var_name.push_back('.'); - var_name.append(Gogo::unpack_hidden_name(no->name())); - var_name.append("$descriptor"); - } - return var_name; + 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; } // Return the name to use for a generated stub method. MNAME is the -// method name. 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. +// 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. std::string -Gogo::stub_method_name(const std::string& mname) +Gogo::stub_method_name(const Package* package, const std::string& mname) { - return mname + "$stub"; + if (!Gogo::is_hidden_name(mname)) + return mname + "..stub"; + + const std::string& ppkgpath(package == NULL + ? this->pkgpath() + : package->pkgpath()); + std::string mpkgpath = Gogo::hidden_name_pkgpath(mname); + if (mpkgpath == ppkgpath) + 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 = this->pkgpath_symbol_for_package(mpkgpath); + ret.push_back('.'); + ret.append(Gogo::unpack_hidden_name(mname)); + ret.append("..stub"); + return ret; } // Return the names of the hash and equality functions for TYPE. If @@ -106,48 +284,12 @@ Gogo::specific_type_function_names(const Type* type, const Named_type* name, std::string *hash_name, std::string *equal_name) { - std::string base_name; - if (name == NULL) - { - // Mangled names can have '.' if they happen to refer to named - // types in some way. That's fine if this is simply a named - // type, but otherwise it will confuse the code that builds - // function identifiers. Remove '.' when necessary. - base_name = type->mangled_name(this); - size_t i; - while ((i = base_name.find('.')) != std::string::npos) - base_name[i] = '$'; - base_name = this->pack_hidden_name(base_name, false); - } - else - { - // This name is already hidden or not as appropriate. - base_name = name->name(); - unsigned int index; - const Named_object* in_function = name->in_function(&index); - if (in_function != NULL) - { - base_name.append(1, '$'); - const Typed_identifier* rcvr = - in_function->func_value()->type()->receiver(); - if (rcvr != NULL) - { - Named_type* rcvr_type = rcvr->type()->deref()->named_type(); - base_name.append(Gogo::unpack_hidden_name(rcvr_type->name())); - base_name.append(1, '$'); - } - base_name.append(Gogo::unpack_hidden_name(in_function->name())); - if (index > 0) - { - char buf[30]; - snprintf(buf, sizeof buf, "%u", index); - base_name += '$'; - base_name += buf; - } - } - } - *hash_name = base_name + "$hash"; - *equal_name = base_name + "$equal"; + const Type* rtype = type; + if (name != NULL) + rtype = name; + std::string tname = rtype->mangled_name(this); + *hash_name = tname + "..hash"; + *equal_name = tname + "..eq"; } // Return the assembler name to use for a global variable. GO_NAME is @@ -158,10 +300,12 @@ Gogo::specific_type_function_names(const Type* type, const Named_type* name, std::string Gogo::global_var_asm_name(const std::string& go_name, const Package* package) { - std::string ret = (package != NULL - ? package->pkgpath_symbol() - : this->pkgpath_symbol()); - ret.push_back('.'); + std::string ret; + if (package == NULL) + ret = this->pkgpath_symbol(); + else + ret = package->pkgpath_symbol(); + ret.append(1, '.'); ret.append(Gogo::unpack_hidden_name(go_name)); return go_encode_id(ret); } @@ -172,9 +316,10 @@ Gogo::global_var_asm_name(const std::string& go_name, const Package* package) std::string Gogo::erroneous_name() { + go_assert(saw_errors()); static int erroneous_count; char name[50]; - snprintf(name, sizeof name, "$erroneous%d", erroneous_count); + snprintf(name, sizeof name, ".erroneous%d", erroneous_count); ++erroneous_count; return name; } @@ -184,7 +329,7 @@ Gogo::erroneous_name() bool Gogo::is_erroneous_name(const std::string& name) { - return name.compare(0, 10, "$erroneous") == 0; + return name.compare(0, 10, ".erroneous") == 0; } // Return a name for a thunk object. @@ -194,9 +339,10 @@ Gogo::thunk_name() { static int thunk_count; char thunk_name[50]; - snprintf(thunk_name, sizeof thunk_name, "$thunk%d", thunk_count); + snprintf(thunk_name, sizeof thunk_name, "..thunk%d", thunk_count); ++thunk_count; - return thunk_name; + std::string ret = this->pkgpath_symbol(); + return ret + thunk_name; } // Return whether a function is a thunk. @@ -204,7 +350,14 @@ Gogo::thunk_name() bool Gogo::is_thunk(const Named_object* no) { - return no->name().compare(0, 6, "$thunk") == 0; + const std::string& name(no->name()); + size_t i = name.find("..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 the name to use for an init function. There can be multiple @@ -215,21 +368,50 @@ Gogo::init_function_name() { static int init_count; char buf[30]; - snprintf(buf, sizeof buf, ".$init%d", init_count); + snprintf(buf, sizeof buf, "..init%d", init_count); ++init_count; - return buf; + std::string ret = this->pkgpath_symbol(); + return ret + buf; } // Return the name to use for a nested function. std::string -Gogo::nested_function_name() +Gogo::nested_function_name(Named_object* enclosing) { - static int nested_count; + std::string prefix; + unsigned int index; + if (enclosing == NULL) + { + // A function literal at top level, as in + // var f = func() {} + static unsigned int toplevel_index; + ++toplevel_index; + index = toplevel_index; + prefix = ".go"; + } + else + { + while (true) + { + Named_object* parent = enclosing->func_value()->enclosing(); + if (parent == NULL) + break; + enclosing = parent; + } + const Typed_identifier* rcvr = + enclosing->func_value()->type()->receiver(); + if (rcvr != NULL) + { + prefix = rcvr->type()->mangled_name(this); + prefix.push_back('.'); + } + prefix.append(Gogo::unpack_hidden_name(enclosing->name())); + index = enclosing->func_value()->next_nested_function_index(); + } char buf[30]; - snprintf(buf, sizeof buf, ".$nested%d", nested_count); - ++nested_count; - return buf; + snprintf(buf, sizeof buf, "..func%u", index); + return prefix + buf; } // Return the name to use for a sink function, a function whose name @@ -241,7 +423,7 @@ Gogo::sink_function_name() { static int sink_count; char buf[30]; - snprintf(buf, sizeof buf, ".$sink%d", sink_count); + snprintf(buf, sizeof buf, ".sink%d", sink_count); ++sink_count; return buf; } @@ -255,7 +437,7 @@ Gogo::redefined_function_name() { static int redefinition_count; char buf[30]; - snprintf(buf, sizeof buf, ".$redefined%d", redefinition_count); + snprintf(buf, sizeof buf, ".redefined%d", redefinition_count); ++redefinition_count; return buf; } @@ -266,13 +448,17 @@ Gogo::redefined_function_name() std::string Gogo::recover_thunk_name(const std::string& name, const Type* rtype) { - std::string ret(name); + std::string ret; if (rtype != NULL) { - ret.push_back('$'); - ret.append(rtype->mangled_name(this)); + ret = rtype->mangled_name(this); + ret.append(1, '.'); } - ret.append("$recover"); + if (Gogo::is_special_name(name)) + ret.append(name); + else + ret.append(Gogo::unpack_hidden_name(name)); + ret.append("..r"); return ret; } @@ -284,7 +470,7 @@ Gogo::recover_thunk_name(const std::string& name, const Type* rtype) std::string Gogo::gc_root_name() { - return "gc0"; + return "go..C0"; } // Return the name to use for a composite literal or string @@ -296,8 +482,8 @@ Gogo::initializer_name() { static unsigned int counter; char buf[30]; - snprintf(buf, sizeof buf, "C%u", counter); ++counter; + snprintf(buf, sizeof buf, "go..C%u", counter); return buf; } @@ -307,7 +493,7 @@ Gogo::initializer_name() std::string Gogo::map_zero_value_name() { - return "go$zerovalue"; + return "go..zerovalue"; } // Return the name to use for the import control function. @@ -343,11 +529,50 @@ Type::mangled_name(Gogo* gogo) const { std::string ret; - // The do_mangled_name virtual function should set RET to the - // mangled name. For a composite type it should append a code for - // the composition and then call do_mangled_name on the components. + // 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)) + { + if (i + 1 < ret.size()) + { + char c = ret[i + 1]; + go_assert(c < '0' || c > '9'); + } + } + + // The order of these characters is the replacement code. + const char * const replace = " *;,{}[]()"; + + 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); + } + } + return ret; } @@ -357,27 +582,27 @@ Type::mangled_name(Gogo* gogo) const void Error_type::do_mangled_name(Gogo*, std::string* ret) const { - ret->push_back('E'); + ret->append("{error}"); } void Void_type::do_mangled_name(Gogo*, std::string* ret) const { - ret->push_back('v'); + ret->append("{void}"); } void Boolean_type::do_mangled_name(Gogo*, std::string* ret) const { - ret->push_back('b'); + ret->append("bool"); } void Integer_type::do_mangled_name(Gogo*, std::string* ret) const { char buf[100]; - snprintf(buf, sizeof buf, "i%s%s%de", - this->is_abstract_ ? "a" : "", + snprintf(buf, sizeof buf, "%s%si%d", + this->is_abstract_ ? "{abstract}" : "", this->is_unsigned_ ? "u" : "", this->bits_); ret->append(buf); @@ -387,8 +612,8 @@ void Float_type::do_mangled_name(Gogo*, std::string* ret) const { char buf[100]; - snprintf(buf, sizeof buf, "f%s%de", - this->is_abstract_ ? "a" : "", + snprintf(buf, sizeof buf, "%sfloat%d", + this->is_abstract_ ? "{abstract}" : "", this->bits_); ret->append(buf); } @@ -397,8 +622,8 @@ void Complex_type::do_mangled_name(Gogo*, std::string* ret) const { char buf[100]; - snprintf(buf, sizeof buf, "c%s%de", - this->is_abstract_ ? "a" : "", + snprintf(buf, sizeof buf, "%sc%d", + this->is_abstract_ ? "{abstract}" : "", this->bits_); ret->append(buf); } @@ -406,83 +631,103 @@ Complex_type::do_mangled_name(Gogo*, std::string* ret) const void String_type::do_mangled_name(Gogo*, std::string* ret) const { - ret->push_back('z'); + ret->append("string"); } void Function_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - ret->push_back('F'); + ret->append("func"); if (this->receiver_ != NULL) { - ret->push_back('m'); + ret->push_back('('); this->append_mangled_name(this->receiver_->type(), gogo, ret); + ret->append(")"); } + ret->push_back('('); const Typed_identifier_list* params = this->parameters(); if (params != NULL) { - ret->push_back('p'); + bool first = true; for (Typed_identifier_list::const_iterator p = params->begin(); p != params->end(); ++p) - this->append_mangled_name(p->type(), gogo, ret); - if (this->is_varargs_) - ret->push_back('V'); - ret->push_back('e'); + { + if (first) + first = false; + 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(",,,"); + } + this->append_mangled_name(p->type(), gogo, ret); + } } + ret->push_back(')'); + ret->push_back('('); const Typed_identifier_list* results = this->results(); if (results != NULL) { - ret->push_back('r'); + bool first = true; for (Typed_identifier_list::const_iterator p = results->begin(); p != results->end(); ++p) - this->append_mangled_name(p->type(), gogo, ret); - ret->push_back('e'); + { + if (first) + first = false; + else + ret->append(","); + this->append_mangled_name(p->type(), gogo, ret); + } } - - ret->push_back('e'); + ret->push_back(')'); } void Pointer_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - ret->push_back('p'); + ret->push_back('*'); this->append_mangled_name(this->to_type_, gogo, ret); } void Nil_type::do_mangled_name(Gogo*, std::string* ret) const { - ret->push_back('n'); + ret->append("{nil}"); } void Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - ret->push_back('S'); + ret->append("struct{"); + + if (this->is_struct_incomparable_) + ret->append("{x}"); const Struct_field_list* fields = this->fields_; if (fields != NULL) { + bool first = true; for (Struct_field_list::const_iterator p = fields->begin(); p != fields->end(); ++p) { - if (p->is_anonymous()) - ret->append("0_"); + if (first) + first = false; else - { + ret->push_back(';'); - std::string n(Gogo::mangle_possibly_hidden_name(p->field_name())); - char buf[20]; - snprintf(buf, sizeof buf, "%u_", - static_cast(n.length())); - ret->append(buf); - ret->append(n); + if (!p->is_anonymous()) + { + ret->append(Gogo::mangle_possibly_hidden_name(p->field_name())); + ret->push_back(' '); } // For an anonymous field with an alias type, the field name @@ -493,44 +738,26 @@ Struct_type::do_mangled_name(Gogo* gogo, std::string* ret) const p->type()->named_type()->append_mangled_type_name(gogo, true, ret); else this->append_mangled_name(p->type(), gogo, ret); + if (p->has_tag()) { - const std::string& tag(p->tag()); - std::string out; - for (std::string::const_iterator p = tag.begin(); - p != tag.end(); - ++p) - { - if (ISALNUM(*p) || *p == '_') - out.push_back(*p); - else - { - char buf[20]; - snprintf(buf, sizeof buf, ".%x.", - static_cast(*p)); - out.append(buf); - } - } - char buf[20]; - snprintf(buf, sizeof buf, "T%u_", - static_cast(out.length())); - ret->append(buf); - ret->append(out); + // Use curly braces around a struct tag, since they are + // unambiguous here and we have no encoding for + // quotation marks. + ret->push_back('{'); + ret->append(go_mangle_struct_tag(p->tag())); + ret->push_back('}'); } } } - if (this->is_struct_incomparable_) - ret->push_back('x'); - - ret->push_back('e'); + ret->push_back('}'); } void Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - ret->push_back('A'); - this->append_mangled_name(this->element_type_, gogo, ret); + ret->push_back('['); if (this->length_ != NULL) { Numeric_constant nc; @@ -550,30 +777,31 @@ Array_type::do_mangled_name(Gogo* gogo, std::string* ret) const free(s); mpz_clear(val); if (this->is_array_incomparable_) - ret->push_back('x'); + ret->append("x"); } - ret->push_back('e'); + ret->push_back(']'); + this->append_mangled_name(this->element_type_, gogo, ret); } void Map_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - ret->push_back('M'); + ret->append("map["); this->append_mangled_name(this->key_type_, gogo, ret); - ret->append("__"); + ret->push_back(']'); this->append_mangled_name(this->val_type_, gogo, ret); } void Channel_type::do_mangled_name(Gogo* gogo, std::string* ret) const { - ret->push_back('C'); + if (!this->may_send_) + ret->append("{}"); + ret->append("chan"); + if (!this->may_receive_) + ret->append("{}"); + ret->push_back(' '); this->append_mangled_name(this->element_type_, gogo, ret); - if (this->may_send_) - ret->push_back('s'); - if (this->may_receive_) - ret->push_back('r'); - ret->push_back('e'); } void @@ -581,31 +809,34 @@ Interface_type::do_mangled_name(Gogo* gogo, std::string* ret) const { go_assert(this->methods_are_finalized_); - ret->push_back('I'); + ret->append("interface{"); const Typed_identifier_list* methods = this->all_methods_; if (methods != NULL && !this->seen_) { this->seen_ = true; + bool first = true; for (Typed_identifier_list::const_iterator p = methods->begin(); p != methods->end(); ++p) { + if (first) + first = false; + else + ret->push_back(';'); + if (!p->name().empty()) { - std::string n(Gogo::mangle_possibly_hidden_name(p->name())); - char buf[20]; - snprintf(buf, sizeof buf, "%u_", - static_cast(n.length())); - ret->append(buf); - ret->append(n); + ret->append(Gogo::mangle_possibly_hidden_name(p->name())); + ret->push_back(' '); } + this->append_mangled_name(p->type(), gogo, ret); } this->seen_ = false; } - ret->push_back('e'); + ret->push_back('}'); } void @@ -622,18 +853,12 @@ Forward_declaration_type::do_mangled_name(Gogo* gogo, std::string* ret) const else { const Named_object* no = this->named_object(); - std::string name; if (no->package() == NULL) - name = gogo->pkgpath_symbol(); + ret->append(gogo->pkgpath_symbol()); else - name = no->package()->pkgpath_symbol(); - name += '.'; - name += Gogo::unpack_hidden_name(no->name()); - char buf[20]; - snprintf(buf, sizeof buf, "N%u_", - static_cast(name.length())); - ret->append(buf); - ret->append(name); + ret->append(no->package()->pkgpath_symbol()); + ret->push_back('.'); + ret->append(Gogo::unpack_hidden_name(no->name())); } } @@ -662,37 +887,44 @@ Named_type::append_mangled_type_name(Gogo* gogo, bool use_alias, go_assert(this->in_function_ == NULL); else { - const std::string& pkgpath(no->package() == NULL - ? gogo->pkgpath_symbol() - : no->package()->pkgpath_symbol()); - name = pkgpath; - name.append(1, '.'); if (this->in_function_ != NULL) { const Typed_identifier* rcvr = this->in_function_->func_value()->type()->receiver(); if (rcvr != NULL) { - Named_type* rcvr_type = rcvr->type()->deref()->named_type(); - name.append(Gogo::unpack_hidden_name(rcvr_type->name())); - name.append(1, '.'); - } - name.append(Gogo::unpack_hidden_name(this->in_function_->name())); - name.append(1, '$'); - if (this->in_function_index_ > 0) - { - char buf[30]; - snprintf(buf, sizeof buf, "%u", this->in_function_index_); - name.append(buf); - name.append(1, '$'); + std::string m = rcvr->type()->mangled_name(gogo); + // Turn a leading ".1" back into "*" since we are going + // to type-mangle this name again. + if (m.compare(0, 2, ".1") == 0) + m = "*" + m.substr(2); + ret->append(m); } + else if (this->in_function_->package() == NULL) + ret->append(gogo->pkgpath_symbol()); + else + ret->append(this->in_function_->package()->pkgpath_symbol()); + ret->push_back('.'); + ret->append(Gogo::unpack_hidden_name(this->in_function_->name())); } + else + { + if (no->package() == NULL) + ret->append(gogo->pkgpath_symbol()); + else + ret->append(no->package()->pkgpath_symbol()); + } + ret->push_back('.'); + } + + ret->append(Gogo::unpack_hidden_name(no->name())); + + if (this->in_function_ != NULL && this->in_function_index_ > 0) + { + char buf[30]; + snprintf(buf, sizeof buf, "..i%u", this->in_function_index_); + ret->append(buf); } - name.append(Gogo::unpack_hidden_name(no->name())); - char buf[20]; - snprintf(buf, sizeof buf, "N%u_", static_cast(name.length())); - ret->append(buf); - ret->append(name); } // Return the name for the type descriptor symbol for TYPE. This can @@ -704,50 +936,53 @@ Gogo::type_descriptor_name(Type* type, Named_type* nt) { // 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. + // that symbol for all unsafe pointer types. if (type->is_unsafe_pointer_type()) - return "__go_tdn_unsafe.Pointer"; + return "unsafe.Pointer..d"; if (nt == NULL) - return "__go_td_" + type->mangled_name(this); + return "type.." + type->mangled_name(this); + std::string ret; Named_object* no = nt->named_object(); unsigned int index; const Named_object* in_function = nt->in_function(&index); - std::string ret = "__go_tdn_"; if (nt->is_builtin()) go_assert(in_function == NULL); else { - const std::string& pkgpath(no->package() == NULL - ? this->pkgpath_symbol() - : no->package()->pkgpath_symbol()); - ret.append(pkgpath); - ret.append(1, '.'); if (in_function != NULL) { const Typed_identifier* rcvr = in_function->func_value()->type()->receiver(); if (rcvr != NULL) - { - Named_type* rcvr_type = rcvr->type()->deref()->named_type(); - ret.append(Gogo::unpack_hidden_name(rcvr_type->name())); - ret.append(1, '.'); - } + ret.append(rcvr->type()->mangled_name(this)); + else if (in_function->package() == NULL) + ret.append(this->pkgpath_symbol()); + else + ret.append(in_function->package()->pkgpath_symbol()); + ret.push_back('.'); ret.append(Gogo::unpack_hidden_name(in_function->name())); - ret.append(1, '.'); - if (index > 0) - { - char buf[30]; - snprintf(buf, sizeof buf, "%u", index); - ret.append(buf); - ret.append(1, '.'); - } + ret.push_back('.'); } + + if (no->package() == NULL) + ret.append(this->pkgpath_symbol()); + else + ret.append(no->package()->pkgpath_symbol()); + ret.push_back('.'); } - std::string mname(Gogo::mangle_possibly_hidden_name(no->name())); - ret.append(mname); + ret.append(Gogo::mangle_possibly_hidden_name(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; } @@ -761,11 +996,11 @@ Gogo::type_descriptor_name(Type* type, Named_type* nt) std::string Gogo::gc_symbol_name(Type* type) { - return this->type_descriptor_name(type, type->named_type()) + "$gc"; + return this->type_descriptor_name(type, type->named_type()) + "..g"; } // Return the name for a ptrmask variable. PTRMASK_SYM_NAME is a -// base64 string encoding the ptrmask (as returned by Ptrmask::symname +// 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 @@ -774,7 +1009,7 @@ Gogo::gc_symbol_name(Type* type) std::string Gogo::ptrmask_symbol_name(const std::string& ptrmask_sym_name) { - return "runtime.gcbits." + ptrmask_sym_name; + return "gcbits.." + ptrmask_sym_name; } // Return the name to use for an interface method table used for the @@ -786,8 +1021,25 @@ std::string Gogo::interface_method_table_name(Interface_type* itype, Type* type, bool is_pointer) { - return ((is_pointer ? "__go_pimt__" : "__go_imt_") + return ((is_pointer ? "pimt.." : "imt..") + itype->mangled_name(this) - + "__" + + ".." + type->mangled_name(this)); } + +// 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 '.'. + +bool +Gogo::is_special_name(const std::string& name) +{ + 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); +} diff --git a/gcc/go/gofrontend/statements.cc b/gcc/go/gofrontend/statements.cc index 1c079bb40c7d..094966592f61 100644 --- a/gcc/go/gofrontend/statements.cc +++ b/gcc/go/gofrontend/statements.cc @@ -2120,7 +2120,7 @@ Thunk_statement::simplify_statement(Gogo* gogo, Named_object* function, fn = Expression::make_temporary_reference(fn_temp, location); } - std::string thunk_name = Gogo::thunk_name(); + std::string thunk_name = gogo->thunk_name(); // Build the thunk. this->build_thunk(gogo, thunk_name); diff --git a/gcc/go/gofrontend/types.cc b/gcc/go/gofrontend/types.cc index 85273bf55d20..28eef8e9ed9d 100644 --- a/gcc/go/gofrontend/types.cc +++ b/gcc/go/gofrontend/types.cc @@ -2654,14 +2654,14 @@ 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. We convert it to a base64 string. +// short. We convert it to a string that uses only 32 characters, +// avoiding digits and u and U. std::string Ptrmask::symname() const { - const char chars[65] = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_."; - go_assert(chars[64] == '\0'); + const char chars[33] = "abcdefghijklmnopqrstvwxyzABCDEFG"; + go_assert(chars[32] == '\0'); std::string ret; unsigned int b = 0; int remaining = 0; @@ -2671,18 +2671,18 @@ Ptrmask::symname() const { b |= *p << remaining; remaining += 8; - while (remaining >= 6) + while (remaining >= 5) { - ret += chars[b & 0x3f]; - b >>= 6; - remaining -= 6; + ret += chars[b & 0x1f]; + b >>= 5; + remaining -= 5; } } while (remaining > 0) { - ret += chars[b & 0x3f]; - b >>= 6; - remaining -= 6; + ret += chars[b & 0x1f]; + b >>= 5; + remaining -= 5; } return ret; } @@ -4447,7 +4447,11 @@ Function_type::is_identical(const Function_type* t, bool ignore_receiver, } const Typed_identifier_list* parms1 = this->parameters(); + if (parms1 != NULL && parms1->empty()) + parms1 = NULL; const Typed_identifier_list* parms2 = t->parameters(); + if (parms2 != NULL && parms2->empty()) + parms2 = NULL; if ((parms1 != NULL) != (parms2 != NULL)) { if (reason != NULL) @@ -4492,7 +4496,11 @@ Function_type::is_identical(const Function_type* t, bool ignore_receiver, } const Typed_identifier_list* results1 = this->results(); + if (results1 != NULL && results1->empty()) + results1 = NULL; const Typed_identifier_list* results2 = t->results(); + if (results2 != NULL && results2->empty()) + results2 = NULL; if ((results1 != NULL) != (results2 != NULL)) { if (reason != NULL) @@ -11144,7 +11152,7 @@ Type::build_stub_methods(Gogo* gogo, const Type* type, const Methods* methods, package = NULL; else package = type->named_type()->named_object()->package(); - std::string stub_name = gogo->stub_method_name(name); + std::string stub_name = gogo->stub_method_name(package, name); Named_object* stub; if (package != NULL) stub = Named_object::make_function_declaration(stub_name, package, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 2c637fe5d891..bdc01f7cfc1e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2018-01-24 Ian Lance Taylor + + * go.go-torture/execute/names-1.go: New test. + 2018-01-19 Jeff Law PR target/83994 diff --git a/gcc/testsuite/go.go-torture/execute/names-1.go b/gcc/testsuite/go.go-torture/execute/names-1.go new file mode 100644 index 000000000000..bac91945bc76 --- /dev/null +++ b/gcc/testsuite/go.go-torture/execute/names-1.go @@ -0,0 +1,195 @@ +// names-1 is a change detector for Go symbol names. We don't want +// the name mangling to change silently. +package main + +import ( + "bytes" + "debug/elf" + "debug/macho" + "debug/pe" + "debug/xcoff" + "fmt" + "os" + "strings" +) + +type Type int +type Alias = int + +//go:noinline +func Function1(out *bytes.Buffer) int { + var f2 func(int) int + f1 := func(i int) int { + if i == 0 { + return 0 + } + type NestedType struct { a int } + t := NestedType{f2(i-1)} + fmt.Fprint(out, t) + return t.a + } + f2 = func(i int) int { + if i == 0 { + return 0 + } + type NestedType struct { a int } + t := NestedType{f1(i-1)} + fmt.Fprint(out, t) + return t.a + } + return f1(10) + f2(10) +} + +//go:noinline +func Function2(out *bytes.Buffer) { + { + type T struct { b int } + fmt.Fprint(out, T{1}) + } + { + type T struct { b int } + fmt.Fprint(out, T{2}) + } +} + +func (t Type) M(bool, int8, float32, complex64, string, func(), func(int16) (float64, complex128), *byte, struct { f int "tag #$%^&{}: 世界" }, []int32, [24]int64, map[uint8]uint16, chan uint32, <-chan uint64, chan <- uintptr, Type, Alias) { +} + +//go:noinline +func Function3(out *bytes.Buffer) { + fmt.Fprintf(out, "%T", Type(0)) +} + +func main() { + var b bytes.Buffer + Function1(&b) + Function2(&b) + Function3(&b) + _ = len(b.String()) + + for _, n := range []string{"/proc/self/exe", os.Args[0]} { + if f, err := os.Open(n); err == nil { + checkFile(f) + return + } + } + fmt.Println("checksyms: could not find executable") + fmt.Println("UNSUPPORTED: checksyms") +} + +func checkFile(f *os.File) { + var syms []string + if ef, err := elf.NewFile(f); err == nil { + esyms, err := ef.Symbols() + if err != nil { + panic(err) + } + for _, esym := range esyms { + syms = append(syms, esym.Name) + } + } else if mf, err := macho.NewFile(f); err == nil { + for _, msym := range mf.Symtab.Syms { + syms = append(syms, msym.Name) + } + } else if pf, err := pe.NewFile(f); err == nil { + for _, psym := range pf.Symbols { + syms = append(syms, psym.Name) + } + } else if xf, err := xcoff.NewFile(f); err == nil { + for _, xsym := range xf.Symbols { + syms = append(syms, xsym.Name) + } + } else { + fmt.Println("checksyms: could not parse executable") + fmt.Println("UNSUPPORTED: checksyms") + return + } + checkSyms(syms) +} + +var want = []string{ + "main.Function1", + "main.Function1..f", + "main.Function1..func1", + "main.Function1..func1.main.NestedType..d", + "main.Function1..func2", + "main.Function1..func2.main.NestedType..d", + "main.Function2", + "main.Function2..f", + "main.Function2.main.T..d", + "main.Function2.main.T..i1..d", + "main.Function3", + "main.Function3..f", + "main.Type..d", + "main.Type.M", + "main.main", + "main.want", + "type...1.1main.Type", // Why is this here? + "type...1main.Function1..func1.NestedType", + "type...1main.Function1..func2.NestedType", + "type...1main.Function2.T", + "type...1main.Function2.T..i1", + "type...1main.Type", + "type..func.8.1main.Type.3bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9", + "type..func.8bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9", + "type..func.8main.Type.3bool.3int8.3float32.3complex64.3string.3func.8.9.8.9.3func.8int16.9.8float64.3complex128.9.3.1uint8.3struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5.3.6.7int32.3.624.7int64.3map.6uint8.7uint16.3chan.0uint32.3.4.5chan.0uint64.3chan.4.5.0uintptr.3main.Type.3int.9.8.9", + "type..struct.4.main.f.0int.4tag.x20.x23.x24.x25.x5e.x26.x7b.x7d.x3a.x20..u4e16..u754c.5.5", +} + +func checkSyms(syms []string) { + m := make(map[string]bool) + for _, sym := range syms { + if strings.Contains(sym, ".") { + m[sym] = true + } + } + + ok := true + for _, w := range want { + if m[w] { + delete(m, w) + } else { + fmt.Printf("checksyms: missing expected symbol %q\n", w) + ok = false + } + } + + for sym := range m { + if !strings.Contains(sym, "main") { + continue + } + + // Skip some symbols we may see but know are unimportant. + if sym == "go-main.c" { + continue + } + if strings.HasPrefix(sym, "runtime.") { + continue + } + + // We can see a lot of spurious .eq and .hash + // functions for types defined in other packages. + // This is a bug but skip them for now. + if strings.Contains(sym, "..eq") || strings.Contains(sym, "..hash") { + continue + } + + // Skip closure types by skipping incomparable structs. + // This may be a bug, not sure. + if strings.Contains(sym, ".4x.5") { + continue + } + + // These functions may be inlined. + if sym == "main.checkFile" || sym == "main.checkSyms" { + continue + } + + fmt.Printf("checksyms: found unexpected symbol %q\n", sym) + ok = false + } + + if !ok { + fmt.Println("FAIL: checksyms") + } +} diff --git a/libgo/go/runtime/crash_test.go b/libgo/go/runtime/crash_test.go index 8ec034835ecc..06200c7303b9 100644 --- a/libgo/go/runtime/crash_test.go +++ b/libgo/go/runtime/crash_test.go @@ -425,7 +425,7 @@ func TestPanicTraceback(t *testing.T) { // Check functions in the traceback. fns := []string{"main.pt1.func1", "panic", "main.pt2.func1", "panic", "main.pt2", "main.pt1"} if runtime.Compiler == "gccgo" { - fns = []string{"main.$nested", "panic", "main.$nested", "panic", "main.pt2", "main.pt1"} + fns = []string{"main.pt1..func1", "panic", "main.pt2..func1", "panic", "main.pt2", "main.pt1"} } for _, fn := range fns { var re *regexp.Regexp @@ -570,7 +570,7 @@ func TestPanicInlined(t *testing.T) { buf = buf[:n] want := []byte("(*point).negate(") if runtime.Compiler == "gccgo" { - want = []byte("negate.pN18_runtime_test.point") + want = []byte("point.negate") } if !bytes.Contains(buf, want) { t.Logf("%s", buf) diff --git a/libgo/go/runtime/panic.go b/libgo/go/runtime/panic.go index b2deb6e65802..24d78eca62d3 100644 --- a/libgo/go/runtime/panic.go +++ b/libgo/go/runtime/panic.go @@ -656,7 +656,7 @@ func canrecover(retaddr uintptr) bool { } // Ignore other functions in the reflect package. - if hasprefix(name, "reflect.") { + if hasprefix(name, "reflect.") || hasprefix(name, ".1reflect.") { continue } diff --git a/libgo/go/runtime/pprof/pprof_test.go b/libgo/go/runtime/pprof/pprof_test.go index 08a4f969ca2b..ae6ec6d0cfc8 100644 --- a/libgo/go/runtime/pprof/pprof_test.go +++ b/libgo/go/runtime/pprof/pprof_test.go @@ -730,7 +730,7 @@ func TestMutexProfile(t *testing.T) { stks := stacks(p) for _, want := range [][]string{ // {"sync.(*Mutex).Unlock", "pprof.blockMutex.func1"}, - {"sync.Unlock.pN10_sync.Mutex", "pprof.$nested17"}, + {".1sync.Mutex.Unlock", "pprof.blockMutex..func1"}, } { if !containsStack(stks, want) { t.Errorf("No matching stack entry for %+v", want) diff --git a/libgo/go/syscall/wait.c b/libgo/go/syscall/wait.c index a50f7d6f5c2f..c0c5cca39af5 100644 --- a/libgo/go/syscall/wait.c +++ b/libgo/go/syscall/wait.c @@ -17,7 +17,7 @@ #endif extern _Bool Exited (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.Exited.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Exited"); _Bool Exited (uint32_t *w) @@ -26,7 +26,7 @@ Exited (uint32_t *w) } extern _Bool Signaled (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.Signaled.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Signaled"); _Bool Signaled (uint32_t *w) @@ -35,7 +35,7 @@ Signaled (uint32_t *w) } extern _Bool Stopped (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.Stopped.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Stopped"); _Bool Stopped (uint32_t *w) @@ -44,7 +44,7 @@ Stopped (uint32_t *w) } extern _Bool Continued (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.Continued.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Continued"); _Bool Continued (uint32_t *w) @@ -53,7 +53,7 @@ Continued (uint32_t *w) } extern _Bool CoreDump (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.CoreDump.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.CoreDump"); _Bool CoreDump (uint32_t *w) @@ -62,7 +62,7 @@ CoreDump (uint32_t *w) } extern int ExitStatus (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.ExitStatus.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.ExitStatus"); int ExitStatus (uint32_t *w) @@ -73,7 +73,7 @@ ExitStatus (uint32_t *w) } extern int Signal (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.Signal.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.Signal"); int Signal (uint32_t *w) @@ -84,7 +84,7 @@ Signal (uint32_t *w) } extern int StopSignal (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.StopSignal.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.StopSignal"); int StopSignal (uint32_t *w) @@ -95,7 +95,7 @@ StopSignal (uint32_t *w) } extern int TrapCause (uint32_t *w) - __asm__ (GOSYM_PREFIX "syscall.TrapCause.N18_syscall.WaitStatus"); + __asm__ (GOSYM_PREFIX "syscall.WaitStatus.TrapCause"); int TrapCause (uint32_t *w __attribute__ ((unused))) diff --git a/libgo/runtime/go-callers.c b/libgo/runtime/go-callers.c index 9f376c7f4aa6..2eaac6827797 100644 --- a/libgo/runtime/go-callers.c +++ b/libgo/runtime/go-callers.c @@ -68,13 +68,14 @@ callback (void *data, uintptr_t pc, const char *filename, int lineno, { const char *p; - p = __builtin_strchr (function, '.'); - if (p != NULL && __builtin_strncmp (p + 1, "$thunk", 6) == 0) + p = function + __builtin_strlen (function); + while (p > function && p[-1] >= '0' && p[-1] <= '9') + --p; + if (p - function > 7 && __builtin_strncmp (p - 7, "..thunk", 7) == 0) return 0; - p = __builtin_strrchr (function, '$'); - if (p != NULL && __builtin_strcmp(p, "$recover") == 0) + if (p - function > 3 && __builtin_strcmp (p - 3, "..r") == 0) return 0; - if (p != NULL && __builtin_strncmp(p, "$stub", 5) == 0) + if (p - function > 6 && __builtin_strcmp (p - 6, "..stub") == 0) return 0; } diff --git a/libgo/runtime/go-unsafe-pointer.c b/libgo/runtime/go-unsafe-pointer.c index 9df7f3aee081..5afd0112fe2a 100644 --- a/libgo/runtime/go-unsafe-pointer.c +++ b/libgo/runtime/go-unsafe-pointer.c @@ -15,10 +15,10 @@ descriptor. */ extern const struct __go_type_descriptor unsafe_Pointer - __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer"); + __asm__ (GOSYM_PREFIX "unsafe.Pointer..d"); extern const byte unsafe_Pointer_gc[] - __asm__ (GOSYM_PREFIX "__go_tdn_unsafe.Pointer$gc"); + __asm__ (GOSYM_PREFIX "unsafe.Pointer..g"); /* Used to determine the field alignment. */ struct field_align @@ -38,9 +38,9 @@ static const String reflection_string = const byte unsafe_Pointer_gc[] = { 1 }; extern const FuncVal runtime_pointerhash_descriptor - __asm__ (GOSYM_PREFIX "runtime.pointerhash$descriptor"); + __asm__ (GOSYM_PREFIX "runtime.pointerhash..f"); extern const FuncVal runtime_pointerequal_descriptor - __asm__ (GOSYM_PREFIX "runtime.pointerequal$descriptor"); + __asm__ (GOSYM_PREFIX "runtime.pointerequal..f"); const struct __go_type_descriptor unsafe_Pointer = { @@ -75,7 +75,7 @@ const struct __go_type_descriptor unsafe_Pointer = it to be defined elsewhere. */ extern const struct __go_ptr_type pointer_unsafe_Pointer - __asm__ (GOSYM_PREFIX "__go_td_pN14_unsafe.Pointer"); + __asm__ (GOSYM_PREFIX "type...1unsafe.Pointer"); /* The reflection string. */ #define PREFLECTION "*unsafe.Pointer" @@ -86,7 +86,7 @@ static const String preflection_string = }; extern const byte pointer_unsafe_Pointer_gc[] - __asm__ (GOSYM_PREFIX "__go_td_pN14_unsafe.Pointer$gc"); + __asm__ (GOSYM_PREFIX "type...1unsafe.Pointer..g"); const byte pointer_unsafe_Pointer_gc[] = { 1 };