Go patch committed: Rationalize external symbol names

Message ID CAOyqgcV_rG0JYNc+m7LdoHgqAmCZju82wyJayqP9sebCkrapmw@mail.gmail.com
State New
Headers show
Series
  • Go patch committed: Rationalize external symbol names
Related show

Commit Message

Ian Lance Taylor Jan. 24, 2018, 11:50 p.m.
This patch to the Go frontend rationalizes the external symbol names
that appear in assembler code.  It changes from the ad hoc mechanisms
used to date to produce a set of names that are at least somewhat more
coherent.  They are also more readable, after applying a simple
demangling algorithms outlined in the long comment in names.cc.  The
new names use only ASCII alphanumeric characters, underscore, and dot
(which fixes AIX by avoiding the use of dollar sign).  If we really
had to we could replace dot with underscore at the cost of forbidding
some uses of underscore in Go identifier names.

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.

Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed
to mainline.

Ian


2018-01-24  Ian Lance Taylor  <iant@golang.org>

* go.go-torture/execute/names-1.go: New test.

Comments

Rainer Orth Jan. 25, 2018, 8:28 p.m. | #1
Hi Ian,

> This patch to the Go frontend rationalizes the external symbol names

> that appear in assembler code.  It changes from the ad hoc mechanisms

> used to date to produce a set of names that are at least somewhat more

> coherent.  They are also more readable, after applying a simple

> demangling algorithms outlined in the long comment in names.cc.  The

> new names use only ASCII alphanumeric characters, underscore, and dot

> (which fixes AIX by avoiding the use of dollar sign).  If we really

> had to we could replace dot with underscore at the cost of forbidding

> some uses of underscore in Go identifier names.

>

> 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.

>

> Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed

> to mainline.


this patch almost certainly (i.e. I didn't reghunt, but it's the only
plausible candidate between r257023 and r257057) Solaris/x86 bootstrap
with /bin/as:

Assembler: doc.go
        "/var/tmp//ccbyjwCc.s", line 1043 : Syntax error
        Near line: "    .globl  .1fmt.fmt.clearflags"
        "/var/tmp//ccbyjwCc.s", line 1044 : Syntax error
        Near line: "    .type   .1fmt.fmt.clearflags, @function"
        "/var/tmp//ccbyjwCc.s", line 1045 : Illegal mnemonic
        Near line: ".1fmt.fmt.clearflags:"
        "/var/tmp//ccbyjwCc.s", line 1045 : Syntax error
        Near line: ".1fmt.fmt.clearflags:"
        "/var/tmp//ccbyjwCc.s", line 1045 : Syntax error
        Near line: ".1fmt.fmt.clearflags:"
        "/var/tmp//ccbyjwCc.s", line 1058 : Syntax error
        Near line: "    .size   .1fmt.fmt.clearflags, .-.1fmt.fmt.clearflags"
        "/var/tmp//ccbyjwCc.s", line 1060 : Syntax error
        Near line: "    .globl  .1fmt.pp.Width"
        "/var/tmp//ccbyjwCc.s", line 1061 : Syntax error
        Near line: "    .type   .1fmt.pp.Width, @function"
        "/var/tmp//ccbyjwCc.s", line 1062 : Illegal mnemonic
        Near line: ".1fmt.pp.Width:"
        "/var/tmp//ccbyjwCc.s", line 1062 : Syntax error
        Near line: ".1fmt.pp.Width:"
        "/var/tmp//ccbyjwCc.s", line 1062 : Syntax error
        Near line: ".1fmt.pp.Width:"
        "/var/tmp//ccbyjwCc.s", line 1082 : Syntax error
        Near line: "    .size   .1fmt.pp.Width, .-.1fmt.pp.Width"
        "/var/tmp//ccbyjwCc.s", line 1084 : Syntax error
        Near line: "    .globl  .1fmt.pp.Precision"
        "/var/tmp//ccbyjwCc.s", line 1085 : Syntax error
        Near line: "    .type   .1fmt.pp.Precision, @function"
        "/var/tmp//ccbyjwCc.s", line 1086 : Illegal mnemonic
        Near line: ".1fmt.pp.Precision:"
        "/var/tmp//ccbyjwCc.s", line 1086 : Syntax error
        Near line: ".1fmt.pp.Precision:"
        "/var/tmp//ccbyjwCc.s", line 1086 : Syntax error
        Near line: ".1fmt.pp.Precision:"
        "/var/tmp//ccbyjwCc.s", line 1106 : Syntax error
        Near line: "    .size   .1fmt.pp.Precision, .-.1fmt.pp.Precision"
        "/var/tmp//ccbyjwCc.s", line 1108 : Syntax error
        Near line: "    .globl  .1fmt.ss.Width"
        "/var/tmp//ccbyjwCc.s", line 1109 : Syntax error
        Near line: "    .type   .1fmt.ss.Width, @function"
        "/var/tmp//ccbyjwCc.s", line 1110 : Illegal mnemonic
        Near line: ".1fmt.ss.Width:"
        "/var/tmp//ccbyjwCc.s", line 1110 : Syntax error
        Near line: ".1fmt.ss.Width:"
        "/var/tmp//ccbyjwCc.s", line 1110 : Syntax error
        Near line: ".1fmt.ss.Width:"
        "/var/tmp//ccbyjwCc.s", line 1149 : Syntax error
        Near line: "    .size   .1fmt.ss.Width, .-.1fmt.ss.Width"
        "/var/tmp//ccbyjwCc.s", line 1151 : Syntax error
        Near line: "    .globl  .1fmt.ss.UnreadRune"
        "/var/tmp//ccbyjwCc.s", line 1152 : Syntax error
        Near line: "    .type   .1fmt.ss.UnreadRune, @function"
        "/var/tmp//ccbyjwCc.s", line 1153 : Illegal mnemonic
        Near line: ".1fmt.ss.UnreadRune:"
        "/var/tmp//ccbyjwCc.s", line 1153 : Syntax error
        Near line: ".1fmt.ss.UnreadRune:"
        "/var/tmp//ccbyjwCc.s", line 1153 : Syntax error
        Near line: ".1fmt.ss.UnreadRune:"
        "/var/tmp//ccbyjwCc.s", line 1209 : Syntax error
        Near line: "    .size   .1fmt.ss.UnreadRune, .-.1fmt.ss.UnreadRune"
        "/var/tmp//ccbyjwCc.s", line 1908 : Syntax error
        Near line: "    .globl  .1fmt.fmt.init"
Too many errors - Goodbye
make[4]: *** [Makefile:3322: fmt.lo] Error 1

Solaris/SPARC with /bin/as is fine, but that's not too astonishing since
both assemblers are mostly different code bases.

	Rainer

-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University
Ian Lance Taylor Jan. 25, 2018, 8:40 p.m. | #2
On Thu, Jan 25, 2018 at 12:28 PM, Rainer Orth
<ro@cebitec.uni-bielefeld.de> wrote:
>

>> This patch to the Go frontend rationalizes the external symbol names

>> that appear in assembler code.  It changes from the ad hoc mechanisms

>> used to date to produce a set of names that are at least somewhat more

>> coherent.  They are also more readable, after applying a simple

>> demangling algorithms outlined in the long comment in names.cc.  The

>> new names use only ASCII alphanumeric characters, underscore, and dot

>> (which fixes AIX by avoiding the use of dollar sign).  If we really

>> had to we could replace dot with underscore at the cost of forbidding

>> some uses of underscore in Go identifier names.

>>

>> 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.

>>

>> Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed

>> to mainline.

>

> this patch almost certainly (i.e. I didn't reghunt, but it's the only

> plausible candidate between r257023 and r257057) Solaris/x86 bootstrap

> with /bin/as:

>

> Assembler: doc.go

>         "/var/tmp//ccbyjwCc.s", line 1043 : Syntax error

>         Near line: "    .globl  .1fmt.fmt.clearflags"

>         "/var/tmp//ccbyjwCc.s", line 1044 : Syntax error

>         Near line: "    .type   .1fmt.fmt.clearflags, @function"

>         "/var/tmp//ccbyjwCc.s", line 1045 : Illegal mnemonic

>         Near line: ".1fmt.fmt.clearflags:"

>         "/var/tmp//ccbyjwCc.s", line 1045 : Syntax error

>         Near line: ".1fmt.fmt.clearflags:"

>         "/var/tmp//ccbyjwCc.s", line 1045 : Syntax error

>         Near line: ".1fmt.fmt.clearflags:"

>         "/var/tmp//ccbyjwCc.s", line 1058 : Syntax error

>         Near line: "    .size   .1fmt.fmt.clearflags, .-.1fmt.fmt.clearflags"

>         "/var/tmp//ccbyjwCc.s", line 1060 : Syntax error

>         Near line: "    .globl  .1fmt.pp.Width"

>         "/var/tmp//ccbyjwCc.s", line 1061 : Syntax error

>         Near line: "    .type   .1fmt.pp.Width, @function"

>         "/var/tmp//ccbyjwCc.s", line 1062 : Illegal mnemonic

>         Near line: ".1fmt.pp.Width:"

>         "/var/tmp//ccbyjwCc.s", line 1062 : Syntax error

>         Near line: ".1fmt.pp.Width:"

>         "/var/tmp//ccbyjwCc.s", line 1062 : Syntax error

>         Near line: ".1fmt.pp.Width:"

>         "/var/tmp//ccbyjwCc.s", line 1082 : Syntax error

>         Near line: "    .size   .1fmt.pp.Width, .-.1fmt.pp.Width"

>         "/var/tmp//ccbyjwCc.s", line 1084 : Syntax error

>         Near line: "    .globl  .1fmt.pp.Precision"

>         "/var/tmp//ccbyjwCc.s", line 1085 : Syntax error

>         Near line: "    .type   .1fmt.pp.Precision, @function"

>         "/var/tmp//ccbyjwCc.s", line 1086 : Illegal mnemonic

>         Near line: ".1fmt.pp.Precision:"

>         "/var/tmp//ccbyjwCc.s", line 1086 : Syntax error

>         Near line: ".1fmt.pp.Precision:"

>         "/var/tmp//ccbyjwCc.s", line 1086 : Syntax error

>         Near line: ".1fmt.pp.Precision:"

>         "/var/tmp//ccbyjwCc.s", line 1106 : Syntax error

>         Near line: "    .size   .1fmt.pp.Precision, .-.1fmt.pp.Precision"

>         "/var/tmp//ccbyjwCc.s", line 1108 : Syntax error

>         Near line: "    .globl  .1fmt.ss.Width"

>         "/var/tmp//ccbyjwCc.s", line 1109 : Syntax error

>         Near line: "    .type   .1fmt.ss.Width, @function"

>         "/var/tmp//ccbyjwCc.s", line 1110 : Illegal mnemonic

>         Near line: ".1fmt.ss.Width:"

>         "/var/tmp//ccbyjwCc.s", line 1110 : Syntax error

>         Near line: ".1fmt.ss.Width:"

>         "/var/tmp//ccbyjwCc.s", line 1110 : Syntax error

>         Near line: ".1fmt.ss.Width:"

>         "/var/tmp//ccbyjwCc.s", line 1149 : Syntax error

>         Near line: "    .size   .1fmt.ss.Width, .-.1fmt.ss.Width"

>         "/var/tmp//ccbyjwCc.s", line 1151 : Syntax error

>         Near line: "    .globl  .1fmt.ss.UnreadRune"

>         "/var/tmp//ccbyjwCc.s", line 1152 : Syntax error

>         Near line: "    .type   .1fmt.ss.UnreadRune, @function"

>         "/var/tmp//ccbyjwCc.s", line 1153 : Illegal mnemonic

>         Near line: ".1fmt.ss.UnreadRune:"

>         "/var/tmp//ccbyjwCc.s", line 1153 : Syntax error

>         Near line: ".1fmt.ss.UnreadRune:"

>         "/var/tmp//ccbyjwCc.s", line 1153 : Syntax error

>         Near line: ".1fmt.ss.UnreadRune:"

>         "/var/tmp//ccbyjwCc.s", line 1209 : Syntax error

>         Near line: "    .size   .1fmt.ss.UnreadRune, .-.1fmt.ss.UnreadRune"

>         "/var/tmp//ccbyjwCc.s", line 1908 : Syntax error

>         Near line: "    .globl  .1fmt.fmt.init"

> Too many errors - Goodbye

> make[4]: *** [Makefile:3322: fmt.lo] Error 1

>

> Solaris/SPARC with /bin/as is fine, but that's not too astonishing since

> both assemblers are mostly different code bases.


From the error messages I guess the problem is that the assembler
doesn't like symbols that start with ".1".  Do you know what names the
assembler permits?

Ian
Jakub Jelinek Jan. 25, 2018, 8:46 p.m. | #3
On Thu, Jan 25, 2018 at 12:40:13PM -0800, Ian Lance Taylor wrote:
> >> Bootstrapped and ran Go testsuite on x86_64-pc-linux-gnu.  Committed

> >> to mainline.

> >

> > this patch almost certainly (i.e. I didn't reghunt, but it's the only

> > plausible candidate between r257023 and r257057) Solaris/x86 bootstrap

> > with /bin/as:

> >

> > Assembler: doc.go

> >         "/var/tmp//ccbyjwCc.s", line 1043 : Syntax error

> >         Near line: "    .globl  .1fmt.fmt.clearflags"

> >         "/var/tmp//ccbyjwCc.s", line 1044 : Syntax error

> >         Near line: "    .type   .1fmt.fmt.clearflags, @function"

> >         "/var/tmp//ccbyjwCc.s", line 1045 : Illegal mnemonic

> >         Near line: ".1fmt.fmt.clearflags:"

> >         "/var/tmp//ccbyjwCc.s", line 1045 : Syntax error

> >         Near line: ".1fmt.fmt.clearflags:"

> >         "/var/tmp//ccbyjwCc.s", line 1045 : Syntax error

> >         Near line: ".1fmt.fmt.clearflags:"

> >         "/var/tmp//ccbyjwCc.s", line 1058 : Syntax error

> >         Near line: "    .size   .1fmt.fmt.clearflags, .-.1fmt.fmt.clearflags"

> >         "/var/tmp//ccbyjwCc.s", line 1060 : Syntax error

> >         Near line: "    .globl  .1fmt.pp.Width"

> >         "/var/tmp//ccbyjwCc.s", line 1061 : Syntax error

> >         Near line: "    .type   .1fmt.pp.Width, @function"

> >         "/var/tmp//ccbyjwCc.s", line 1062 : Illegal mnemonic

> >         Near line: ".1fmt.pp.Width:"

> >         "/var/tmp//ccbyjwCc.s", line 1062 : Syntax error

> >         Near line: ".1fmt.pp.Width:"

> >         "/var/tmp//ccbyjwCc.s", line 1062 : Syntax error

> >         Near line: ".1fmt.pp.Width:"

> >         "/var/tmp//ccbyjwCc.s", line 1082 : Syntax error

> >         Near line: "    .size   .1fmt.pp.Width, .-.1fmt.pp.Width"

> >         "/var/tmp//ccbyjwCc.s", line 1084 : Syntax error

> >         Near line: "    .globl  .1fmt.pp.Precision"

> >         "/var/tmp//ccbyjwCc.s", line 1085 : Syntax error

> >         Near line: "    .type   .1fmt.pp.Precision, @function"

> >         "/var/tmp//ccbyjwCc.s", line 1086 : Illegal mnemonic

> >         Near line: ".1fmt.pp.Precision:"

> >         "/var/tmp//ccbyjwCc.s", line 1086 : Syntax error

> >         Near line: ".1fmt.pp.Precision:"

> >         "/var/tmp//ccbyjwCc.s", line 1086 : Syntax error

> >         Near line: ".1fmt.pp.Precision:"

> >         "/var/tmp//ccbyjwCc.s", line 1106 : Syntax error

> >         Near line: "    .size   .1fmt.pp.Precision, .-.1fmt.pp.Precision"

> >         "/var/tmp//ccbyjwCc.s", line 1108 : Syntax error

> >         Near line: "    .globl  .1fmt.ss.Width"

> >         "/var/tmp//ccbyjwCc.s", line 1109 : Syntax error

> >         Near line: "    .type   .1fmt.ss.Width, @function"

> >         "/var/tmp//ccbyjwCc.s", line 1110 : Illegal mnemonic

> >         Near line: ".1fmt.ss.Width:"

> >         "/var/tmp//ccbyjwCc.s", line 1110 : Syntax error

> >         Near line: ".1fmt.ss.Width:"

> >         "/var/tmp//ccbyjwCc.s", line 1110 : Syntax error

> >         Near line: ".1fmt.ss.Width:"

> >         "/var/tmp//ccbyjwCc.s", line 1149 : Syntax error

> >         Near line: "    .size   .1fmt.ss.Width, .-.1fmt.ss.Width"

> >         "/var/tmp//ccbyjwCc.s", line 1151 : Syntax error

> >         Near line: "    .globl  .1fmt.ss.UnreadRune"

> >         "/var/tmp//ccbyjwCc.s", line 1152 : Syntax error

> >         Near line: "    .type   .1fmt.ss.UnreadRune, @function"

> >         "/var/tmp//ccbyjwCc.s", line 1153 : Illegal mnemonic

> >         Near line: ".1fmt.ss.UnreadRune:"

> >         "/var/tmp//ccbyjwCc.s", line 1153 : Syntax error

> >         Near line: ".1fmt.ss.UnreadRune:"

> >         "/var/tmp//ccbyjwCc.s", line 1153 : Syntax error

> >         Near line: ".1fmt.ss.UnreadRune:"

> >         "/var/tmp//ccbyjwCc.s", line 1209 : Syntax error

> >         Near line: "    .size   .1fmt.ss.UnreadRune, .-.1fmt.ss.UnreadRune"

> >         "/var/tmp//ccbyjwCc.s", line 1908 : Syntax error

> >         Near line: "    .globl  .1fmt.fmt.init"

> > Too many errors - Goodbye

> > make[4]: *** [Makefile:3322: fmt.lo] Error 1

> >

> > Solaris/SPARC with /bin/as is fine, but that's not too astonishing since

> > both assemblers are mostly different code bases.

> 

> >From the error messages I guess the problem is that the assembler

> doesn't like symbols that start with ".1".  Do you know what names the

> assembler permits?


It isn't just Solaris/SPARC, I saw bootstrap failure with go on
powerpc64-linux and powerpc64le-linux too, similar errors.
/tmp/cc9hDRO7.s: Assembler messages:
/tmp/cc9hDRO7.s:128: Error: symbol `.1errors.errorString.Error' is already defined
/tmp/cc9hDRO7.s: Error: .size expression for .1errors.errorString.Error does not evaluate to a constant

/home/jakub/gcc/obj22/gotools/../../gotools/../libgo/go/cmd/vet/unsafeptr.go:60: undefined reference to `.1go_types.Package.Path'
/home/jakub/gcc/obj22/gotools/../../gotools/../libgo/go/cmd/vet/unsafeptr.go:61: undefined reference to `.1go_types.Named.Obj'
/home/jakub/gcc/obj22/gotools/../../gotools/../libgo/go/cmd/vet/unsafeptr.go:61: undefined reference to `.1go_types.TypeName.Name..stub'
/home/jakub/gcc/obj22/gotools/../../gotools/../libgo/go/cmd/vet/unsafeptr.go:80: undefined reference to `.1go_types.Named.Obj'
/usr/bin/ld: /home/jakub/gcc/obj22/gotools/../../gotools/../libgo/go/cmd/vet/unsafeptr.go:80:(.text+0x2bec): unresolvable R_PPC64_REL24 against `.1
go_types.TypeName.Pkg..stub'

etc.

	Jakub
Rainer Orth Jan. 25, 2018, 8:50 p.m. | #4
Hi Ian,

> From the error messages I guess the problem is that the assembler

> doesn't like symbols that start with ".1".  Do you know what names the

> assembler permits?


The x86 Assembly Language Reference Manual states:

2.1.2.1 Identifiers

An identifier is an arbitrarily-long sequence of letters and digits. The
first character must be a letter; the underscore (_) (ASCII 0x5F) and
the period (.) (ASCII 0x2E) are considered to be letters. Case is
significant: uppercase and lowercase letters are different.

Contrary to that, /bin/as won't assemble

.globl .1

The SPARC Assembly Language Reference Manual states this instead:

1.3.6 Symbol Names

The syntax for a symbol name is:

{ letter | _ | $ | . }   { letter | _ | $ | . | digit }* 

In the above syntax:

* Uppercase and lowercase letters are distinct; the underscore ( _ ),
  dollar sign ($), and dot ( . ) are treated as alphabetic characters.

* Symbol names that begin with a dot ( . ) are assumed to be local
  symbols. To simplify debugging, avoid using this type of symbol name
  in hand-coded assembly language routines.

* The symbol dot ( . ) is predefined and always refers to the address of
  the beginning of the current assembly language statement.

* External variable names beginning with the underscore character are
  reserved by the ANSI C Standard. Do not begin these names with the
  underscore; otherwise, the program will not conform to ANSI C and
  unpredictable behavior may result.

	Rainer

-- 
-----------------------------------------------------------------------------
Rainer Orth, Center for Biotechnology, Bielefeld University
Ian Lance Taylor Jan. 25, 2018, 11:10 p.m. | #5
On Thu, Jan 25, 2018 at 12:50 PM, Rainer Orth
<ro@cebitec.uni-bielefeld.de> wrote:
>

>> From the error messages I guess the problem is that the assembler

>> doesn't like symbols that start with ".1".  Do you know what names the

>> assembler permits?

>

> The x86 Assembly Language Reference Manual states:


Thanks.  Looking back, it was actually not my plan to have symbols
that start with '.'.  It was a bug, fixed by this patch.  Bootstrapped
and ran Go testsuite on x86_64-pc-linux-gnu.  Committed to mainline.

Ian
Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 257061)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -1,4 +1,4 @@
-553e04735d1be372c596c720bcaea27e050b13a6
+203cbe7d3820fa03c965a01f72461f71588fe952
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
Index: gcc/go/gofrontend/go-encode-id.cc
===================================================================
--- gcc/go/gofrontend/go-encode-id.cc	(revision 257033)
+++ gcc/go/gofrontend/go-encode-id.cc	(working copy)
@@ -104,6 +104,14 @@ go_encode_id(const std::string &id)
   std::string ret;
   const char* p = id.c_str();
   const char* pend = p + id.length();
+
+  // A leading ".0" is a space introduced before a mangled type name
+  // that starts with a 'u' or 'U', to avoid confusion with the
+  // mangling used here.  We don't need a leading ".0", and we don't
+  // want symbols that start with '.', so remove it.
+  if (p[0] == '.' && p[1] == '0')
+    p += 2;
+
   while (p < pend)
     {
       unsigned int c;
@@ -115,16 +123,19 @@ go_encode_id(const std::string &id)
 	  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
 	{
 	  char buf[16];
-	  snprintf(buf, sizeof buf, "..U%08x", c);
+	  if (c < 0x10000)
+	    snprintf(buf, sizeof buf, "..u%04x", c);
+	  else
+	    snprintf(buf, sizeof buf, "..U%08x", c);
+
+	  // We don't want a symbol to start with '.', so add a prefix
+	  // if needed.
+	  if (ret.empty())
+	    ret += '_';
+
 	  ret += buf;
 	}
       p += len;
Index: gcc/go/gofrontend/names.cc
===================================================================
--- gcc/go/gofrontend/names.cc	(revision 257033)
+++ gcc/go/gofrontend/names.cc	(working copy)
@@ -213,7 +213,7 @@ Gogo::function_asm_name(const std::strin
 {
   std::string ret;
   if (rtype != NULL)
-    ret = rtype->mangled_name(this);
+    ret = rtype->deref()->mangled_name(this);
   else if (package == NULL)
     ret = this->pkgpath_symbol();
   else
@@ -892,14 +892,7 @@ Named_type::append_mangled_type_name(Gog
 	  const Typed_identifier* rcvr =
 	    this->in_function_->func_value()->type()->receiver();
 	  if (rcvr != NULL)
-	    {
-	      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);
-	    }
+	    ret->append(rcvr->type()->deref()->mangled_name(gogo));
 	  else if (this->in_function_->package() == NULL)
 	    ret->append(gogo->pkgpath_symbol());
 	  else
@@ -956,7 +949,7 @@ Gogo::type_descriptor_name(Type* type, N
 	  const Typed_identifier* rcvr =
 	    in_function->func_value()->type()->receiver();
 	  if (rcvr != NULL)
-	    ret.append(rcvr->type()->mangled_name(this));
+	    ret.append(rcvr->type()->deref()->mangled_name(this));
 	  else if (in_function->package() == NULL)
 	    ret.append(this->pkgpath_symbol());
 	  else
Index: libgo/testsuite/gotest
===================================================================
--- libgo/testsuite/gotest	(revision 256593)
+++ libgo/testsuite/gotest	(working copy)
@@ -518,7 +518,7 @@ localname() {
 	pattern='Test([^a-z].*)?'
 	# The -p option tells GNU nm not to sort.
 	# The -v option tells Solaris nm to sort by value.
-	tests=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | grep -v '\..*\..*\.' | fgrep -v '$' | fgrep -v ' __go_' | sed 's/.* //' | $symtogo)
+	tests=$($NM -p -v _gotest_.o $xofile | egrep " $text .*\."$pattern'$' | grep -v '\..*\.' | fgrep -v '$' | fgrep -v ' __go_' | sed 's/.* //' | $symtogo)
 	if [ "x$tests" = x ]; then
 		echo 'gotest: warning: no tests matching '$pattern in _gotest_.o $xofile 1>&2
 		exit 2
Index: libgo/go/runtime/pprof/pprof_test.go
===================================================================
--- libgo/go/runtime/pprof/pprof_test.go	(revision 257033)
+++ libgo/go/runtime/pprof/pprof_test.go	(working copy)
@@ -730,7 +730,7 @@ func TestMutexProfile(t *testing.T) {
 		stks := stacks(p)
 		for _, want := range [][]string{
 			// {"sync.(*Mutex).Unlock", "pprof.blockMutex.func1"},
-			{".1sync.Mutex.Unlock", "pprof.blockMutex..func1"},
+			{"sync.Mutex.Unlock", "pprof.blockMutex..func1"},
 		} {
 			if !containsStack(stks, want) {
 				t.Errorf("No matching stack entry for %+v", want)

Patch

Index: gcc/go/gofrontend/MERGE
===================================================================
--- gcc/go/gofrontend/MERGE	(revision 256971)
+++ gcc/go/gofrontend/MERGE	(working copy)
@@ -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.
Index: gcc/go/gofrontend/escape.cc
===================================================================
--- gcc/go/gofrontend/escape.cc	(revision 256835)
+++ gcc/go/gofrontend/escape.cc	(working copy)
@@ -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;
-
-              case Type::TYPE_POINTER:
-                {
-                  Named_type* nt = rt->points_to()->named_type();
-                  if (nt != NULL)
-                    fnname = "(*" + nt->name() + ")." + fnname;
-                  break;
-                }
-
-              default:
-                break;
-            }
-        }
-      return fnname;
+      // 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;
+	  }
+
+	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.
Index: gcc/go/gofrontend/expressions.cc
===================================================================
--- gcc/go/gofrontend/expressions.cc	(revision 256835)
+++ gcc/go/gofrontend/expressions.cc	(working copy)
@@ -1310,6 +1310,16 @@  Func_descriptor_expression::do_get_backe
       && 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(Go
 
   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(Go
   // 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(Go
 
   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
   // 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::cr
 
   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
   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_expres
       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);
Index: gcc/go/gofrontend/go-encode-id.cc
===================================================================
--- gcc/go/gofrontend/go-encode-id.cc	(revision 256593)
+++ gcc/go/gofrontend/go-encode-id.cc	(working copy)
@@ -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
   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::stri
     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;
+}
Index: gcc/go/gofrontend/go-encode-id.h
===================================================================
--- gcc/go/gofrontend/go-encode-id.h	(revision 256593)
+++ gcc/go/gofrontend/go-encode-id.h	(working copy)
@@ -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)
Index: gcc/go/gofrontend/gogo.cc
===================================================================
--- gcc/go/gofrontend/gogo.cc	(revision 256706)
+++ gcc/go/gofrontend/gogo.cc	(working copy)
@@ -1758,7 +1758,7 @@  Gogo::start_function(const std::string&
   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,
   : 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;
Index: gcc/go/gofrontend/gogo.h
===================================================================
--- gcc/go/gofrontend/gogo.h	(revision 256707)
+++ gcc/go/gofrontend/gogo.h	(working copy)
@@ -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.
Index: gcc/go/gofrontend/lex.cc
===================================================================
--- gcc/go/gofrontend/lex.cc	(revision 256593)
+++ gcc/go/gofrontend/lex.cc	(working copy)
@@ -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))
Index: gcc/go/gofrontend/names.cc
===================================================================
--- gcc/go/gofrontend/names.cc	(revision 256593)
+++ gcc/go/gofrontend/names.cc	(working copy)
@@ -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::strin
 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
 				   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
 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::str
 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::stri
 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
 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<unsigned int>(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,
 	    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<unsigned int>(*p));
-		      out.append(buf);
-		    }
-		}
-	      char buf[20];
-	      snprintf(buf, sizeof buf, "T%u_",
-		       static_cast<unsigned int>(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,
       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* go
 {
   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<unsigned int>(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_nam
   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<unsigned int>(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(Gog
     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<unsigned int>(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, N
 {
   // 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, N
 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);
+}
Index: gcc/go/gofrontend/statements.cc
===================================================================
--- gcc/go/gofrontend/statements.cc	(revision 256593)
+++ gcc/go/gofrontend/statements.cc	(working copy)
@@ -2120,7 +2120,7 @@  Thunk_statement::simplify_statement(Gogo
       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);
Index: gcc/go/gofrontend/types.cc
===================================================================
--- gcc/go/gofrontend/types.cc	(revision 256593)
+++ gcc/go/gofrontend/types.cc	(working copy)
@@ -2654,14 +2654,14 @@  Ptrmask::set_from(Gogo* gogo, Type* type
 // 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 Functi
     }
 
   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 Functi
     }
 
   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, con
 	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,
Index: libgo/go/runtime/crash_test.go
===================================================================
--- libgo/go/runtime/crash_test.go	(revision 256593)
+++ libgo/go/runtime/crash_test.go	(working copy)
@@ -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)
Index: libgo/go/runtime/panic.go
===================================================================
--- libgo/go/runtime/panic.go	(revision 256593)
+++ libgo/go/runtime/panic.go	(working copy)
@@ -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
 		}
 
Index: libgo/go/runtime/pprof/pprof_test.go
===================================================================
--- libgo/go/runtime/pprof/pprof_test.go	(revision 256593)
+++ libgo/go/runtime/pprof/pprof_test.go	(working copy)
@@ -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)
Index: libgo/go/syscall/wait.c
===================================================================
--- libgo/go/syscall/wait.c	(revision 256593)
+++ libgo/go/syscall/wait.c	(working copy)
@@ -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)))
Index: libgo/runtime/go-callers.c
===================================================================
--- libgo/runtime/go-callers.c	(revision 256593)
+++ libgo/runtime/go-callers.c	(working copy)
@@ -68,13 +68,14 @@  callback (void *data, uintptr_t pc, cons
     {
       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;
     }
 
Index: libgo/runtime/go-unsafe-pointer.c
===================================================================
--- libgo/runtime/go-unsafe-pointer.c	(revision 256593)
+++ libgo/runtime/go-unsafe-pointer.c	(working copy)
@@ -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
    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 };
 
Index: gcc/testsuite/go.go-torture/execute/names-1.go
===================================================================
--- gcc/testsuite/go.go-torture/execute/names-1.go	(nonexistent)
+++ gcc/testsuite/go.go-torture/execute/names-1.go	(working copy)
@@ -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")
+	}
+}