mirror of
https://gcc.gnu.org/git/gcc.git
synced 2024-11-24 03:14:08 +08:00
compiler: rationalize external symbol names
Encode all external symbol names using only ASCII alphanumeric characters, underscore, and dot. Use a scheme that can be reliably demangled to a somewhat readable version as described in the long comment in names.cc. A minor cleanup discovered during this was that we were treating function types as different if one had a NULL parameters_ field and another has a non-NULL parameters_ field that has no parameters. This worked because we mangled them slightly differently. We now mangle them the same, so we treat them as equal, as we should anyhow. Reviewed-on: https://go-review.googlesource.com/89555 * go.go-torture/execute/names-1.go: New test. From-SVN: r257033
This commit is contained in:
parent
d3719ee2c0
commit
4880b994d6
@ -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.
|
||||
|
@ -686,8 +686,7 @@ 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())
|
||||
{
|
||||
@ -712,16 +711,8 @@ debug_function_name(Named_object* fn)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return fnname;
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -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);
|
||||
|
@ -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,15 +108,24 @@ 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))
|
||||
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);
|
||||
char buf[16];
|
||||
snprintf(buf, sizeof buf, "..U%08x", c);
|
||||
ret += buf;
|
||||
ret += "$";
|
||||
}
|
||||
p += len;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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))
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -1,3 +1,7 @@
|
||||
2018-01-24 Ian Lance Taylor <iant@golang.org>
|
||||
|
||||
* go.go-torture/execute/names-1.go: New test.
|
||||
|
||||
2018-01-19 Jeff Law <law@redhat.com>
|
||||
|
||||
PR target/83994
|
||||
|
195
gcc/testsuite/go.go-torture/execute/names-1.go
Normal file
195
gcc/testsuite/go.go-torture/execute/names-1.go
Normal file
@ -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")
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 };
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user