From a3dfcf51c6543ac1af853f1799d70eae83073f1a Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Fri, 1 May 2015 19:50:27 -0700 Subject: [PATCH 001/232] cmd/internal/gc: unsafe.Pointer constants may only be converted to uintptr Fixes #8927. Change-Id: I638cddd439dd2d4eeef5474118cfcbde0c8a5a43 Reviewed-on: https://go-review.googlesource.com/9632 Run-TryBot: David Chase Reviewed-by: David Chase --- src/cmd/internal/gc/const.go | 10 ++++++++-- test/convlit.go | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/cmd/internal/gc/const.go b/src/cmd/internal/gc/const.go index ad2915812e..5ec54bdffb 100644 --- a/src/cmd/internal/gc/const.go +++ b/src/cmd/internal/gc/const.go @@ -204,6 +204,9 @@ func convlit1(np **Node, t *Type, explicit bool) { } case CTINT, CTRUNE, CTFLT, CTCPLX: + if n.Type.Etype == TUNSAFEPTR && t.Etype != TUINTPTR { + goto bad + } ct := int(n.Val.Ctype) if Isint[et] { switch ct { @@ -264,8 +267,6 @@ bad: defaultlit(&n, nil) *np = n } - - return } func copyval(v Val) Val { @@ -396,6 +397,11 @@ func overflow(v Val, t *Type) { return } + // Only uintptrs may be converted to unsafe.Pointer, which cannot overflow. + if t.Etype == TUNSAFEPTR { + return + } + if !doesoverflow(v, t) { return } diff --git a/test/convlit.go b/test/convlit.go index 8a6145d2a0..904e1e63b1 100644 --- a/test/convlit.go +++ b/test/convlit.go @@ -9,6 +9,8 @@ package main +import "unsafe" + // explicit conversion of constants var x1 = string(1) var x2 string = string(1) @@ -18,6 +20,11 @@ var x5 = "a" + string(1) var x6 = int(1e100) // ERROR "overflow" var x7 = float32(1e1000) // ERROR "overflow" +// unsafe.Pointer can only convert to/from uintptr +var _ = string(unsafe.Pointer(uintptr(65))) // ERROR "convert" +var _ = float64(unsafe.Pointer(uintptr(65))) // ERROR "convert" +var _ = int(unsafe.Pointer(uintptr(65))) // ERROR "convert" + // implicit conversions merit scrutiny var s string var bad1 string = 1 // ERROR "conver|incompatible|invalid|cannot" From fd392ee52b984e655390ad9147c9fe95e82bc459 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Thu, 7 May 2015 00:48:09 -0400 Subject: [PATCH 002/232] cmd/internal/ld: generate correct .debug_frames on RISC architectures With this patch, gdb seems to be able to corretly backtrace Go process on at least linux/{arm,arm64,ppc64}. Change-Id: Ic40a2a70e71a19c4a92e4655710f38a807b67e9a Reviewed-on: https://go-review.googlesource.com/9822 Run-TryBot: Minux Ma TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor --- src/cmd/5l/l.go | 3 ++- src/cmd/5l/obj.go | 1 + src/cmd/6l/l.go | 3 ++- src/cmd/6l/obj.go | 1 + src/cmd/7l/l.go | 3 ++- src/cmd/7l/obj.go | 1 + src/cmd/8l/l.go | 3 ++- src/cmd/8l/obj.go | 1 + src/cmd/9l/l.go | 3 ++- src/cmd/9l/obj.go | 1 + src/cmd/internal/ld/dwarf.go | 44 +++++++++++++++++++++++---------- src/cmd/internal/ld/lib.go | 1 + src/runtime/runtime-gdb_test.go | 2 +- 13 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/cmd/5l/l.go b/src/cmd/5l/l.go index a52154594d..adc8d286ae 100644 --- a/src/cmd/5l/l.go +++ b/src/cmd/5l/l.go @@ -72,7 +72,8 @@ const ( MINLC = 4 ) -/* Used by ../ld/dwarf.c */ +/* Used by ../internal/ld/dwarf.go */ const ( DWARFREGSP = 13 + DWARFREGLR = 14 ) diff --git a/src/cmd/5l/obj.go b/src/cmd/5l/obj.go index fa74908005..e4fffdec6a 100644 --- a/src/cmd/5l/obj.go +++ b/src/cmd/5l/obj.go @@ -56,6 +56,7 @@ func linkarchinit() { ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR ld.Thearch.Adddynlib = adddynlib ld.Thearch.Adddynrel = adddynrel diff --git a/src/cmd/6l/l.go b/src/cmd/6l/l.go index 6b42088de3..64466d126a 100644 --- a/src/cmd/6l/l.go +++ b/src/cmd/6l/l.go @@ -40,7 +40,8 @@ const ( MINLC = 1 ) -/* Used by ../ld/dwarf.c */ +/* Used by ../internal/ld/dwarf.go */ const ( DWARFREGSP = 7 + DWARFREGLR = 16 ) diff --git a/src/cmd/6l/obj.go b/src/cmd/6l/obj.go index 9e6dc60e2d..8ee7bb28db 100644 --- a/src/cmd/6l/obj.go +++ b/src/cmd/6l/obj.go @@ -59,6 +59,7 @@ func linkarchinit() { ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR ld.Thearch.Adddynlib = adddynlib ld.Thearch.Adddynrel = adddynrel diff --git a/src/cmd/7l/l.go b/src/cmd/7l/l.go index 6f90acb107..7227cc430f 100644 --- a/src/cmd/7l/l.go +++ b/src/cmd/7l/l.go @@ -71,7 +71,8 @@ const ( MINLC = 4 ) -/* Used by ../ld/dwarf.c */ +/* Used by ../internal/ld/dwarf.go */ const ( DWARFREGSP = 31 + DWARFREGLR = 30 ) diff --git a/src/cmd/7l/obj.go b/src/cmd/7l/obj.go index f8ac7d33ea..aeea421bc2 100644 --- a/src/cmd/7l/obj.go +++ b/src/cmd/7l/obj.go @@ -56,6 +56,7 @@ func linkarchinit() { ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR ld.Thearch.Adddynlib = adddynlib ld.Thearch.Adddynrel = adddynrel diff --git a/src/cmd/8l/l.go b/src/cmd/8l/l.go index 60050857c4..5cb9f8d8af 100644 --- a/src/cmd/8l/l.go +++ b/src/cmd/8l/l.go @@ -40,7 +40,8 @@ const ( MINLC = 1 ) -/* Used by ../ld/dwarf.c */ +/* Used by ../internal/ld/dwarf.go */ const ( DWARFREGSP = 4 + DWARFREGLR = 8 ) diff --git a/src/cmd/8l/obj.go b/src/cmd/8l/obj.go index 7b490ae87c..5af3f9249b 100644 --- a/src/cmd/8l/obj.go +++ b/src/cmd/8l/obj.go @@ -56,6 +56,7 @@ func linkarchinit() { ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR ld.Thearch.Adddynlib = adddynlib ld.Thearch.Adddynrel = adddynrel diff --git a/src/cmd/9l/l.go b/src/cmd/9l/l.go index e7dc102af2..8723eaeca4 100644 --- a/src/cmd/9l/l.go +++ b/src/cmd/9l/l.go @@ -71,7 +71,8 @@ const ( MINLC = 4 ) -/* Used by ../ld/dwarf.c */ +/* Used by ../internal/ld/dwarf.go */ const ( DWARFREGSP = 1 + DWARFREGLR = 65 ) diff --git a/src/cmd/9l/obj.go b/src/cmd/9l/obj.go index 46a92396e4..2da37561e9 100644 --- a/src/cmd/9l/obj.go +++ b/src/cmd/9l/obj.go @@ -60,6 +60,7 @@ func linkarchinit() { ld.Thearch.Maxalign = MaxAlign ld.Thearch.Minlc = MINLC ld.Thearch.Dwarfregsp = DWARFREGSP + ld.Thearch.Dwarfreglr = DWARFREGLR ld.Thearch.Adddynlib = adddynlib ld.Thearch.Adddynrel = adddynrel diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/internal/ld/dwarf.go index 6d90404b13..476b329e7a 100644 --- a/src/cmd/internal/ld/dwarf.go +++ b/src/cmd/internal/ld/dwarf.go @@ -1692,11 +1692,17 @@ func writelines() { switch a.Name { case obj.A_AUTO: dt = DW_ABRV_AUTO - offs = int64(a.Aoffset) - int64(Thearch.Ptrsize) + offs = int64(a.Aoffset) + if !haslinkregister() { + offs -= int64(Thearch.Ptrsize) + } case obj.A_PARAM: dt = DW_ABRV_PARAM offs = int64(a.Aoffset) + if haslinkregister() { + offs += int64(Thearch.Ptrsize) + } default: continue @@ -1749,7 +1755,6 @@ func writelines() { const ( CIERESERVE = 16 DATAALIGNMENTFACTOR = -4 - FAKERETURNCOLUMN = 16 // TODO gdb6 doesn't like > 15? ) func putpccfadelta(deltapc int64, cfa int64) { @@ -1778,21 +1783,30 @@ func writeframes() { frameo = Cpos() // Emit the CIE, Section 6.4.1 - Thearch.Lput(CIERESERVE) // initial length, must be multiple of thearch.ptrsize - Thearch.Lput(0xffffffff) // cid. - Cput(3) // dwarf version (appendix F) - Cput(0) // augmentation "" - uleb128put(1) // code_alignment_factor - sleb128put(DATAALIGNMENTFACTOR) // guess - uleb128put(FAKERETURNCOLUMN) // return_address_register + Thearch.Lput(CIERESERVE) // initial length, must be multiple of thearch.ptrsize + Thearch.Lput(0xffffffff) // cid. + Cput(3) // dwarf version (appendix F) + Cput(0) // augmentation "" + uleb128put(1) // code_alignment_factor + sleb128put(DATAALIGNMENTFACTOR) // guess + uleb128put(int64(Thearch.Dwarfreglr)) // return_address_register Cput(DW_CFA_def_cfa) uleb128put(int64(Thearch.Dwarfregsp)) // register SP (**ABI-dependent, defined in l.h) - uleb128put(int64(Thearch.Ptrsize)) // offset + if haslinkregister() { + uleb128put(int64(0)) // offset + } else { + uleb128put(int64(Thearch.Ptrsize)) // offset + } - Cput(DW_CFA_offset + FAKERETURNCOLUMN) // return address - uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4 + Cput(DW_CFA_offset_extended) + uleb128put(int64(Thearch.Dwarfreglr)) // return address + if haslinkregister() { + uleb128put(int64(0) / DATAALIGNMENTFACTOR) // at cfa - 0 + } else { + uleb128put(int64(-Thearch.Ptrsize) / DATAALIGNMENTFACTOR) // at cfa - x*4 + } // 4 is to exclude the length field. pad := CIERESERVE + frameo + 4 - Cpos() @@ -1834,7 +1848,11 @@ func writeframes() { } } - putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value)) + if haslinkregister() { + putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(pcsp.value)) + } else { + putpccfadelta(int64(nextpc)-int64(pcsp.pc), int64(Thearch.Ptrsize)+int64(pcsp.value)) + } } fdesize = Cpos() - fdeo - 4 // exclude the length field. diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go index 184175e026..edafaebb0b 100644 --- a/src/cmd/internal/ld/lib.go +++ b/src/cmd/internal/ld/lib.go @@ -86,6 +86,7 @@ type Arch struct { Maxalign int Minlc int Dwarfregsp int + Dwarfreglr int Linuxdynld string Freebsddynld string Netbsddynld string diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index fe7d38a39c..8d04f6328c 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -85,7 +85,7 @@ func TestGdbPython(t *testing.T) { // stack frames on RISC architectures. canBackTrace := false switch runtime.GOARCH { - case "amd64", "386": + case "amd64", "386", "ppc64", "ppc64le", "arm", "arm64": canBackTrace = true args = append(args, "-ex", "echo BEGIN goroutine 2 bt\n", From e8fc93ea45ca0147f24f61f5ed48e68b57d473df Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 7 May 2015 12:58:43 -0700 Subject: [PATCH 003/232] cmd/cgo: wrap generated exports with extern "C" for C++ This will make it possible for C++ code to #include the export header file and see the correct declarations. The preamble remains the user's responsibility. It would not be appropriate to wrap the preamble in extern "C", because it might include header files that work with both C and C++. Putting those header files in an extern "C" block would break them. Change-Id: Ifb40879d709d26596d5c80b1307a49f1bd70932a Reviewed-on: https://go-review.googlesource.com/9850 Reviewed-by: Minux Ma Run-TryBot: Ian Lance Taylor Reviewed-by: David Crawshaw TryBot-Result: Gobot Gobot --- src/cmd/cgo/out.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go index 30f828c4e9..87f21ed822 100644 --- a/src/cmd/cgo/out.go +++ b/src/cmd/cgo/out.go @@ -846,6 +846,8 @@ func (p *Package) writeExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprint(fgo2, "}\n") } } + + fmt.Fprintf(fgcch, "%s", gccExportHeaderEpilog) } // Write out the C header allowing C code to call exported gccgo functions. @@ -1009,6 +1011,8 @@ func (p *Package) writeGccgoExports(fgo2, fm, fgcc, fgcch io.Writer) { fmt.Fprint(fgo2, ")\n") fmt.Fprint(fgo2, "}\n") } + + fmt.Fprintf(fgcch, "%s", gccExportHeaderEpilog) } // writeExportHeader writes out the start of the _cgo_export.h file. @@ -1374,6 +1378,17 @@ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; #endif /* End of boilerplate cgo prologue. */ + +#ifdef __cplusplus +extern "C" { +#endif +` + +// gccExportHeaderEpilog goes at the end of the generated header file. +const gccExportHeaderEpilog = ` +#ifdef __cplusplus +} +#endif ` // gccgoExportFileProlog is written to the _cgo_export.c file when From 3a3773c8cb439034094025cf2f85ed52535c3e1f Mon Sep 17 00:00:00 2001 From: Brett Cannon Date: Fri, 8 May 2015 10:41:15 -0400 Subject: [PATCH 004/232] doc/effective_go: make function signature match function body In the Slices section of Effective Go, the os package's File.Read function is used as an example. Unfortunately the function signature does not match the function's code in the example, nor the os package's documentation. This change updates the function signature to match the os package and the pre-existing function code. Change-Id: Iae9f30c898d3a1ff8d47558ca104dfb3ff07112c Reviewed-on: https://go-review.googlesource.com/9845 Reviewed-by: Rob Pike --- doc/effective_go.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/effective_go.html b/doc/effective_go.html index d6be37994b..8a827d0433 100644 --- a/doc/effective_go.html +++ b/doc/effective_go.html @@ -1382,7 +1382,7 @@ limit of how much data to read. Here is the signature of the os:

-func (file *File) Read(buf []byte) (n int, err error)
+func (f *File) Read(buf []byte) (n int, err error)
 

The method returns the number of bytes read and an error value, if From 91d989eb6df17b0696cfd53e84b10ccb3f09c1dd Mon Sep 17 00:00:00 2001 From: Didier Spezia Date: Wed, 6 May 2015 22:14:32 +0000 Subject: [PATCH 005/232] html/template: fix pipeline sanitization Pipelines are altered by inserting sanitizers if they are not already present. The code makes the assumption that the first operands of each commands are function identifiers. This is wrong, since they can also be methods. It results in a panic with templates such as {{1|print 2|.f 3}} Adds an extra type assertion to make sure only identifiers are compared with sanitizers. Fixes #10673 Change-Id: I3eb820982675231dbfa970f197abc5ef335ce86b Reviewed-on: https://go-review.googlesource.com/9801 Reviewed-by: Rob Pike --- src/html/template/escape.go | 6 +++--- src/html/template/escape_test.go | 10 ++++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/html/template/escape.go b/src/html/template/escape.go index ee01fb12ab..a9529446dd 100644 --- a/src/html/template/escape.go +++ b/src/html/template/escape.go @@ -297,9 +297,9 @@ var redundantFuncs = map[string]map[string]bool{ // unless it is redundant with the last command. func appendCmd(cmds []*parse.CommandNode, cmd *parse.CommandNode) []*parse.CommandNode { if n := len(cmds); n != 0 { - last, ok := cmds[n-1].Args[0].(*parse.IdentifierNode) - next, _ := cmd.Args[0].(*parse.IdentifierNode) - if ok && redundantFuncs[last.Ident][next.Ident] { + last, okLast := cmds[n-1].Args[0].(*parse.IdentifierNode) + next, okNext := cmd.Args[0].(*parse.IdentifierNode) + if okLast && okNext && redundantFuncs[last.Ident][next.Ident] { return cmds } } diff --git a/src/html/template/escape_test.go b/src/html/template/escape_test.go index 9c9502a617..6729ebf4a7 100644 --- a/src/html/template/escape_test.go +++ b/src/html/template/escape_test.go @@ -1547,6 +1547,16 @@ func TestEnsurePipelineContains(t *testing.T) { "($).X | urlquery | html | print", []string{"urlquery", "html"}, }, + { + "{{.X | print 2 | .f 3}}", + ".X | print 2 | .f 3 | urlquery | html", + []string{"urlquery", "html"}, + }, + { + "{{.X | html | print 2 | .f 3}}", + ".X | urlquery | html | print 2 | .f 3", + []string{"urlquery", "html"}, + }, } for i, test := range tests { tmpl := template.Must(template.New("test").Parse(test.input)) From 2b833666f13e851f1a83873ac249bfce1059df2c Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Fri, 8 May 2015 11:00:17 -0700 Subject: [PATCH 006/232] testing: make the output of -v more uniform and aligned when using fixed-width fonts Delete the colon from RUN: for examples, since it's not there for tests. Add spaces to line up RUN and PASS: lines. Before: === RUN TestCount --- PASS: TestCount (0.00s) === RUN: ExampleFields --- PASS: ExampleFields (0.00s) After: === RUN TestCount --- PASS: TestCount (0.00s) === RUN ExampleFields --- PASS: ExampleFields (0.00s) Fixes #10594. Change-Id: I189c80a5d99101ee72d8c9c3a4639c07e640cbd8 Reviewed-on: https://go-review.googlesource.com/9846 Reviewed-by: Brad Fitzpatrick --- src/testing/example.go | 2 +- src/testing/testing.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/testing/example.go b/src/testing/example.go index 61339a6465..30baf27030 100644 --- a/src/testing/example.go +++ b/src/testing/example.go @@ -43,7 +43,7 @@ func RunExamples(matchString func(pat, str string) (bool, error), examples []Int func runExample(eg InternalExample) (ok bool) { if *chatty { - fmt.Printf("=== RUN: %s\n", eg.Name) + fmt.Printf("=== RUN %s\n", eg.Name) } // Capture stdout. diff --git a/src/testing/testing.go b/src/testing/testing.go index 280d76a1aa..35ab82d421 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -557,7 +557,7 @@ func RunTests(matchString func(pat, str string) (bool, error), tests []InternalT } t.self = t if *chatty { - fmt.Printf("=== RUN %s\n", t.name) + fmt.Printf("=== RUN %s\n", t.name) } go tRunner(t, &tests[i]) out := (<-t.signal).(*T) From 2d9a50b97f762637627436b6ed153242e43874fb Mon Sep 17 00:00:00 2001 From: Didier Spezia Date: Fri, 8 May 2015 16:38:08 +0000 Subject: [PATCH 007/232] html: simplify and optimize escape/unescape The html package uses some specific code to escape special characters. Actually, the strings.Replacer can be used instead, and is much more efficient. The converse operation is more complex but can still be slightly optimized. Credits to Ken Bloom (kabloom@google.com), who first submitted a similar patch at https://codereview.appspot.com/141930043 Added benchmarks and slightly optimized UnescapeString. benchmark old ns/op new ns/op delta BenchmarkEscape-4 118713 19825 -83.30% BenchmarkEscapeNone-4 87653 3784 -95.68% BenchmarkUnescape-4 24888 23417 -5.91% BenchmarkUnescapeNone-4 14423 157 -98.91% benchmark old allocs new allocs delta BenchmarkEscape-4 9 2 -77.78% BenchmarkEscapeNone-4 0 0 +0.00% BenchmarkUnescape-4 2 2 +0.00% BenchmarkUnescapeNone-4 0 0 +0.00% benchmark old bytes new bytes delta BenchmarkEscape-4 24800 12288 -50.45% BenchmarkEscapeNone-4 0 0 +0.00% BenchmarkUnescape-4 10240 10240 +0.00% BenchmarkUnescapeNone-4 0 0 +0.00% Fixes #8697 Change-Id: I208261ed7cbe9b3dee6317851f8c0cf15528bce4 Reviewed-on: https://go-review.googlesource.com/9808 Run-TryBot: Brad Fitzpatrick Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/html/escape.go | 57 ++++++++--------------------------------- src/html/escape_test.go | 40 ++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/html/escape.go b/src/html/escape.go index dd5dfa7cd7..f50a4b937a 100644 --- a/src/html/escape.go +++ b/src/html/escape.go @@ -6,7 +6,6 @@ package html import ( - "bytes" "strings" "unicode/utf8" ) @@ -187,52 +186,20 @@ func unescape(b []byte) []byte { return b } -const escapedChars = `&'<>"` - -func escape(w writer, s string) error { - i := strings.IndexAny(s, escapedChars) - for i != -1 { - if _, err := w.WriteString(s[:i]); err != nil { - return err - } - var esc string - switch s[i] { - case '&': - esc = "&" - case '\'': - // "'" is shorter than "'" and apos was not in HTML until HTML5. - esc = "'" - case '<': - esc = "<" - case '>': - esc = ">" - case '"': - // """ is shorter than """. - esc = """ - default: - panic("unrecognized escape character") - } - s = s[i+1:] - if _, err := w.WriteString(esc); err != nil { - return err - } - i = strings.IndexAny(s, escapedChars) - } - _, err := w.WriteString(s) - return err -} +var htmlEscaper = strings.NewReplacer( + `&`, "&", + `'`, "'", // "'" is shorter than "'" and apos was not in HTML until HTML5. + `<`, "<", + `>`, ">", + `"`, """, // """ is shorter than """. +) // EscapeString escapes special characters like "<" to become "<". It // escapes only five such characters: <, >, &, ' and ". // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func EscapeString(s string) string { - if strings.IndexAny(s, escapedChars) == -1 { - return s - } - var buf bytes.Buffer - escape(&buf, s) - return buf.String() + return htmlEscaper.Replace(s) } // UnescapeString unescapes entities like "<" to become "<". It unescapes a @@ -241,10 +208,8 @@ func EscapeString(s string) string { // UnescapeString(EscapeString(s)) == s always holds, but the converse isn't // always true. func UnescapeString(s string) string { - for _, c := range s { - if c == '&' { - return string(unescape([]byte(s))) - } + if !strings.Contains(s, "&") { + return s } - return s + return string(unescape([]byte(s))) } diff --git a/src/html/escape_test.go b/src/html/escape_test.go index 2d7ad8ac26..3702626a3d 100644 --- a/src/html/escape_test.go +++ b/src/html/escape_test.go @@ -4,7 +4,10 @@ package html -import "testing" +import ( + "strings" + "testing" +) type unescapeTest struct { // A short description of the test case. @@ -113,3 +116,38 @@ func TestUnescapeEscape(t *testing.T) { } } } + +var ( + benchEscapeData = strings.Repeat("AAAAA < BBBBB > CCCCC & DDDDD ' EEEEE \" ", 100) + benchEscapeNone = strings.Repeat("AAAAA x BBBBB x CCCCC x DDDDD x EEEEE x ", 100) +) + +func BenchmarkEscape(b *testing.B) { + n := 0 + for i := 0; i < b.N; i++ { + n += len(EscapeString(benchEscapeData)) + } +} + +func BenchmarkEscapeNone(b *testing.B) { + n := 0 + for i := 0; i < b.N; i++ { + n += len(EscapeString(benchEscapeNone)) + } +} + +func BenchmarkUnescape(b *testing.B) { + s := EscapeString(benchEscapeData) + n := 0 + for i := 0; i < b.N; i++ { + n += len(UnescapeString(s)) + } +} + +func BenchmarkUnescapeNone(b *testing.B) { + s := EscapeString(benchEscapeNone) + n := 0 + for i := 0; i < b.N; i++ { + n += len(UnescapeString(s)) + } +} From 82359d1c2d835d44d84408b52fd8eaf4965ad363 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Sat, 9 May 2015 01:29:03 +0900 Subject: [PATCH 008/232] net: enable cgo test on solaris MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4ade27469d82839b4396e1a88465dddc6b31d578 Reviewed-on: https://go-review.googlesource.com/9838 Reviewed-by: Aram Hăvărneanu --- src/net/cgo_unix_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net/cgo_unix_test.go b/src/net/cgo_unix_test.go index 55ea86a458..4d5ab23fd3 100644 --- a/src/net/cgo_unix_test.go +++ b/src/net/cgo_unix_test.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // +build cgo,!netgo -// +build darwin dragonfly freebsd linux netbsd openbsd +// +build darwin dragonfly freebsd linux netbsd openbsd solaris package net From d9f69196dee8c95e4669d18619f3e3729599e90f Mon Sep 17 00:00:00 2001 From: Rahul Chaudhry Date: Fri, 8 May 2015 12:18:52 -0700 Subject: [PATCH 009/232] build: correct quoting of args in run.bash Change-Id: I72df4d979212d8af74a4d2763423346eb6ba14f2 Reviewed-on: https://go-review.googlesource.com/9892 Reviewed-by: Brad Fitzpatrick --- src/run.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/run.bash b/src/run.bash index 6fc864dc0e..f35ec78982 100755 --- a/src/run.bash +++ b/src/run.bash @@ -35,4 +35,4 @@ if ulimit -T &> /dev/null; then [ "$(ulimit -H -T)" == "unlimited" ] || ulimit -S -T $(ulimit -H -T) fi -exec go tool dist test $@ +exec go tool dist test "$@" From 347536201124400705d0d217e2180c9f01066808 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Thu, 7 May 2015 21:08:40 +0000 Subject: [PATCH 010/232] syscall: fix InotifyInit on linux/arm64 There is no SYS_INOTIFY_INIT on linux/arm64, only SYS_INOTIFY_INIT1. Change-Id: I97f430f2c2b910fb19dce495ff1adf591b8634fc Reviewed-on: https://go-review.googlesource.com/9870 Run-TryBot: Minux Ma TryBot-Result: Gobot Gobot Reviewed-by: Ian Lance Taylor Reviewed-by: Dave Cheney --- src/syscall/syscall_linux.go | 1 - src/syscall/syscall_linux_386.go | 1 + src/syscall/syscall_linux_amd64.go | 1 + src/syscall/syscall_linux_arm.go | 1 + src/syscall/syscall_linux_arm64.go | 5 ++++- src/syscall/syscall_linux_ppc64x.go | 1 + src/syscall/zsyscall_linux_386.go | 22 +++++++++++----------- src/syscall/zsyscall_linux_amd64.go | 22 +++++++++++----------- src/syscall/zsyscall_linux_arm.go | 22 +++++++++++----------- src/syscall/zsyscall_linux_arm64.go | 11 ----------- src/syscall/zsyscall_linux_ppc64.go | 22 +++++++++++----------- src/syscall/zsyscall_linux_ppc64le.go | 22 +++++++++++----------- 12 files changed, 63 insertions(+), 68 deletions(-) diff --git a/src/syscall/syscall_linux.go b/src/syscall/syscall_linux.go index 05d4044635..4f88d517e4 100644 --- a/src/syscall/syscall_linux.go +++ b/src/syscall/syscall_linux.go @@ -852,7 +852,6 @@ func Getpgrp() (pid int) { //sysnb Gettid() (tid int) //sys Getxattr(path string, attr string, dest []byte) (sz int, err error) //sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) -//sysnb InotifyInit() (fd int, err error) //sysnb InotifyInit1(flags int) (fd int, err error) //sysnb InotifyRmWatch(fd int, watchdesc uint32) (success int, err error) //sysnb Kill(pid int, sig Signal) (err error) diff --git a/src/syscall/syscall_linux_386.go b/src/syscall/syscall_linux_386.go index 98636a53ad..9ee1c1cd16 100644 --- a/src/syscall/syscall_linux_386.go +++ b/src/syscall/syscall_linux_386.go @@ -66,6 +66,7 @@ func Pipe2(p []int, flags int) (err error) { //sysnb Geteuid() (euid int) = SYS_GETEUID32 //sysnb Getgid() (gid int) = SYS_GETGID32 //sysnb Getuid() (uid int) = SYS_GETUID32 +//sysnb InotifyInit() (fd int, err error) //sys Ioperm(from int, num int, on int) (err error) //sys Iopl(level int) (err error) //sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32 diff --git a/src/syscall/syscall_linux_amd64.go b/src/syscall/syscall_linux_amd64.go index fad9c32580..6fbef21120 100644 --- a/src/syscall/syscall_linux_amd64.go +++ b/src/syscall/syscall_linux_amd64.go @@ -16,6 +16,7 @@ const _SYS_dup = SYS_DUP2 //sysnb Getgid() (gid int) //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) //sysnb Getuid() (uid int) +//sysnb InotifyInit() (fd int, err error) //sys Ioperm(from int, num int, on int) (err error) //sys Iopl(level int) (err error) //sys Lchown(path string, uid int, gid int) (err error) diff --git a/src/syscall/syscall_linux_arm.go b/src/syscall/syscall_linux_arm.go index f0cc25ebed..218d6b86d4 100644 --- a/src/syscall/syscall_linux_arm.go +++ b/src/syscall/syscall_linux_arm.go @@ -87,6 +87,7 @@ func Seek(fd int, offset int64, whence int) (newoffset int64, err error) { //sysnb Geteuid() (euid int) = SYS_GETEUID32 //sysnb Getgid() (gid int) = SYS_GETGID32 //sysnb Getuid() (uid int) = SYS_GETUID32 +//sysnb InotifyInit() (fd int, err error) //sys Lchown(path string, uid int, gid int) (err error) = SYS_LCHOWN32 //sys Listen(s int, n int) (err error) //sys Lstat(path string, stat *Stat_t) (err error) = SYS_LSTAT64 diff --git a/src/syscall/syscall_linux_arm64.go b/src/syscall/syscall_linux_arm64.go index 3c4eabca5f..7ca4164544 100644 --- a/src/syscall/syscall_linux_arm64.go +++ b/src/syscall/syscall_linux_arm64.go @@ -124,11 +124,14 @@ func (cmsg *Cmsghdr) SetLen(length int) { cmsg.Len = uint64(length) } +func InotifyInit() (fd int, err error) { + return InotifyInit1(0) +} + // TODO(dfc): constants that should be in zsysnum_linux_arm64.go, remove // these when the deprecated syscalls that the syscall package relies on // are removed. const ( - SYS_INOTIFY_INIT = 1043 SYS_GETPGRP = 1060 SYS_UTIMES = 1037 SYS_FUTIMESAT = 1066 diff --git a/src/syscall/syscall_linux_ppc64x.go b/src/syscall/syscall_linux_ppc64x.go index 5318c6128c..10489d927f 100644 --- a/src/syscall/syscall_linux_ppc64x.go +++ b/src/syscall/syscall_linux_ppc64x.go @@ -19,6 +19,7 @@ const _SYS_dup = SYS_DUP2 //sysnb Getgid() (gid int) //sysnb Getrlimit(resource int, rlim *Rlimit) (err error) = SYS_UGETRLIMIT //sysnb Getuid() (uid int) +//sysnb InotifyInit() (fd int, err error) //sys Ioperm(from int, num int, on int) (err error) //sys Iopl(level int) (err error) //sys Lchown(path string, uid int, gid int) (err error) diff --git a/src/syscall/zsyscall_linux_386.go b/src/syscall/zsyscall_linux_386.go index e7cf7452c2..2584d61e2f 100644 --- a/src/syscall/zsyscall_linux_386.go +++ b/src/syscall/zsyscall_linux_386.go @@ -615,17 +615,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func InotifyInit() (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func InotifyInit1(flags int) (fd int, err error) { r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0) fd = int(r0) @@ -1298,6 +1287,17 @@ func Getuid() (uid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func InotifyInit() (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Ioperm(from int, num int, on int) (err error) { _, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on)) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_amd64.go b/src/syscall/zsyscall_linux_amd64.go index b23573bc84..141f4f39be 100644 --- a/src/syscall/zsyscall_linux_amd64.go +++ b/src/syscall/zsyscall_linux_amd64.go @@ -615,17 +615,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func InotifyInit() (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func InotifyInit1(flags int) (fd int, err error) { r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0) fd = int(r0) @@ -1298,6 +1287,17 @@ func Getuid() (uid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func InotifyInit() (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Ioperm(from int, num int, on int) (err error) { _, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on)) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go index 054cf4005d..ee4f6e1245 100644 --- a/src/syscall/zsyscall_linux_arm.go +++ b/src/syscall/zsyscall_linux_arm.go @@ -615,17 +615,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func InotifyInit() (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func InotifyInit1(flags int) (fd int, err error) { r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0) fd = int(r0) @@ -1457,6 +1446,17 @@ func Getuid() (uid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func InotifyInit() (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Lchown(path string, uid int, gid int) (err error) { var _p0 *byte _p0, err = BytePtrFromString(path) diff --git a/src/syscall/zsyscall_linux_arm64.go b/src/syscall/zsyscall_linux_arm64.go index 26a14b7244..a294eb6096 100644 --- a/src/syscall/zsyscall_linux_arm64.go +++ b/src/syscall/zsyscall_linux_arm64.go @@ -615,17 +615,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func InotifyInit() (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func InotifyInit1(flags int) (fd int, err error) { r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0) fd = int(r0) diff --git a/src/syscall/zsyscall_linux_ppc64.go b/src/syscall/zsyscall_linux_ppc64.go index 326218031e..ba287e27e1 100644 --- a/src/syscall/zsyscall_linux_ppc64.go +++ b/src/syscall/zsyscall_linux_ppc64.go @@ -615,17 +615,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func InotifyInit() (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func InotifyInit1(flags int) (fd int, err error) { r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0) fd = int(r0) @@ -1298,6 +1287,17 @@ func Getuid() (uid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func InotifyInit() (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Ioperm(from int, num int, on int) (err error) { _, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on)) if e1 != 0 { diff --git a/src/syscall/zsyscall_linux_ppc64le.go b/src/syscall/zsyscall_linux_ppc64le.go index 326218031e..ba287e27e1 100644 --- a/src/syscall/zsyscall_linux_ppc64le.go +++ b/src/syscall/zsyscall_linux_ppc64le.go @@ -615,17 +615,6 @@ func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err e // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT -func InotifyInit() (fd int, err error) { - r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) - fd = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - func InotifyInit1(flags int) (fd int, err error) { r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT1, uintptr(flags), 0, 0) fd = int(r0) @@ -1298,6 +1287,17 @@ func Getuid() (uid int) { // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT +func InotifyInit() (fd int, err error) { + r0, _, e1 := RawSyscall(SYS_INOTIFY_INIT, 0, 0, 0) + fd = int(r0) + if e1 != 0 { + err = errnoErr(e1) + } + return +} + +// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT + func Ioperm(from int, num int, on int) (err error) { _, _, e1 := Syscall(SYS_IOPERM, uintptr(from), uintptr(num), uintptr(on)) if e1 != 0 { From 2320b56af1b26e4c945c282a89c53e599596b86c Mon Sep 17 00:00:00 2001 From: Patrick Mezard Date: Fri, 8 May 2015 14:57:30 +0200 Subject: [PATCH 011/232] internal/syscall/windows: increase registry.ExpandString buffer ExpandString correctly loops on the syscall until it reaches the required buffer size but truncates it before converting it back to string. The truncation limit is increased to 2^15 bytes which is the documented maximum ExpandEnvironmentStrings output size. This fixes TestExpandString on systems where len($PATH) > 1024. Change-Id: I2a6f184eeca939121b458bcffe1a436a50f3298e Reviewed-on: https://go-review.googlesource.com/9805 Reviewed-by: Brad Fitzpatrick Reviewed-by: Alex Brainman Run-TryBot: Alex Brainman TryBot-Result: Gobot Gobot --- src/internal/syscall/windows/registry/value.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/syscall/windows/registry/value.go b/src/internal/syscall/windows/registry/value.go index 1c1771d30e..b2b28aadf8 100644 --- a/src/internal/syscall/windows/registry/value.go +++ b/src/internal/syscall/windows/registry/value.go @@ -130,7 +130,7 @@ func ExpandString(value string) (string, error) { return "", err } if n <= uint32(len(r)) { - u := (*[1 << 10]uint16)(unsafe.Pointer(&r[0]))[:] + u := (*[1 << 15]uint16)(unsafe.Pointer(&r[0]))[:] return syscall.UTF16ToString(u), nil } r = make([]uint16, n) From c8b31c5cea0ecdb7eec44752701a653c325688df Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Sat, 9 May 2015 21:53:33 -0400 Subject: [PATCH 012/232] go/build: fix typo Thanks Dmitri Shuralyov for pointing it out. Change-Id: If9c5ac0e56d601d327b2b682ee3548037439cb83 Reviewed-on: https://go-review.googlesource.com/9881 Reviewed-by: Josh Bleecher Snyder --- src/go/build/build.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/go/build/build.go b/src/go/build/build.go index 124da40d3b..d91eb0b24d 100644 --- a/src/go/build/build.go +++ b/src/go/build/build.go @@ -1328,7 +1328,7 @@ func (ctxt *Context) goodOSArchFile(name string, allTags map[string]bool) bool { // build tag "linux" in that file. For Go 1.4 and beyond, we require this // auto-tagging to apply only to files with a non-empty prefix, so // "foo_linux.go" is tagged but "linux.go" is not. This allows new operating - // sytems, such as android, to arrive without breaking existing code with + // systems, such as android, to arrive without breaking existing code with // innocuous source code in "android.go". The easiest fix: cut everything // in the name before the initial _. i := strings.Index(name, "_") From cbcc7584de99dbefd3e019d2a53442b30d5af989 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Sun, 10 May 2015 23:11:04 +0900 Subject: [PATCH 013/232] net: increase timeout in TestWriteTimeoutFluctuation on darwin/arm On darwin/arm, the test sometimes fails with: Process 557 resuming --- FAIL: TestWriteTimeoutFluctuation (1.64s) timeout_test.go:706: Write took over 1s; expected 0.1s FAIL Process 557 exited with status = 1 (0x00000001) go_darwin_arm_exec: timeout running tests This change increaes timeout on iOS builders from 1s to 3s as a temporarily fix. Updates #10775. Change-Id: Ifdaf99cf5b8582c1a636a0f7d5cc66bb276efd72 Reviewed-on: https://go-review.googlesource.com/9915 Reviewed-by: Minux Ma --- src/net/timeout_test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/net/timeout_test.go b/src/net/timeout_test.go index cafa3755f6..9688c21699 100644 --- a/src/net/timeout_test.go +++ b/src/net/timeout_test.go @@ -696,14 +696,18 @@ func TestWriteTimeoutFluctuation(t *testing.T) { } defer c.Close() - max := time.NewTimer(time.Second) + d := time.Second + if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") { + d = 3 * time.Second // see golang.org/issue/10775 + } + max := time.NewTimer(d) defer max.Stop() ch := make(chan error) go timeoutTransmitter(c, 100*time.Millisecond, 50*time.Millisecond, 250*time.Millisecond, ch) select { case <-max.C: - t.Fatal("Write took over 1s; expected 0.1s") + t.Fatalf("Write took over %v; expected 0.1s", d) case err := <-ch: if perr := parseWriteError(err); perr != nil { t.Error(perr) From dce432b388541d9a54be1bdebb1a6ff13e426427 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Sat, 9 May 2015 23:04:04 -0400 Subject: [PATCH 014/232] misc/trace: add license for the trace-viewer The trace-viewer doesn't use the Go license, so it makes sense to include the license text into the README.md file. While we're at here, reformat existing text using real Markdown syntax. Change-Id: I13e42d3cc6a0ca7e64e3d46ad460dc0460f7ed09 Reviewed-on: https://go-review.googlesource.com/9882 Reviewed-by: Rob Pike --- misc/trace/README.md | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/misc/trace/README.md b/misc/trace/README.md index b9364de78c..775fdb8c10 100644 --- a/misc/trace/README.md +++ b/misc/trace/README.md @@ -1,6 +1,37 @@ -This directory contains helper file for trace viewer (go tool trace). +This directory contains helper file for trace viewer (`go tool trace`). -trace_viewer_lean.html was generated following instructions in: -https://github.com/google/trace-viewer/wiki/Embedding -on revision 895aa74558d19d91906fb720df6458244ef160c6 using: +`trace_viewer_lean.html` was generated by following +[instructions](https://github.com/google/trace-viewer/wiki/Embedding) +on revision `895aa74558d19d91906fb720df6458244ef160c6` using: +``` trace-viewer$ ./vulcanize_trace_viewer --config=lean +``` + +The license for trace-viewer is as follows: +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 19e81a9b3b0d103fe7a9a9605c16eb4ba1f20ed8 Mon Sep 17 00:00:00 2001 From: Patrick Mezard Date: Sat, 9 May 2015 15:44:58 +0200 Subject: [PATCH 015/232] internal/syscall/windows/registry: handle invalid integer values I have around twenty of such values on a Windows 7 development machine. regedit displays (translated): "invalid 32-bits DWORD value". Change-Id: Ib37a414ee4c85e891b0a25fed2ddad9e105f5f4e Reviewed-on: https://go-review.googlesource.com/9901 Reviewed-by: Alex Brainman --- src/internal/syscall/windows/registry/value.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/internal/syscall/windows/registry/value.go b/src/internal/syscall/windows/registry/value.go index b2b28aadf8..814fe445b9 100644 --- a/src/internal/syscall/windows/registry/value.go +++ b/src/internal/syscall/windows/registry/value.go @@ -175,8 +175,14 @@ func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error } switch typ { case DWORD: + if len(data) != 4 { + return 0, typ, errors.New("DWORD value is not 4 bytes long") + } return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil case QWORD: + if len(data) != 8 { + return 0, typ, errors.New("QWORD value is not 8 bytes long") + } return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil default: return 0, typ, ErrUnexpectedType From db6f88a84b126877bd523df8c45af06779ce0e42 Mon Sep 17 00:00:00 2001 From: Daniel Morsing Date: Thu, 30 Apr 2015 15:32:54 +0100 Subject: [PATCH 016/232] runtime: enable profiling on g0 Since we now have stack information for code running on the systemstack, we can traceback over it. To make cpu profiles useful, add a case in gentraceback to jump over systemstack switches. Fixes #10609. Change-Id: I21f47fcc802c07c5d4a1ada56374314e388a6dc7 Reviewed-on: https://go-review.googlesource.com/9506 Reviewed-by: Dmitry Vyukov --- src/runtime/arch1_386.go | 15 ++++---- src/runtime/arch1_amd64.go | 15 ++++---- src/runtime/arch1_amd64p32.go | 15 ++++---- src/runtime/arch1_arm.go | 15 ++++---- src/runtime/arch1_arm64.go | 15 ++++---- src/runtime/arch1_ppc64.go | 15 ++++---- src/runtime/arch1_ppc64le.go | 15 ++++---- src/runtime/export_test.go | 5 --- src/runtime/proc1.go | 64 +++++++++++++++++++++-------------- src/runtime/runtime2.go | 5 +-- src/runtime/runtime_test.go | 52 ---------------------------- src/runtime/traceback.go | 16 ++++++++- 12 files changed, 105 insertions(+), 142 deletions(-) diff --git a/src/runtime/arch1_386.go b/src/runtime/arch1_386.go index b024d7a51f..d41696a6d6 100644 --- a/src/runtime/arch1_386.go +++ b/src/runtime/arch1_386.go @@ -5,12 +5,11 @@ package runtime const ( - thechar = '8' - _BigEndian = 0 - _CacheLineSize = 64 - _RuntimeGogoBytes = 64 - _PhysPageSize = goos_nacl*65536 + (1-goos_nacl)*4096 // 4k normally; 64k on NaCl - _PCQuantum = 1 - _Int64Align = 4 - hugePageSize = 1 << 21 + thechar = '8' + _BigEndian = 0 + _CacheLineSize = 64 + _PhysPageSize = goos_nacl*65536 + (1-goos_nacl)*4096 // 4k normally; 64k on NaCl + _PCQuantum = 1 + _Int64Align = 4 + hugePageSize = 1 << 21 ) diff --git a/src/runtime/arch1_amd64.go b/src/runtime/arch1_amd64.go index 932b2b7c55..15f4cc65fe 100644 --- a/src/runtime/arch1_amd64.go +++ b/src/runtime/arch1_amd64.go @@ -5,12 +5,11 @@ package runtime const ( - thechar = '6' - _BigEndian = 0 - _CacheLineSize = 64 - _RuntimeGogoBytes = 80 + (goos_solaris)*16 - _PhysPageSize = 4096 - _PCQuantum = 1 - _Int64Align = 8 - hugePageSize = 1 << 21 + thechar = '6' + _BigEndian = 0 + _CacheLineSize = 64 + _PhysPageSize = 4096 + _PCQuantum = 1 + _Int64Align = 8 + hugePageSize = 1 << 21 ) diff --git a/src/runtime/arch1_amd64p32.go b/src/runtime/arch1_amd64p32.go index 79421e848a..3c5456f933 100644 --- a/src/runtime/arch1_amd64p32.go +++ b/src/runtime/arch1_amd64p32.go @@ -5,12 +5,11 @@ package runtime const ( - thechar = '6' - _BigEndian = 0 - _CacheLineSize = 64 - _RuntimeGogoBytes = 64 - _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl) - _PCQuantum = 1 - _Int64Align = 8 - hugePageSize = 1 << 21 + thechar = '6' + _BigEndian = 0 + _CacheLineSize = 64 + _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl) + _PCQuantum = 1 + _Int64Align = 8 + hugePageSize = 1 << 21 ) diff --git a/src/runtime/arch1_arm.go b/src/runtime/arch1_arm.go index c3fe4f0cb3..0ec2093881 100644 --- a/src/runtime/arch1_arm.go +++ b/src/runtime/arch1_arm.go @@ -5,12 +5,11 @@ package runtime const ( - thechar = '5' - _BigEndian = 0 - _CacheLineSize = 32 - _RuntimeGogoBytes = 60 - _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl) - _PCQuantum = 4 - _Int64Align = 4 - hugePageSize = 0 + thechar = '5' + _BigEndian = 0 + _CacheLineSize = 32 + _PhysPageSize = 65536*goos_nacl + 4096*(1-goos_nacl) + _PCQuantum = 4 + _Int64Align = 4 + hugePageSize = 0 ) diff --git a/src/runtime/arch1_arm64.go b/src/runtime/arch1_arm64.go index 549a635ca4..1a3165c8b7 100644 --- a/src/runtime/arch1_arm64.go +++ b/src/runtime/arch1_arm64.go @@ -5,12 +5,11 @@ package runtime const ( - thechar = '7' - _BigEndian = 0 - _CacheLineSize = 32 - _RuntimeGogoBytes = 64 - _PhysPageSize = 4096*(1-goos_darwin) + 16384*goos_darwin - _PCQuantum = 4 - _Int64Align = 8 - hugePageSize = 0 + thechar = '7' + _BigEndian = 0 + _CacheLineSize = 32 + _PhysPageSize = 4096*(1-goos_darwin) + 16384*goos_darwin + _PCQuantum = 4 + _Int64Align = 8 + hugePageSize = 0 ) diff --git a/src/runtime/arch1_ppc64.go b/src/runtime/arch1_ppc64.go index ee453c09f2..de6dd91401 100644 --- a/src/runtime/arch1_ppc64.go +++ b/src/runtime/arch1_ppc64.go @@ -5,12 +5,11 @@ package runtime const ( - thechar = '9' - _BigEndian = 1 - _CacheLineSize = 64 - _RuntimeGogoBytes = 72 - _PhysPageSize = 65536 - _PCQuantum = 4 - _Int64Align = 8 - hugePageSize = 0 + thechar = '9' + _BigEndian = 1 + _CacheLineSize = 64 + _PhysPageSize = 65536 + _PCQuantum = 4 + _Int64Align = 8 + hugePageSize = 0 ) diff --git a/src/runtime/arch1_ppc64le.go b/src/runtime/arch1_ppc64le.go index aa028a10f3..9a55c71101 100644 --- a/src/runtime/arch1_ppc64le.go +++ b/src/runtime/arch1_ppc64le.go @@ -5,12 +5,11 @@ package runtime const ( - thechar = '9' - _BigEndian = 0 - _CacheLineSize = 64 - _RuntimeGogoBytes = 72 - _PhysPageSize = 65536 - _PCQuantum = 4 - _Int64Align = 8 - hugePageSize = 0 + thechar = '9' + _BigEndian = 0 + _CacheLineSize = 64 + _PhysPageSize = 65536 + _PCQuantum = 4 + _Int64Align = 8 + hugePageSize = 0 ) diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index e0c8b17bd3..378a68e019 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -106,11 +106,6 @@ var MemclrBytes = memclrBytes var HashLoad = &hashLoad -// For testing. -func GogoBytes() int32 { - return _RuntimeGogoBytes -} - // entry point for testing func GostringW(w []uint16) (s string) { systemstack(func() { diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go index 00535da77d..6bd90ece31 100644 --- a/src/runtime/proc1.go +++ b/src/runtime/proc1.go @@ -2484,11 +2484,9 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { mp.mallocing++ // Define that a "user g" is a user-created goroutine, and a "system g" - // is one that is m->g0 or m->gsignal. We've only made sure that we - // can unwind user g's, so exclude the system g's. + // is one that is m->g0 or m->gsignal. // - // It is not quite as easy as testing gp == m->curg (the current user g) - // because we might be interrupted for profiling halfway through a + // We might be interrupted for profiling halfway through a // goroutine switch. The switch involves updating three (or four) values: // g, PC, SP, and (on arm) LR. The PC must be the last to be updated, // because once it gets updated the new g is running. @@ -2497,8 +2495,7 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { // so the update only affects g, SP, and PC. Since PC must be last, there // the possible partial transitions in ordinary execution are (1) g alone is updated, // (2) both g and SP are updated, and (3) SP alone is updated. - // If g is updated, we'll see a system g and not look closer. - // If SP alone is updated, we can detect the partial transition by checking + // If SP or g alone is updated, we can detect the partial transition by checking // whether the SP is within g's stack bounds. (We could also require that SP // be changed only after g, but the stack bounds check is needed by other // cases, so there is no need to impose an additional requirement.) @@ -2527,15 +2524,11 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { // disabled, so a profiling signal cannot arrive then anyway. // // Third, the common case: it may be that the switch updates g, SP, and PC - // separately, as in gogo. - // - // Because gogo is the only instance, we check whether the PC lies - // within that function, and if so, not ask for a traceback. This approach - // requires knowing the size of the gogo function, which we - // record in arch_*.h and check in runtime_test.go. + // separately. If the PC is within any of the functions that does this, + // we don't ask for a traceback. C.F. the function setsSP for more about this. // // There is another apparently viable approach, recorded here in case - // the "PC within gogo" check turns out not to be usable. + // the "PC within setsSP function" check turns out not to be usable. // It would be possible to delay the update of either g or SP until immediately // before the PC update instruction. Then, because of the stack bounds check, // the only problematic interrupt point is just before that PC update instruction, @@ -2556,28 +2549,23 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { // transition. We simply require that g and SP match and that the PC is not // in gogo. traceback := true - gogo := funcPC(gogo) - if gp == nil || gp != mp.curg || - sp < gp.stack.lo || gp.stack.hi < sp || - (gogo <= pc && pc < gogo+_RuntimeGogoBytes) { + if gp == nil || sp < gp.stack.lo || gp.stack.hi < sp || setsSP(pc) { traceback = false } - var stk [maxCPUProfStack]uintptr n := 0 - if traceback { - n = gentraceback(pc, sp, lr, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap) + if mp.ncgo > 0 && mp.curg != nil && mp.curg.syscallpc != 0 && mp.curg.syscallsp != 0 { + // Cgo, we can't unwind and symbolize arbitrary C code, + // so instead collect Go stack that leads to the cgo call. + // This is especially important on windows, since all syscalls are cgo calls. + n = gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[0], len(stk), nil, nil, 0) + } else if traceback { + n = gentraceback(pc, sp, lr, gp, 0, &stk[0], len(stk), nil, nil, _TraceTrap|_TraceJumpStack) } if !traceback || n <= 0 { // Normal traceback is impossible or has failed. // See if it falls into several common cases. n = 0 - if mp.ncgo > 0 && mp.curg != nil && mp.curg.syscallpc != 0 && mp.curg.syscallsp != 0 { - // Cgo, we can't unwind and symbolize arbitrary C code, - // so instead collect Go stack that leads to the cgo call. - // This is especially important on windows, since all syscalls are cgo calls. - n = gentraceback(mp.curg.syscallpc, mp.curg.syscallsp, 0, mp.curg, 0, &stk[0], len(stk), nil, nil, 0) - } if GOOS == "windows" && n == 0 && mp.libcallg != 0 && mp.libcallpc != 0 && mp.libcallsp != 0 { // Libcall, i.e. runtime syscall on windows. // Collect Go stack that leads to the call. @@ -2612,6 +2600,30 @@ func sigprof(pc, sp, lr uintptr, gp *g, mp *m) { mp.mallocing-- } +// Reports whether a function will set the SP +// to an absolute value. Important that +// we don't traceback when these are at the bottom +// of the stack since we can't be sure that we will +// find the caller. +// +// If the function is not on the bottom of the stack +// we assume that it will have set it up so that traceback will be consistent, +// either by being a traceback terminating function +// or putting one on the stack at the right offset. +func setsSP(pc uintptr) bool { + f := findfunc(pc) + if f == nil { + // couldn't find the function for this PC, + // so assume the worst and stop traceback + return true + } + switch f.entry { + case gogoPC, systemstackPC, mcallPC, morestackPC: + return true + } + return false +} + // Arrange to call fn with a traceback hz times a second. func setcpuprofilerate_m(hz int32) { // Force sane arguments. diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index ac539b9a9d..8dfece5845 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -594,8 +594,9 @@ type stkframe struct { } const ( - _TraceRuntimeFrames = 1 << 0 // include frames for internal runtime functions. - _TraceTrap = 1 << 1 // the initial PC, SP are from a trap, not a return PC from a call + _TraceRuntimeFrames = 1 << iota // include frames for internal runtime functions. + _TraceTrap // the initial PC, SP are from a trap, not a return PC from a call + _TraceJumpStack // if traceback is on a systemstack, resume trace at g that called into it ) const ( diff --git a/src/runtime/runtime_test.go b/src/runtime/runtime_test.go index d4cccbf084..f65562ab91 100644 --- a/src/runtime/runtime_test.go +++ b/src/runtime/runtime_test.go @@ -6,13 +6,8 @@ package runtime_test import ( "io" - "io/ioutil" - "os" - "os/exec" . "runtime" "runtime/debug" - "strconv" - "strings" "testing" "unsafe" ) @@ -88,53 +83,6 @@ func BenchmarkDeferMany(b *testing.B) { } } -// The profiling signal handler needs to know whether it is executing runtime.gogo. -// The constant RuntimeGogoBytes in arch_*.h gives the size of the function; -// we don't have a way to obtain it from the linker (perhaps someday). -// Test that the constant matches the size determined by 'go tool nm -S'. -// The value reported will include the padding between runtime.gogo and the -// next function in memory. That's fine. -func TestRuntimeGogoBytes(t *testing.T) { - switch GOOS { - case "android", "nacl": - t.Skipf("skipping on %s", GOOS) - case "darwin": - switch GOARCH { - case "arm", "arm64": - t.Skipf("skipping on %s/%s, no fork", GOOS, GOARCH) - } - } - - dir, err := ioutil.TempDir("", "go-build") - if err != nil { - t.Fatalf("failed to create temp directory: %v", err) - } - defer os.RemoveAll(dir) - - out, err := exec.Command("go", "build", "-o", dir+"/hello", "../../test/helloworld.go").CombinedOutput() - if err != nil { - t.Fatalf("building hello world: %v\n%s", err, out) - } - - out, err = exec.Command("go", "tool", "nm", "-size", dir+"/hello").CombinedOutput() - if err != nil { - t.Fatalf("go tool nm: %v\n%s", err, out) - } - - for _, line := range strings.Split(string(out), "\n") { - f := strings.Fields(line) - if len(f) == 4 && f[3] == "runtime.gogo" { - size, _ := strconv.Atoi(f[1]) - if GogoBytes() != int32(size) { - t.Fatalf("RuntimeGogoBytes = %d, should be %d", GogoBytes(), size) - } - return - } - } - - t.Fatalf("go tool nm did not report size for runtime.gogo") -} - // golang.org/issue/7063 func TestStopCPUProfilingWithProfilerOff(t *testing.T) { SetCPUProfileRate(0) diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 9f34e37ea4..0f29608aae 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -46,6 +46,9 @@ var ( timerprocPC uintptr gcBgMarkWorkerPC uintptr systemstack_switchPC uintptr + systemstackPC uintptr + + gogoPC uintptr externalthreadhandlerp uintptr // initialized elsewhere ) @@ -69,6 +72,10 @@ func tracebackinit() { timerprocPC = funcPC(timerproc) gcBgMarkWorkerPC = funcPC(gcBgMarkWorker) systemstack_switchPC = funcPC(systemstack_switch) + systemstackPC = funcPC(systemstack) + + // used by sigprof handler + gogoPC = funcPC(gogo) } // Traceback over the deferred function calls. @@ -194,7 +201,14 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in // Found an actual function. // Derive frame pointer and link register. if frame.fp == 0 { - frame.fp = frame.sp + uintptr(funcspdelta(f, frame.pc)) + // We want to jump over the systemstack switch. If we're running on the + // g0, this systemstack is at the top of the stack. + // if we're not on g0 or there's a no curg, then this is a regular call. + sp := frame.sp + if flags&_TraceJumpStack != 0 && f.entry == systemstackPC && gp == g.m.g0 && gp.m.curg != nil { + sp = gp.m.curg.sched.sp + } + frame.fp = sp + uintptr(funcspdelta(f, frame.pc)) if !usesLR { // On x86, call instruction pushes return PC before entering new function. frame.fp += regSize From 7d9e16abc6bea2eb12d718b578f91328af99586a Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Sat, 2 May 2015 22:59:35 -0400 Subject: [PATCH 017/232] runtime: add benchmark of heapBitsSetType There was an old benchmark that measured this indirectly via allocation, but I don't understand how to factor out the allocation cost when interpreting the numbers. Replace with a benchmark that only calls heapBitsSetType, that does not allocate. This was not possible when the benchmark was first written, because heapBitsSetType had not been factored out of mallocgc. Change-Id: I30f0f02362efab3465a50769398be859832e6640 Reviewed-on: https://go-review.googlesource.com/9701 Reviewed-by: Austin Clements --- src/runtime/export_test.go | 29 +++++++ src/runtime/gc_test.go | 166 +++++++++++++++++++++++++++++-------- 2 files changed, 160 insertions(+), 35 deletions(-) diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 378a68e019..1efe24c61a 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -128,3 +128,32 @@ func Envs() []string { return envs } func SetEnvs(e []string) { envs = e } var BigEndian = _BigEndian + +// For benchmarking. + +func BenchSetType(n int, x interface{}) { + e := *(*eface)(unsafe.Pointer(&x)) + t := e._type + var size uintptr + var p unsafe.Pointer + switch t.kind & kindMask { + case _KindPtr: + t = (*ptrtype)(unsafe.Pointer(t)).elem + size = t.size + p = e.data + case _KindSlice: + slice := *(*struct { + ptr unsafe.Pointer + len, cap uintptr + })(e.data) + t = (*slicetype)(unsafe.Pointer(t)).elem + size = t.size * slice.len + p = slice.ptr + } + allocSize := roundupsize(size) + systemstack(func() { + for i := 0; i < n; i++ { + heapBitsSetType(uintptr(p), allocSize, size, t) + } + }) +} diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go index 6abec4cca7..f049bad499 100644 --- a/src/runtime/gc_test.go +++ b/src/runtime/gc_test.go @@ -6,6 +6,7 @@ package runtime_test import ( "os" + "reflect" "runtime" "runtime/debug" "testing" @@ -197,45 +198,140 @@ func TestHugeGCInfo(t *testing.T) { } } -func BenchmarkSetTypeNoPtr1(b *testing.B) { - type NoPtr1 struct { - p uintptr - } - var p *NoPtr1 - for i := 0; i < b.N; i++ { - p = &NoPtr1{} - } - _ = p +func BenchmarkSetTypePtr(b *testing.B) { + benchSetType(b, new(*byte)) } -func BenchmarkSetTypeNoPtr2(b *testing.B) { - type NoPtr2 struct { - p, q uintptr - } - var p *NoPtr2 - for i := 0; i < b.N; i++ { - p = &NoPtr2{} - } - _ = p + +func BenchmarkSetTypePtr8(b *testing.B) { + benchSetType(b, new([8]*byte)) } -func BenchmarkSetTypePtr1(b *testing.B) { - type Ptr1 struct { - p *byte - } - var p *Ptr1 - for i := 0; i < b.N; i++ { - p = &Ptr1{} - } - _ = p + +func BenchmarkSetTypePtr16(b *testing.B) { + benchSetType(b, new([16]*byte)) } -func BenchmarkSetTypePtr2(b *testing.B) { - type Ptr2 struct { - p, q *byte + +func BenchmarkSetTypePtr32(b *testing.B) { + benchSetType(b, new([32]*byte)) +} + +func BenchmarkSetTypePtr64(b *testing.B) { + benchSetType(b, new([64]*byte)) +} + +func BenchmarkSetTypePtr126(b *testing.B) { + benchSetType(b, new([126]*byte)) +} + +func BenchmarkSetTypePtr128(b *testing.B) { + benchSetType(b, new([128]*byte)) +} + +func BenchmarkSetTypePtrSlice(b *testing.B) { + benchSetType(b, make([]*byte, 1<<10)) +} + +type Node1 struct { + Value [1]uintptr + Left, Right *byte +} + +func BenchmarkSetTypeNode1(b *testing.B) { + benchSetType(b, new(Node1)) +} + +func BenchmarkSetTypeNode1Slice(b *testing.B) { + benchSetType(b, make([]Node1, 32)) +} + +type Node8 struct { + Value [8]uintptr + Left, Right *byte +} + +func BenchmarkSetTypeNode8(b *testing.B) { + benchSetType(b, new(Node8)) +} + +func BenchmarkSetTypeNode8Slice(b *testing.B) { + benchSetType(b, make([]Node8, 32)) +} + +type Node64 struct { + Value [64]uintptr + Left, Right *byte +} + +func BenchmarkSetTypeNode64(b *testing.B) { + benchSetType(b, new(Node64)) +} + +func BenchmarkSetTypeNode64Slice(b *testing.B) { + benchSetType(b, make([]Node64, 32)) +} + +type Node64Dead struct { + Left, Right *byte + Value [64]uintptr +} + +func BenchmarkSetTypeNode64Dead(b *testing.B) { + benchSetType(b, new(Node64Dead)) +} + +func BenchmarkSetTypeNode64DeadSlice(b *testing.B) { + benchSetType(b, make([]Node64Dead, 32)) +} + +type Node124 struct { + Value [124]uintptr + Left, Right *byte +} + +func BenchmarkSetTypeNode124(b *testing.B) { + benchSetType(b, new(Node124)) +} + +func BenchmarkSetTypeNode124Slice(b *testing.B) { + benchSetType(b, make([]Node124, 32)) +} + +type Node126 struct { + Value [126]uintptr + Left, Right *byte +} + +func BenchmarkSetTypeNode126(b *testing.B) { + benchSetType(b, new(Node126)) +} + +func BenchmarkSetTypeNode126Slice(b *testing.B) { + benchSetType(b, make([]Node126, 32)) +} + +type Node1024 struct { + Value [1024]uintptr + Left, Right *byte +} + +func BenchmarkSetTypeNode1024(b *testing.B) { + benchSetType(b, new(Node1024)) +} + +func BenchmarkSetTypeNode1024Slice(b *testing.B) { + benchSetType(b, make([]Node1024, 32)) +} + +func benchSetType(b *testing.B, x interface{}) { + v := reflect.ValueOf(x) + t := v.Type() + switch t.Kind() { + case reflect.Ptr: + b.SetBytes(int64(t.Elem().Size())) + case reflect.Slice: + b.SetBytes(int64(t.Elem().Size()) * int64(v.Len())) } - var p *Ptr2 - for i := 0; i < b.N; i++ { - p = &Ptr2{} - } - _ = p + b.ResetTimer() + runtime.BenchSetType(b.N, x) } func BenchmarkAllocation(b *testing.B) { From 6d8a147bef8ee28eb647db21ea91ecb823fa2480 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 28 Apr 2015 00:28:47 -0400 Subject: [PATCH 018/232] runtime: use 1-bit pointer bitmaps in type representation The type information in reflect.Type and the GC programs is now 1 bit per word, down from 2 bits. The in-memory unrolled type bitmap representation are now 1 bit per word, down from 4 bits. The conversion from the unrolled (now 1-bit) bitmap to the heap bitmap (still 4-bit) is not optimized. A followup CL will work on that, after the heap bitmap has been converted to 2-bit. The typeDead optimization, in which a special value denotes that there are no more pointers anywhere in the object, is lost in this CL. A followup CL will bring it back in the final form of heapBitsSetType. Change-Id: If61e67950c16a293b0b516a6fd9a1c755b6d5549 Reviewed-on: https://go-review.googlesource.com/9702 Reviewed-by: Austin Clements --- src/cmd/internal/gc/reflect.go | 80 +++++------------ src/cmd/internal/ld/data.go | 42 +++------ src/cmd/internal/obj/mgc0.go | 10 --- src/reflect/all_test.go | 36 ++++---- src/reflect/export_test.go | 4 +- src/reflect/type.go | 72 +++++++--------- src/runtime/export_test.go | 9 +- src/runtime/gcinfo_test.go | 14 +-- src/runtime/mbarrier.go | 86 ++++++++++--------- src/runtime/mbitmap.go | 152 ++++++++++++++------------------- 10 files changed, 204 insertions(+), 301 deletions(-) diff --git a/src/cmd/internal/gc/reflect.go b/src/cmd/internal/gc/reflect.go index 9979fe85fd..6ff9df2cfc 100644 --- a/src/cmd/internal/gc/reflect.go +++ b/src/cmd/internal/gc/reflect.go @@ -1430,11 +1430,7 @@ func usegcprog(t *Type) bool { // Calculate size of the unrolled GC mask. nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr) - size := nptr - if size%2 != 0 { - size *= 2 // repeated - } - size = size * obj.GcBits / 8 // 4 bits per word + size := (nptr + 7) / 8 // Decide whether to use unrolled GC mask or GC program. // We could use a more elaborate condition, but this seems to work well in practice. @@ -1445,7 +1441,7 @@ func usegcprog(t *Type) bool { return size > int64(2*Widthptr) } -// Generates sparse GC bitmask (4 bits per word). +// Generates GC bitmask (1 bit per word). func gengcmask(t *Type, gcmask []byte) { for i := int64(0); i < 16; i++ { gcmask[i] = 0 @@ -1454,40 +1450,14 @@ func gengcmask(t *Type, gcmask []byte) { return } - // Generate compact mask as stacks use. - xoffset := int64(0) - vec := bvalloc(2 * int32(Widthptr) * 8) + xoffset := int64(0) onebitwalktype1(t, &xoffset, vec) - // Unfold the mask for the GC bitmap format: - // 4 bits per word, 2 high bits encode pointer info. - pos := gcmask - nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr) - half := false - - // If number of words is odd, repeat the mask. - // This makes simpler handling of arrays in runtime. - var i int64 - var bits uint8 - for j := int64(0); j <= (nptr % 2); j++ { - for i = 0; i < nptr; i++ { - // convert 0=scalar / 1=pointer to GC bit encoding - if bvget(vec, int32(i)) == 0 { - bits = obj.BitsScalar - } else { - bits = obj.BitsPointer - } - bits <<= 2 - if half { - bits <<= 4 - } - pos[0] |= byte(bits) - half = !half - if !half { - pos = pos[1:] - } + for i := int64(0); i < nptr; i++ { + if bvget(vec, int32(i)) == 1 { + gcmask[i/8] |= 1 << (uint(i) % 8) } } } @@ -1496,7 +1466,7 @@ func gengcmask(t *Type, gcmask []byte) { type ProgGen struct { s *Sym datasize int32 - data [256 / obj.PointersPerByte]uint8 + data [256 / 8]uint8 ot int64 } @@ -1504,7 +1474,7 @@ func proggeninit(g *ProgGen, s *Sym) { g.s = s g.datasize = 0 g.ot = 0 - g.data = [256 / obj.PointersPerByte]uint8{} + g.data = [256 / 8]uint8{} } func proggenemit(g *ProgGen, v uint8) { @@ -1518,16 +1488,16 @@ func proggendataflush(g *ProgGen) { } proggenemit(g, obj.InsData) proggenemit(g, uint8(g.datasize)) - s := (g.datasize + obj.PointersPerByte - 1) / obj.PointersPerByte + s := (g.datasize + 7) / 8 for i := int32(0); i < s; i++ { proggenemit(g, g.data[i]) } g.datasize = 0 - g.data = [256 / obj.PointersPerByte]uint8{} + g.data = [256 / 8]uint8{} } func proggendata(g *ProgGen, d uint8) { - g.data[g.datasize/obj.PointersPerByte] |= d << uint((g.datasize%obj.PointersPerByte)*obj.BitsPerPointer) + g.data[g.datasize/8] |= d << uint(g.datasize%8) g.datasize++ if g.datasize == 255 { proggendataflush(g) @@ -1538,7 +1508,7 @@ func proggendata(g *ProgGen, d uint8) { func proggenskip(g *ProgGen, off int64, v int64) { for i := off; i < off+v; i++ { if (i % int64(Widthptr)) == 0 { - proggendata(g, obj.BitsScalar) + proggendata(g, 0) } } } @@ -1566,12 +1536,7 @@ func proggenfini(g *ProgGen) int64 { // Generates GC program for large types. func gengcprog(t *Type, pgc0 **Sym, pgc1 **Sym) { nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr) - size := nptr - if size%2 != 0 { - size *= 2 // repeated twice - } - size = size * obj.PointersPerByte / 8 // 4 bits per word - size++ // unroll flag in the beginning, used by runtime (see runtime.markallocated) + size := nptr + 1 // unroll flag in the beginning, used by runtime (see runtime.markallocated) // emity space in BSS for unrolled program *pgc0 = nil @@ -1623,26 +1588,25 @@ func gengcprog1(g *ProgGen, t *Type, xoffset *int64) { TFUNC, TCHAN, TMAP: - proggendata(g, obj.BitsPointer) + proggendata(g, 1) *xoffset += t.Width case TSTRING: - proggendata(g, obj.BitsPointer) - proggendata(g, obj.BitsScalar) + proggendata(g, 1) + proggendata(g, 0) *xoffset += t.Width // Assuming IfacePointerOnly=1. case TINTER: - proggendata(g, obj.BitsPointer) - - proggendata(g, obj.BitsPointer) + proggendata(g, 1) + proggendata(g, 1) *xoffset += t.Width case TARRAY: if Isslice(t) { - proggendata(g, obj.BitsPointer) - proggendata(g, obj.BitsScalar) - proggendata(g, obj.BitsScalar) + proggendata(g, 1) + proggendata(g, 0) + proggendata(g, 0) } else { t1 := t.Type if t1.Width == 0 { @@ -1656,7 +1620,7 @@ func gengcprog1(g *ProgGen, t *Type, xoffset *int64) { n := t.Width n -= -*xoffset & (int64(Widthptr) - 1) // skip to next ptr boundary proggenarray(g, (n+int64(Widthptr)-1)/int64(Widthptr)) - proggendata(g, obj.BitsScalar) + proggendata(g, 0) proggenarrayend(g) *xoffset -= (n+int64(Widthptr)-1)/int64(Widthptr)*int64(Widthptr) - t.Width } else { diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go index 3194bd568e..676c8856de 100644 --- a/src/cmd/internal/ld/data.go +++ b/src/cmd/internal/ld/data.go @@ -1032,7 +1032,7 @@ func maxalign(s *LSym, type_ int) int32 { type ProgGen struct { s *LSym datasize int32 - data [256 / obj.PointersPerByte]uint8 + data [256 / 8]uint8 pos int64 } @@ -1040,7 +1040,7 @@ func proggeninit(g *ProgGen, s *LSym) { g.s = s g.datasize = 0 g.pos = 0 - g.data = [256 / obj.PointersPerByte]uint8{} + g.data = [256 / 8]uint8{} } func proggenemit(g *ProgGen, v uint8) { @@ -1054,16 +1054,16 @@ func proggendataflush(g *ProgGen) { } proggenemit(g, obj.InsData) proggenemit(g, uint8(g.datasize)) - s := (g.datasize + obj.PointersPerByte - 1) / obj.PointersPerByte + s := (g.datasize + 7) / 8 for i := int32(0); i < s; i++ { proggenemit(g, g.data[i]) } g.datasize = 0 - g.data = [256 / obj.PointersPerByte]uint8{} + g.data = [256 / 8]uint8{} } func proggendata(g *ProgGen, d uint8) { - g.data[g.datasize/obj.PointersPerByte] |= d << uint((g.datasize%obj.PointersPerByte)*obj.BitsPerPointer) + g.data[g.datasize/8] |= d << uint(g.datasize%8) g.datasize++ if g.datasize == 255 { proggendataflush(g) @@ -1074,7 +1074,7 @@ func proggendata(g *ProgGen, d uint8) { func proggenskip(g *ProgGen, off int64, v int64) { for i := off; i < off+v; i++ { if (i % int64(Thearch.Ptrsize)) == 0 { - proggendata(g, obj.BitsScalar) + proggendata(g, 0) } } } @@ -1119,35 +1119,18 @@ func proggenaddsym(g *ProgGen, s *LSym) { // Leave debugging the SDATA issue for the Go rewrite. if s.Gotype == nil && s.Size >= int64(Thearch.Ptrsize) && s.Name[0] != '.' { - // conservative scan Diag("missing Go type information for global symbol: %s size %d", s.Name, int(s.Size)) + return + } - if (s.Size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { - Diag("proggenaddsym: unaligned conservative symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) - } - size := (s.Size + int64(Thearch.Ptrsize) - 1) / int64(Thearch.Ptrsize) * int64(Thearch.Ptrsize) - if size < int64(32*Thearch.Ptrsize) { - // Emit small symbols as data. - for i := int64(0); i < size/int64(Thearch.Ptrsize); i++ { - proggendata(g, obj.BitsPointer) - } - } else { - // Emit large symbols as array. - proggenarray(g, size/int64(Thearch.Ptrsize)) - - proggendata(g, obj.BitsPointer) - proggenarrayend(g) - } - - g.pos = s.Value + size - } else if s.Gotype == nil || decodetype_noptr(s.Gotype) != 0 || s.Size < int64(Thearch.Ptrsize) || s.Name[0] == '.' { + if s.Gotype == nil || decodetype_noptr(s.Gotype) != 0 || s.Size < int64(Thearch.Ptrsize) || s.Name[0] == '.' { // no scan if s.Size < int64(32*Thearch.Ptrsize) { // Emit small symbols as data. // This case also handles unaligned and tiny symbols, so tread carefully. for i := s.Value; i < s.Value+s.Size; i++ { if (i % int64(Thearch.Ptrsize)) == 0 { - proggendata(g, obj.BitsScalar) + proggendata(g, 0) } } } else { @@ -1156,7 +1139,7 @@ func proggenaddsym(g *ProgGen, s *LSym) { Diag("proggenaddsym: unaligned noscan symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) } proggenarray(g, s.Size/int64(Thearch.Ptrsize)) - proggendata(g, obj.BitsScalar) + proggendata(g, 0) proggenarrayend(g) } @@ -1183,7 +1166,8 @@ func proggenaddsym(g *ProgGen, s *LSym) { Diag("proggenaddsym: unaligned gcmask symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) } for i := int64(0); i < size; i += int64(Thearch.Ptrsize) { - proggendata(g, uint8((mask[i/int64(Thearch.Ptrsize)/2]>>uint64((i/int64(Thearch.Ptrsize)%2)*4+2))&obj.BitsMask)) + word := uint(i / int64(Thearch.Ptrsize)) + proggendata(g, (mask[word/8]>>(word%8))&1) } g.pos = s.Value + size } diff --git a/src/cmd/internal/obj/mgc0.go b/src/cmd/internal/obj/mgc0.go index 2407deaf32..a385d607bb 100644 --- a/src/cmd/internal/obj/mgc0.go +++ b/src/cmd/internal/obj/mgc0.go @@ -21,16 +21,6 @@ package obj // Used by cmd/gc. -const ( - GcBits = 4 - BitsPerPointer = 2 - BitsDead = 0 - BitsScalar = 1 - BitsPointer = 2 - BitsMask = 3 - PointersPerByte = 8 / BitsPerPointer -) - const ( InsData = 1 + iota InsArray diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go index 877b2efd84..373583d471 100644 --- a/src/reflect/all_test.go +++ b/src/reflect/all_test.go @@ -4388,7 +4388,7 @@ func TestCallGC(t *testing.T) { type funcLayoutTest struct { rcvr, t Type size, argsize, retOffset uintptr - stack []byte + stack []byte // pointer bitmap: 1 is pointer, 0 is scalar (or uninitialized) gc []byte } @@ -4399,7 +4399,7 @@ func init() { var naclExtra []byte if runtime.GOARCH == "amd64p32" { argAlign = 2 * PtrSize - naclExtra = append(naclExtra, BitsScalar) + naclExtra = append(naclExtra, 0) } roundup := func(x uintptr, a uintptr) uintptr { return (x + a - 1) / a * a @@ -4412,17 +4412,17 @@ func init() { 6 * PtrSize, 4 * PtrSize, 4 * PtrSize, - []byte{BitsPointer, BitsScalar, BitsPointer}, - []byte{BitsPointer, BitsScalar, BitsPointer, BitsScalar, BitsPointer, BitsScalar}, + []byte{1, 0, 1}, + []byte{1, 0, 1, 0, 1, 0}, }) var r, s []byte if PtrSize == 4 { - r = []byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer} - s = append([]byte{BitsScalar, BitsScalar, BitsScalar, BitsPointer, BitsScalar}, naclExtra...) + r = []byte{0, 0, 0, 1} + s = append([]byte{0, 0, 0, 1, 0}, naclExtra...) } else { - r = []byte{BitsScalar, BitsScalar, BitsPointer} - s = []byte{BitsScalar, BitsScalar, BitsPointer, BitsScalar} + r = []byte{0, 0, 1} + s = []byte{0, 0, 1, 0} } funcLayoutTests = append(funcLayoutTests, funcLayoutTest{ @@ -4442,8 +4442,8 @@ func init() { 4 * PtrSize, 4 * PtrSize, 4 * PtrSize, - []byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer}, - []byte{BitsPointer, BitsScalar, BitsPointer, BitsPointer}, + []byte{1, 0, 1, 1}, + []byte{1, 0, 1, 1}, }) type S struct { @@ -4457,8 +4457,8 @@ func init() { 4 * PtrSize, 4 * PtrSize, 4 * PtrSize, - []byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer}, - []byte{BitsScalar, BitsScalar, BitsPointer, BitsPointer}, + []byte{0, 0, 1, 1}, + []byte{0, 0, 1, 1}, }) funcLayoutTests = append(funcLayoutTests, @@ -4468,8 +4468,8 @@ func init() { roundup(3*PtrSize, argAlign), 3 * PtrSize, roundup(3*PtrSize, argAlign), - []byte{BitsPointer, BitsScalar, BitsPointer}, - append([]byte{BitsPointer, BitsScalar, BitsPointer}, naclExtra...), + []byte{1, 0, 1}, + append([]byte{1, 0, 1}, naclExtra...), }) funcLayoutTests = append(funcLayoutTests, @@ -4480,7 +4480,7 @@ func init() { PtrSize, roundup(PtrSize, argAlign), []byte{}, - append([]byte{BitsScalar}, naclExtra...), + append([]byte{0}, naclExtra...), }) funcLayoutTests = append(funcLayoutTests, @@ -4491,7 +4491,7 @@ func init() { 0, 0, []byte{}, - []byte{BitsScalar}, + []byte{0}, }) funcLayoutTests = append(funcLayoutTests, @@ -4501,8 +4501,8 @@ func init() { 2 * PtrSize, 2 * PtrSize, 2 * PtrSize, - []byte{BitsPointer}, - []byte{BitsPointer, BitsScalar}, + []byte{1}, + []byte{1, 0}, // Note: this one is tricky, as the receiver is not a pointer. But we // pass the receiver by reference to the autogenerated pointer-receiver // version of the function. diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go index c89e9c1298..6748eba3d1 100644 --- a/src/reflect/export_test.go +++ b/src/reflect/export_test.go @@ -18,8 +18,6 @@ func IsRO(v Value) bool { var CallGC = &callGC const PtrSize = ptrSize -const BitsPointer = bitsPointer -const BitsScalar = bitsScalar func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, stack []byte, gc []byte, ptrs bool) { var ft *rtype @@ -38,7 +36,7 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr, } gcdata := (*[1000]byte)(ft.gc[0]) for i := uintptr(0); i < ft.size/ptrSize; i++ { - gc = append(gc, gcdata[i/2]>>(i%2*4+2)&3) + gc = append(gc, gcdata[i/8]>>(i%8)&1) } ptrs = ft.kind&kindNoPointers == 0 return diff --git a/src/reflect/type.go b/src/reflect/type.go index 5315bd3971..5a43805626 100644 --- a/src/reflect/type.go +++ b/src/reflect/type.go @@ -1701,14 +1701,14 @@ func (gc *gcProg) appendProg(t *rtype) { default: panic("reflect: non-pointer type marked as having pointers") case Ptr, UnsafePointer, Chan, Func, Map: - gc.appendWord(bitsPointer) + gc.appendWord(1) case Slice: - gc.appendWord(bitsPointer) - gc.appendWord(bitsScalar) - gc.appendWord(bitsScalar) + gc.appendWord(1) + gc.appendWord(0) + gc.appendWord(0) case String: - gc.appendWord(bitsPointer) - gc.appendWord(bitsScalar) + gc.appendWord(1) + gc.appendWord(0) case Array: c := t.Len() e := t.Elem().common() @@ -1716,8 +1716,8 @@ func (gc *gcProg) appendProg(t *rtype) { gc.appendProg(e) } case Interface: - gc.appendWord(bitsPointer) - gc.appendWord(bitsPointer) + gc.appendWord(1) + gc.appendWord(1) case Struct: oldsize := gc.size c := t.NumField() @@ -1737,13 +1737,12 @@ func (gc *gcProg) appendWord(v byte) { panic("reflect: unaligned GC program") } nptr := gc.size / ptrsize - for uintptr(len(gc.gc)) < nptr/2+1 { - gc.gc = append(gc.gc, 0x44) // BitsScalar + for uintptr(len(gc.gc)) <= nptr/8 { + gc.gc = append(gc.gc, 0) } - gc.gc[nptr/2] &= ^(3 << ((nptr%2)*4 + 2)) - gc.gc[nptr/2] |= v << ((nptr%2)*4 + 2) + gc.gc[nptr/8] |= v << (nptr % 8) gc.size += ptrsize - if v == bitsPointer { + if v == 1 { gc.hasPtr = true } } @@ -1758,33 +1757,20 @@ func (gc *gcProg) finalize() (unsafe.Pointer, bool) { ptrsize := unsafe.Sizeof(uintptr(0)) gc.align(ptrsize) nptr := gc.size / ptrsize - for uintptr(len(gc.gc)) < nptr/2+1 { - gc.gc = append(gc.gc, 0x44) // BitsScalar - } - // If number of words is odd, repeat the mask twice. - // Compiler does the same. - if nptr%2 != 0 { - for i := uintptr(0); i < nptr; i++ { - gc.appendWord(extractGCWord(gc.gc, i)) - } + for uintptr(len(gc.gc)) <= nptr/8 { + gc.gc = append(gc.gc, 0) } return unsafe.Pointer(&gc.gc[0]), gc.hasPtr } func extractGCWord(gc []byte, i uintptr) byte { - return (gc[i/2] >> ((i%2)*4 + 2)) & 3 + return gc[i/8] >> (i % 8) & 1 } func (gc *gcProg) align(a uintptr) { gc.size = align(gc.size, a) } -// These constants must stay in sync with ../runtime/mbitmap.go. -const ( - bitsScalar = 1 - bitsPointer = 2 -) - // Make sure these routines stay in sync with ../../runtime/hashmap.go! // These types exist only for GC, so we only fill out GC relevant info. // Currently, that's just size and the GC program. We also fill in string @@ -1814,7 +1800,7 @@ func bucketOf(ktyp, etyp *rtype) *rtype { var gc gcProg // topbits for i := 0; i < int(bucketSize*unsafe.Sizeof(uint8(0))/ptrsize); i++ { - gc.append(bitsScalar) + gc.append(0) } // keys for i := 0; i < bucketSize; i++ { @@ -1825,10 +1811,10 @@ func bucketOf(ktyp, etyp *rtype) *rtype { gc.appendProg(etyp) } // overflow - gc.append(bitsPointer) + gc.append(1) ptrdata := gc.size if runtime.GOARCH == "amd64p32" { - gc.append(bitsScalar) + gc.append(0) } b := new(rtype) @@ -2058,16 +2044,16 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin // space no matter how big they actually are. if ifaceIndir(rcvr) { // we pass a pointer to the receiver. - gc.append(bitsPointer) - stack.append2(bitsPointer) + gc.append(1) + stack.append2(1) } else if rcvr.pointers() { // rcvr is a one-word pointer object. Its gc program // is just what we need here. - gc.append(bitsPointer) - stack.append2(bitsPointer) + gc.append(1) + stack.append2(1) } else { - gc.append(bitsScalar) - stack.append2(bitsScalar) + gc.append(0) + stack.append2(0) } offset += ptrSize } @@ -2154,17 +2140,17 @@ func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) { case Chan, Func, Map, Ptr, Slice, String, UnsafePointer: // 1 pointer at start of representation for bv.n < 2*uint32(*offset/uintptr(ptrSize)) { - bv.append2(bitsScalar) + bv.append2(0) } - bv.append2(bitsPointer) + bv.append2(1) case Interface: // 2 pointers for bv.n < 2*uint32(*offset/uintptr(ptrSize)) { - bv.append2(bitsScalar) + bv.append2(0) } - bv.append2(bitsPointer) - bv.append2(bitsPointer) + bv.append2(1) + bv.append2(1) case Array: // repeat inner type diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 1efe24c61a..817622abd0 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -76,15 +76,8 @@ func ParForIters(desc *ParFor, tid uint32) (uint32, uint32) { } func GCMask(x interface{}) (ret []byte) { - e := (*eface)(unsafe.Pointer(&x)) - s := (*slice)(unsafe.Pointer(&ret)) systemstack(func() { - var len uintptr - var a *byte - getgcmask(e.data, e._type, &a, &len) - s.array = unsafe.Pointer(a) - s.len = int(len) - s.cap = s.len + ret = getgcmask(x) }) return } diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go index 66b0353f08..b4ab9134aa 100644 --- a/src/runtime/gcinfo_test.go +++ b/src/runtime/gcinfo_test.go @@ -28,13 +28,13 @@ func TestGCInfo(t *testing.T) { verifyGCInfo(t, "data eface", &dataEface, infoEface) verifyGCInfo(t, "data iface", &dataIface, infoIface) - verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr) - verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar) - verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct()) - verifyGCInfo(t, "stack string", new(string), infoString) - verifyGCInfo(t, "stack slice", new([]string), infoSlice) - verifyGCInfo(t, "stack eface", new(interface{}), infoEface) - verifyGCInfo(t, "stack iface", new(Iface), infoIface) + verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), nonStackInfo(infoScalarPtr)) + verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), nonStackInfo(infoPtrScalar)) + verifyGCInfo(t, "stack BigStruct", new(BigStruct), nonStackInfo(infoBigStruct())) + verifyGCInfo(t, "stack string", new(string), nonStackInfo(infoString)) + verifyGCInfo(t, "stack slice", new([]string), nonStackInfo(infoSlice)) + verifyGCInfo(t, "stack eface", new(interface{}), nonStackInfo(infoEface)) + verifyGCInfo(t, "stack iface", new(Iface), nonStackInfo(infoIface)) for i := 0; i < 10; i++ { verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), infoScalarPtr) diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index eb5881707b..4162483ade 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -223,29 +223,25 @@ func typedmemmove(typ *_type, dst, src unsafe.Pointer) { } systemstack(func() { - mask := typeBitmapInHeapBitmapFormat(typ) + dst := dst // make local copies + src := src nptr := typ.size / ptrSize - for i := uintptr(0); i < nptr; i += 2 { - bits := mask[i/2] - if (bits>>2)&typeMask == typePointer { - writebarrierptr((*uintptr)(dst), *(*uintptr)(src)) - } else { - *(*uintptr)(dst) = *(*uintptr)(src) + i := uintptr(0) + Copy: + for _, bits := range ptrBitmapForType(typ) { + for j := 0; j < 8; j++ { + if bits&1 != 0 { + writebarrierptr((*uintptr)(dst), *(*uintptr)(src)) + } else { + *(*uintptr)(dst) = *(*uintptr)(src) + } + if i++; i >= nptr { + break Copy + } + dst = add(dst, ptrSize) + src = add(src, ptrSize) + bits >>= 1 } - // TODO(rsc): The noescape calls should be unnecessary. - dst = add(noescape(dst), ptrSize) - src = add(noescape(src), ptrSize) - if i+1 == nptr { - break - } - bits >>= 4 - if (bits>>2)&typeMask == typePointer { - writebarrierptr((*uintptr)(dst), *(*uintptr)(src)) - } else { - *(*uintptr)(dst) = *(*uintptr)(src) - } - dst = add(noescape(dst), ptrSize) - src = add(noescape(src), ptrSize) } }) } @@ -274,18 +270,25 @@ func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size off += frag } - mask := typeBitmapInHeapBitmapFormat(typ) + mask := ptrBitmapForType(typ) nptr := (off + size) / ptrSize - for i := uintptr(off / ptrSize); i < nptr; i++ { - bits := mask[i/2] >> ((i & 1) << 2) - if (bits>>2)&typeMask == typePointer { - writebarrierptr((*uintptr)(dst), *(*uintptr)(src)) - } else { - *(*uintptr)(dst) = *(*uintptr)(src) + i := uintptr(off / ptrSize) +Copy: + for { + bits := mask[i/8] >> (i % 8) + for j := i % 8; j < 8; j++ { + if bits&1 != 0 { + writebarrierptr((*uintptr)(dst), *(*uintptr)(src)) + } else { + *(*uintptr)(dst) = *(*uintptr)(src) + } + if i++; i >= nptr { + break Copy + } + dst = add(dst, ptrSize) + src = add(src, ptrSize) + bits >>= 1 } - // TODO(rsc): The noescape calls should be unnecessary. - dst = add(noescape(dst), ptrSize) - src = add(noescape(src), ptrSize) } size &= ptrSize - 1 if size > 0 { @@ -307,18 +310,25 @@ func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uin } systemstack(func() { - mask := typeBitmapInHeapBitmapFormat(typ) + mask := ptrBitmapForType(typ) // retoffset is known to be pointer-aligned (at least). // TODO(rsc): The noescape call should be unnecessary. dst := add(noescape(frame), retoffset) nptr := framesize / ptrSize - for i := uintptr(retoffset / ptrSize); i < nptr; i++ { - bits := mask[i/2] >> ((i & 1) << 2) - if (bits>>2)&typeMask == typePointer { - writebarrierptr_nostore((*uintptr)(dst), *(*uintptr)(dst)) + i := uintptr(retoffset / ptrSize) + Copy: + for { + bits := mask[i/8] >> (i % 8) + for j := i % 8; j < 8; j++ { + if bits&1 != 0 { + writebarrierptr_nostore((*uintptr)(dst), *(*uintptr)(dst)) + } + if i++; i >= nptr { + break Copy + } + dst = add(dst, ptrSize) + bits >>= 1 } - // TODO(rsc): The noescape call should be unnecessary. - dst = add(noescape(dst), ptrSize) } }) } diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index f0c7520e38..cfdd259371 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -446,25 +446,23 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // and storing type info in the GC bitmap. h := heapBitsForAddr(x) - var ti, te uintptr var ptrmask *uint8 if size == ptrSize { // It's one word and it has pointers, it must be a pointer. // The bitmap byte is shared with the one-word object // next to it, and concurrent GC might be marking that // object, so we must use an atomic update. + // TODO(rsc): It may make sense to set all the pointer bits + // when initializing the span, and then the atomicor8 here + // goes away - heapBitsSetType would be a no-op + // in that case. atomicor8(h.bitp, typePointer<<(typeShift+h.shift)) return } if typ.kind&kindGCProg != 0 { nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize - masksize := nptr - if masksize%2 != 0 { - masksize *= 2 // repeated - } - const typeBitsPerByte = 8 / typeBitsWidth - masksize = masksize * typeBitsPerByte / 8 // 4 bits per word - masksize++ // unroll flag in the beginning + masksize := (nptr + 7) / 8 + masksize++ // unroll flag in the beginning if masksize > maxGCMask && typ.gc[1] != 0 { // write barriers have not been updated to deal with this case yet. throw("maxGCMask too small for now") @@ -490,64 +488,55 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } else { ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask } - if size == 2*ptrSize { - // h.shift is 0 for all sizes > ptrSize. - *h.bitp = *ptrmask - return - } - te = uintptr(typ.size) / ptrSize - // If the type occupies odd number of words, its mask is repeated. - if te%2 == 0 { - te /= 2 - } - // Copy pointer bitmask into the bitmap. - // TODO(rlh): add comment addressing the following concerns: - // If size > 2*ptrSize, is x guaranteed to be at least 2*ptrSize-aligned? - // And if type occupies and odd number of words, why are we only going through half - // of ptrmask and why don't we have to shift everything by 4 on odd iterations? - for i := uintptr(0); i < dataSize; i += 2 * ptrSize { - v := *(*uint8)(add(unsafe.Pointer(ptrmask), ti)) - ti++ - if ti == te { - ti = 0 - } - if i+ptrSize == dataSize { - v &^= typeMask << (4 + typeShift) + // Copy from 1-bit ptrmask into 4-bit bitmap. + elemSize := typ.size + var v uint32 // pending byte of 4-bit bitmap; uint32 for better code gen + nv := 0 // number of bits added to v + for i := uintptr(0); i < dataSize; i += elemSize { + // At each word, b holds the pending bits from the 1-bit bitmap, + // with a sentinel 1 bit above all the actual bits. + // When b == 1, that means it is out of bits and needs to be refreshed. + // *(p+1) is the next byte to read. + p := ptrmask + b := uint32(*p) | 0x100 + for j := uintptr(0); j < elemSize; j += ptrSize { + if b == 1 { + p = addb(p, 1) + b = uint32(*p) | 0x100 + } + // b&1 is 1 for pointer, 0 for scalar. + // We want typePointer (2) or typeScalar (1), so add 1. + v |= ((b & 1) + 1) << (uint(nv) + typeShift) + b >>= 1 + if nv += heapBitsWidth; nv == 8 { + *h.bitp = uint8(v) + h.bitp = subtractb(h.bitp, 1) + v = 0 + nv = 0 + } } + } - *h.bitp = v + // Finish final byte of bitmap and mark next word (if any) with typeDead (0) + if nv != 0 { + *h.bitp = uint8(v) h.bitp = subtractb(h.bitp, 1) - } - if dataSize%(2*ptrSize) == 0 && dataSize < size { - // Mark the word after last object's word as typeDead. + } else if dataSize < size { *h.bitp = 0 } } -// typeBitmapInHeapBitmapFormat returns a bitmap holding -// the type bits for the type typ, but expanded into heap bitmap format -// to make it easier to copy them into the heap bitmap. -// TODO(rsc): Change clients to use the type bitmap format instead, -// which can be stored more densely (especially if we drop to 1 bit per pointer). -// -// To make it easier to replicate the bits when filling out the heap -// bitmap for an array of typ, if typ holds an odd number of words -// (meaning the heap bitmap would stop halfway through a byte), -// typeBitmapInHeapBitmapFormat returns the bitmap for two instances -// of typ in a row. -// TODO(rsc): Remove doubling. -func typeBitmapInHeapBitmapFormat(typ *_type) []uint8 { +// ptrBitmapForType returns a bitmap indicating where pointers are +// in the memory representation of the type typ. +// The bit x[i/8]&(1<<(i%8)) is 1 if the i'th word in a value of type typ +// is a pointer. +func ptrBitmapForType(typ *_type) []uint8 { var ptrmask *uint8 nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize if typ.kind&kindGCProg != 0 { - masksize := nptr - if masksize%2 != 0 { - masksize *= 2 // repeated - } - const typeBitsPerByte = 8 / typeBitsWidth - masksize = masksize * typeBitsPerByte / 8 // 4 bits per word - masksize++ // unroll flag in the beginning + masksize := (nptr + 7) / 8 + masksize++ // unroll flag in the beginning if masksize > maxGCMask && typ.gc[1] != 0 { // write barriers have not been updated to deal with this case yet. throw("maxGCMask too small for now") @@ -565,7 +554,7 @@ func typeBitmapInHeapBitmapFormat(typ *_type) []uint8 { } else { ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask } - return (*[1 << 30]byte)(unsafe.Pointer(ptrmask))[:(nptr+1)/2] + return (*[1 << 30]byte)(unsafe.Pointer(ptrmask))[:(nptr+7)/8] } // GC type info programs @@ -625,10 +614,7 @@ func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace, sparse bool) prog = addb(prog, 1) p := (*[1 << 30]byte)(unsafe.Pointer(prog)) for i := 0; i < siz; i++ { - const typeBitsPerByte = 8 / typeBitsWidth - v := p[i/typeBitsPerByte] - v >>= (uint(i) % typeBitsPerByte) * typeBitsWidth - v &= typeMask + v := p[i/8] >> (uint(i) % 8) & 1 if inplace { // Store directly into GC bitmap. h := heapBitsForAddr(uintptr(unsafe.Pointer(&mask[pos]))) @@ -639,18 +625,18 @@ func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace, sparse bool) } pos += ptrSize } else if sparse { + throw("sparse") // 4-bits per word, type bits in high bits v <<= (pos % 8) + typeShift mask[pos/8] |= v pos += heapBitsWidth } else { // 1 bit per word, for data/bss bitmap - v >>= 1 // convert typePointer to 1, others to 0 mask[pos/8] |= v << (pos % 8) pos++ } } - prog = addb(prog, round(uintptr(siz)*typeBitsWidth, 8)/8) + prog = addb(prog, (uintptr(siz)+7)/8) case insArray: prog = (*byte)(add(unsafe.Pointer(prog), 1)) @@ -675,7 +661,7 @@ func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace, sparse bool) } } -// Unrolls GC program prog for data/bss, returns dense GC mask. +// Unrolls GC program prog for data/bss, returns 1-bit GC mask. func unrollglobgcprog(prog *byte, size uintptr) bitvector { masksize := round(round(size, ptrSize)/ptrSize, 8) / 8 mask := (*[1 << 30]byte)(persistentalloc(masksize+1, 0, &memstats.gc_sys)) @@ -721,16 +707,10 @@ func unrollgcprog_m(typ *_type) { if *mask == 0 { pos := uintptr(8) // skip the unroll flag prog := (*byte)(unsafe.Pointer(uintptr(typ.gc[1]))) - prog = unrollgcprog1(mask, prog, &pos, false, true) + prog = unrollgcprog1(mask, prog, &pos, false, false) if *prog != insEnd { throw("unrollgcprog: program does not end with insEnd") } - if typ.size/ptrSize%2 != 0 { - // repeat the program - prog := (*byte)(unsafe.Pointer(uintptr(typ.gc[1]))) - unrollgcprog1(mask, prog, &pos, false, true) - } - // atomic way to say mask[0] = 1 atomicor8(mask, 1) } @@ -749,21 +729,21 @@ func getgcmaskcb(frame *stkframe, ctxt unsafe.Pointer) bool { } // Returns GC type info for object p for testing. -func getgcmask(p unsafe.Pointer, t *_type, mask **byte, len *uintptr) { - *mask = nil - *len = 0 - - // data +func getgcmask(ep interface{}) (mask []byte) { + e := *(*eface)(unsafe.Pointer(&ep)) + p := e.data + t := e._type + // data or bss for datap := &firstmoduledata; datap != nil; datap = datap.next { + // data if datap.data <= uintptr(p) && uintptr(p) < datap.edata { n := (*ptrtype)(unsafe.Pointer(t)).elem.size - *len = n / ptrSize - *mask = &make([]byte, *len)[0] + mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { off := (uintptr(p) + i - datap.data) / ptrSize bits := (*addb(datap.gcdatamask.bytedata, off/8) >> (off % 8)) & 1 bits += 1 // convert 1-bit to 2-bit - *addb(*mask, i/ptrSize) = bits + mask[i/ptrSize] = bits } return } @@ -771,13 +751,12 @@ func getgcmask(p unsafe.Pointer, t *_type, mask **byte, len *uintptr) { // bss if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss { n := (*ptrtype)(unsafe.Pointer(t)).elem.size - *len = n / ptrSize - *mask = &make([]byte, *len)[0] + mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { off := (uintptr(p) + i - datap.bss) / ptrSize bits := (*addb(datap.gcbssmask.bytedata, off/8) >> (off % 8)) & 1 bits += 1 // convert 1-bit to 2-bit - *addb(*mask, i/ptrSize) = bits + mask[i/ptrSize] = bits } return } @@ -787,11 +766,10 @@ func getgcmask(p unsafe.Pointer, t *_type, mask **byte, len *uintptr) { var n uintptr var base uintptr if mlookup(uintptr(p), &base, &n, nil) != 0 { - *len = n / ptrSize - *mask = &make([]byte, *len)[0] + mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { bits := heapBitsForAddr(base + i).typeBits() - *addb(*mask, i/ptrSize) = bits + mask[i/ptrSize] = bits } return } @@ -821,13 +799,13 @@ func getgcmask(p unsafe.Pointer, t *_type, mask **byte, len *uintptr) { bv := stackmapdata(stkmap, pcdata) size := uintptr(bv.n) * ptrSize n := (*ptrtype)(unsafe.Pointer(t)).elem.size - *len = n / ptrSize - *mask = &make([]byte, *len)[0] + mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { off := (uintptr(p) + i - frame.varp + size) / ptrSize bits := (*addb(bv.bytedata, off/8) >> (off % 8)) & 1 bits += 1 // convert 1-bit to 2-bit - *addb(*mask, i/ptrSize) = bits + mask[i/ptrSize] = bits } } + return } From 0234dfd493b9bb009728d60658aa0ccdd2463c09 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 4 May 2015 10:19:24 -0400 Subject: [PATCH 019/232] runtime: use 2-bit heap bitmap (in place of 4-bit) Previous CLs changed the representation of the non-heap type bitmaps to be 1-bit bitmaps (pointer or not). Before this CL, the heap bitmap stored a 2-bit type for each word and a mark bit and checkmark bit for the first word of the object. (There used to be additional per-word bits.) Reduce heap bitmap to 2-bit, with 1 dedicated to pointer or not, and the other used for mark, checkmark, and "keep scanning forward to find pointers in this object." See comments for details. This CL replaces heapBitsSetType with very slow but obviously correct code. A followup CL will optimize it. (Spoiler: the new code is faster than Go 1.4 was.) Change-Id: I999577a133f3cfecacebdec9cdc3573c235c7fb9 Reviewed-on: https://go-review.googlesource.com/9703 Reviewed-by: Rick Hudson Reviewed-by: Austin Clements --- src/runtime/gcinfo_test.go | 23 +- src/runtime/heapdump.go | 7 +- src/runtime/mbitmap.go | 487 +++++++++++++++++++------------------ src/runtime/mgcmark.go | 34 ++- src/runtime/stack1.go | 6 + 5 files changed, 292 insertions(+), 265 deletions(-) diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go index b4ab9134aa..dd5c25e0b1 100644 --- a/src/runtime/gcinfo_test.go +++ b/src/runtime/gcinfo_test.go @@ -10,6 +10,12 @@ import ( "testing" ) +const ( + typeScalar = 0 + typePointer = 1 + typeDead = 255 +) + // TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info. func TestGCInfo(t *testing.T) { verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr) @@ -37,7 +43,9 @@ func TestGCInfo(t *testing.T) { verifyGCInfo(t, "stack iface", new(Iface), nonStackInfo(infoIface)) for i := 0; i < 10; i++ { + verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), infoPtr10) verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), infoScalarPtr) + verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), infoScalarPtr4) verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), infoPtrScalar) verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), infoBigStruct()) verifyGCInfo(t, "heap string", escape(new(string)), infoString) @@ -78,18 +86,7 @@ func escape(p interface{}) interface{} { return p } -const ( - typeDead = iota - typeScalar - typePointer -) - -const ( - BitsString = iota // unused - BitsSlice // unused - BitsIface - BitsEface -) +var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer} type ScalarPtr struct { q int @@ -102,6 +99,8 @@ type ScalarPtr struct { var infoScalarPtr = []byte{typeScalar, typePointer, typeScalar, typePointer, typeScalar, typePointer} +var infoScalarPtr4 = append(append(append(append([]byte(nil), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...), infoScalarPtr...) + type PtrScalar struct { q *int w int diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go index e18aa79164..0add63acb4 100644 --- a/src/runtime/heapdump.go +++ b/src/runtime/heapdump.go @@ -730,14 +730,13 @@ func makeheapobjbv(p uintptr, size uintptr) bitvector { i := uintptr(0) hbits := heapBitsForAddr(p) for ; i < nptr; i++ { - bits := hbits.typeBits() - if bits == typeDead { + if i >= 2 && !hbits.isMarked() { break // end of object } - hbits = hbits.next() - if bits == typePointer { + if hbits.isPointer() { tmpbuf[i/8] |= 1 << (i % 8) } + hbits = hbits.next() } return bitvector{int32(i), &tmpbuf[0]} } diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index cfdd259371..dea6879adc 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -6,48 +6,36 @@ // // Stack, data, and bss bitmaps // -// Not handled in this file, but worth mentioning: stack frames and global data -// in the data and bss sections are described by 1-bit bitmaps in which 0 means -// scalar or uninitialized or dead and 1 means pointer to visit during GC. -// -// Comparing this 1-bit form with the 2-bit form described below, 0 represents -// both the 2-bit 00 and 01, while 1 represents the 2-bit 10. -// Therefore conversions between the two (until the 2-bit form is gone) -// can be done by x>>1 for 2-bit to 1-bit and x+1 for 1-bit to 2-bit. -// -// Type bitmaps -// -// Types that aren't too large -// record information about the layout of their memory words using a type bitmap. -// The bitmap holds two bits for each pointer-sized word. The two-bit values are: -// -// 00 - typeDead: not a pointer, and no pointers in the rest of the object -// 01 - typeScalar: not a pointer -// 10 - typePointer: a pointer that GC should trace -// 11 - unused -// -// typeDead only appears in type bitmaps in Go type descriptors -// and in type bitmaps embedded in the heap bitmap (see below). +// Stack frames and global variables in the data and bss sections are described +// by 1-bit bitmaps in which 0 means uninteresting and 1 means live pointer +// to be visited during GC. // // Heap bitmap // // The allocated heap comes from a subset of the memory in the range [start, used), // where start == mheap_.arena_start and used == mheap_.arena_used. -// The heap bitmap comprises 4 bits for each pointer-sized word in that range, +// The heap bitmap comprises 2 bits for each pointer-sized word in that range, // stored in bytes indexed backward in memory from start. -// That is, the byte at address start-1 holds the 4-bit entries for the two words -// start, start+ptrSize, the byte at start-2 holds the entries for start+2*ptrSize, -// start+3*ptrSize, and so on. -// In the byte holding the entries for addresses p and p+ptrSize, the low 4 bits -// describe p and the high 4 bits describe p+ptrSize. +// That is, the byte at address start-1 holds the 2-bit entries for the four words +// start through start+3*ptrSize, the byte at start-2 holds the entries for +// start+4*ptrSize through start+7*ptrSize, and so on. +// In each byte, the low 2 bits describe the first word, the next 2 bits describe +// the next word, and so on. // -// The 4 bits for each word are: -// 0001 - not used -// 0010 - bitMarked: this object has been marked by GC -// tt00 - word type bits, as in a type bitmap. +// In each 2-bit entry, the lower bit holds the same information as in the 1-bit +// bitmaps: 0 means uninteresting and 1 means live pointer to be visited during GC. +// The meaning of the high bit depends on the position of the word being described +// in its allocated object. In the first word, the high bit is the GC ``marked'' bit. +// In the second word, the high bit is the GC ``checkmarked'' bit (see below). +// In the third and later words, the high bit indicates that the object is still +// being described. In these words, if a bit pair with a high bit 0 is encountered, +// the low bit can also be assumed to be 0, and the object description is over. +// This 00 is called the ``dead'' encoding: it signals that the rest of the words +// in the object are uninteresting to the garbage collector. // -// The code makes use of the fact that the zero value for a heap bitmap nibble -// has no boundary bit set, no marked bit set, and type bits == typeDead. +// The code makes use of the fact that the zero value for a heap bitmap +// has no live pointer bit set and is (depending on position), not marked, +// not checkmarked, and is the dead encoding. // These properties must be preserved when modifying the encoding. // // Checkmarks @@ -57,45 +45,33 @@ // collector implementation. As a sanity check, the GC has a 'checkmark' // mode that retraverses the object graph with the world stopped, to make // sure that everything that should be marked is marked. -// In checkmark mode, in the heap bitmap, the type bits for the first word -// of an object are redefined: +// In checkmark mode, in the heap bitmap, the high bit of the 2-bit entry +// for the second word of the object holds the checkmark bit. +// When not in checkmark mode, this bit is set to 1. // -// 00 - typeScalarCheckmarked // typeScalar, checkmarked -// 01 - typeScalar // typeScalar, not checkmarked -// 10 - typePointer // typePointer, not checkmarked -// 11 - typePointerCheckmarked // typePointer, checkmarked -// -// That is, typeDead is redefined to be typeScalar + a checkmark, and the -// previously unused 11 pattern is redefined to be typePointer + a checkmark. -// To prepare for this mode, we must move any typeDead in the first word of -// a multiword object to the second word. +// The smallest possible allocation is 8 bytes. On a 32-bit machine, that +// means every allocated object has two words, so there is room for the +// checkmark bit. On a 64-bit machine, however, the 8-byte allocation is +// just one word, so the second bit pair is not available for encoding the +// checkmark. However, because non-pointer allocations are combined +// into larger 16-byte (maxTinySize) allocations, a plain 8-byte allocation +// must be a pointer, so the type bit in the first word is not actually needed. +// It is still used in general, except in checkmark the type bit is repurposed +// as the checkmark bit and then reinitialized (to 1) as the type bit when +// finished. package runtime import "unsafe" const ( - typeDead = 0 - typeScalarCheckmarked = 0 - typeScalar = 1 - typePointer = 2 - typePointerCheckmarked = 3 + bitPointer = 1 + bitMarked = 2 - typeBitsWidth = 2 // # of type bits per pointer-sized word - typeMask = 1<> h.shift +} + // isMarked reports whether the heap bits have the marked bit set. +// h must describe the initial word of the object. func (h heapBits) isMarked() bool { return *h.bitp&(bitMarked<> (h.shift + typeShift)) & typeMask +// isPointer reports whether the heap bits describe a pointer word. +// h must describe the initial word of the object. +func (h heapBits) isPointer() bool { + return (*h.bitp>>h.shift)&bitPointer != 0 +} + +// hasPointers reports whether the given object has any pointers. +// It must be told how large the object at h is, so that it does not read too +// far into the bitmap. +// h must describe the initial word of the object. +func (h heapBits) hasPointers(size uintptr) bool { + if size == ptrSize { // 1-word objects are always pointers + return true + } + // Otherwise, at least a 2-word object, and at least 2-word aligned, + // so h.shift is either 0 or 4, so we know we can get the bits for the + // first two words out of *h.bitp. + // If either of the first two words is a pointer, not pointer free. + b := uint32(*h.bitp >> h.shift) + if b&(bitPointer|bitPointer<>h.shift)&bitPointer != 0 + } + // All multiword objects are 2-word aligned, + // so we know that the initial word's 2-bit pair + // and the second word's 2-bit pair are in the + // same heap bitmap byte, *h.bitp. + return (*h.bitp>>(heapBitsWidth+h.shift))&bitMarked != 0 } // setCheckmarked sets the checkmarked bit. -func (h heapBits) setCheckmarked() { - typ := h.typeBits() - if typ == typeScalar { - // Clear low type bit to turn 01 into 00. - atomicand8(h.bitp, ^((1 << typeShift) << h.shift)) - } else if typ == typePointer { - // Set low type bit to turn 10 into 11. - atomicor8(h.bitp, (1<>typeShift)&typeMask == typeDead { - x += (typeScalar - typeDead) << typeShift - } - if (x>>(4+typeShift))&typeMask == typeDead { - x += (typeScalar - typeDead) << (4 + typeShift) - } - *bitp = uint8(x) + for i := uintptr(0); i < n; i += 4 { + *bitp &^= bitPointer | bitPointer<<2 | bitPointer<<4 | bitPointer<<6 bitp = subtractb(bitp, 1) } return } - - // Update bottom nibble for first word of each object. - // If the bottom nibble says typeDead, change to typeScalar - // and clear top nibble to mark as typeDead. - bitp := h.bitp - step := size / heapBitmapScale for i := uintptr(0); i < n; i++ { - x := *bitp - if (x>>typeShift)&typeMask == typeDead { - x += (typeScalar - typeDead) << typeShift - x &= 0x0f // clear top nibble to typeDead - } - bitp = subtractb(bitp, step) + *h.bitp &^= bitMarked << (heapBitsWidth + h.shift) + h = h.forward(size / ptrSize) } } -// clearCheckmarkSpan removes all the checkmarks from a span. -// If it finds a multiword object starting with typeScalar typeDead, -// it rewrites the heap bits to the simpler typeDead typeDead. +// clearCheckmarkSpan undoes all the checkmarking in a span. +// The actual checkmark bits are ignored, so the only work to do +// is to fix the pointer bits. (Pointer bits are ignored by scanobject +// but consulted by typedmemmove.) func (h heapBits) clearCheckmarkSpan(size, n, total uintptr) { - if size == ptrSize { + // The ptrSize == 8 is a compile-time constant false on 32-bit and eliminates this code entirely. + if ptrSize == 8 && size == ptrSize { + // Checkmark bit is type bit, bottom bit of every 2-bit entry. // Only possible on 64-bit system, since minimum size is 8. - // Must update both top and bottom nibble of each byte. - // typeScalarCheckmarked can be left as typeDead, - // but we want to change typeScalar back to typeDead. + // Must clear type bit (checkmark bit) of every word. + // The type bit is the lower of every two-bit pair. bitp := h.bitp - for i := uintptr(0); i < n; i += 2 { - x := int(*bitp) - switch typ := (x >> typeShift) & typeMask; typ { - case typeScalar: - x += (typeDead - typeScalar) << typeShift - case typePointerCheckmarked: - x += (typePointer - typePointerCheckmarked) << typeShift - } - - switch typ := (x >> (4 + typeShift)) & typeMask; typ { - case typeScalar: - x += (typeDead - typeScalar) << (4 + typeShift) - case typePointerCheckmarked: - x += (typePointer - typePointerCheckmarked) << (4 + typeShift) - } - - *bitp = uint8(x) + for i := uintptr(0); i < n; i += 4 { + *bitp |= bitPointer | bitPointer<<2 | bitPointer<<4 | bitPointer<<6 bitp = subtractb(bitp, 1) } - return - } - - // Update bottom nibble for first word of each object. - // If the bottom nibble says typeScalarCheckmarked and the top is not typeDead, - // change to typeScalar. Otherwise leave, since typeScalarCheckmarked == typeDead. - // If the bottom nibble says typePointerCheckmarked, change to typePointer. - bitp := h.bitp - step := size / heapBitmapScale - for i := uintptr(0); i < n; i++ { - x := int(*bitp) - switch typ := (x >> typeShift) & typeMask; { - case typ == typeScalarCheckmarked && (x>>(4+typeShift))&typeMask != typeDead: - x += (typeScalar - typeScalarCheckmarked) << typeShift - case typ == typePointerCheckmarked: - x += (typePointer - typePointerCheckmarked) << typeShift - } - - *bitp = uint8(x) - bitp = subtractb(bitp, step) } } @@ -393,44 +374,98 @@ func (h heapBits) clearCheckmarkSpan(size, n, total uintptr) { // bits for the first two words (or one for single-word objects) to typeDead // and then calls f(p), where p is the object's base address. // f is expected to add the object to a free list. +// For non-free objects, heapBitsSweepSpan turns off the marked bit. func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) { h := heapBitsForSpan(base) - if size == ptrSize { - // Only possible on 64-bit system, since minimum size is 8. - // Must read and update both top and bottom nibble of each byte. + switch { + default: + throw("heapBitsSweepSpan") + case size == ptrSize: + // Consider mark bits in all four 2-bit entries of each bitmap byte. bitp := h.bitp - for i := uintptr(0); i < n; i += 2 { - x := int(*bitp) + for i := uintptr(0); i < n; i += 4 { + x := uint32(*bitp) if x&bitMarked != 0 { x &^= bitMarked } else { - x &^= typeMask << typeShift + x &^= bitPointer f(base + i*ptrSize) } + if x&(bitMarked<<2) != 0 { + x &^= bitMarked << 2 + } else { + x &^= bitPointer << 2 + f(base + (i+1)*ptrSize) + } if x&(bitMarked<<4) != 0 { x &^= bitMarked << 4 } else { - x &^= typeMask << (4 + typeShift) - f(base + (i+1)*ptrSize) + x &^= bitPointer << 4 + f(base + (i+2)*ptrSize) + } + if x&(bitMarked<<6) != 0 { + x &^= bitMarked << 6 + } else { + x &^= bitPointer << 6 + f(base + (i+3)*ptrSize) } *bitp = uint8(x) bitp = subtractb(bitp, 1) } - return - } - bitp := h.bitp - step := size / heapBitmapScale - for i := uintptr(0); i < n; i++ { - x := int(*bitp) - if x&bitMarked != 0 { - x &^= bitMarked - } else { - x = 0 - f(base + i*size) + case size%(4*ptrSize) == 0: + // Mark bit is in first word of each object. + // Each object starts at bit 0 of a heap bitmap byte. + bitp := h.bitp + step := size / heapBitmapScale + for i := uintptr(0); i < n; i++ { + x := uint32(*bitp) + if x&bitMarked != 0 { + x &^= bitMarked + } else { + x = 0 + f(base + i*size) + } + *bitp = uint8(x) + bitp = subtractb(bitp, step) + } + + case size%(4*ptrSize) == 2*ptrSize: + // Mark bit is in first word of each object, + // but every other object starts halfway through a heap bitmap byte. + // Unroll loop 2x to handle alternating shift count and step size. + bitp := h.bitp + step := size / heapBitmapScale + var i uintptr + for i = uintptr(0); i < n; i += 2 { + x := uint32(*bitp) + if x&bitMarked != 0 { + x &^= bitMarked + } else { + x &^= 0x0f + f(base + i*size) + if size > 2*ptrSize { + x = 0 + } + } + *bitp = uint8(x) + if i+1 >= n { + break + } + bitp = subtractb(bitp, step) + x = uint32(*bitp) + if x&(bitMarked<<4) != 0 { + x &^= bitMarked << 4 + } else { + x &^= 0xf0 + f(base + (i+1)*size) + if size > 2*ptrSize { + *subtractb(bitp, 1) = 0 + } + } + *bitp = uint8(x) + bitp = subtractb(bitp, step+1) } - *bitp = uint8(x) - bitp = subtractb(bitp, step) } } @@ -456,7 +491,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // when initializing the span, and then the atomicor8 here // goes away - heapBitsSetType would be a no-op // in that case. - atomicor8(h.bitp, typePointer<<(typeShift+h.shift)) + atomicor8(h.bitp, bitPointer<>= 1 - if nv += heapBitsWidth; nv == 8 { - *h.bitp = uint8(v) - h.bitp = subtractb(h.bitp, 1) - v = 0 - nv = 0 - } + // Copy from 1-bit ptrmask into 2-bit bitmap. + // If size is a multiple of 4 words, then the bitmap bytes for the object + // are not shared with any other object and can be written directly. + // On 64-bit systems, many sizes are only 16-byte aligned; half of + // those are not multiples of 4 words (for example, 48/8 = 6 words); + // those share either the leading byte or the trailing byte of their bitmaps + // with another object. + nptr := typ.size / ptrSize + _ = nptr + for i := uintptr(0); i < dataSize/ptrSize; i++ { + atomicand8(h.bitp, ^((bitPointer | bitMarked) << h.shift)) + j := i % nptr + if (*addb(ptrmask, j/8)>>(j%8))&1 != 0 { + atomicor8(h.bitp, bitPointer<= 2 { + atomicor8(h.bitp, bitMarked<> (uint(i) % 8) & 1 if inplace { + throw("gc inplace") + const typeShift = 2 // Store directly into GC bitmap. h := heapBitsForAddr(uintptr(unsafe.Pointer(&mask[pos]))) if h.shift == 0 { @@ -624,12 +648,6 @@ func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace, sparse bool) *h.bitp |= v << (4 + typeShift) } pos += ptrSize - } else if sparse { - throw("sparse") - // 4-bits per word, type bits in high bits - v <<= (pos % 8) + typeShift - mask[pos/8] |= v - pos += heapBitsWidth } else { // 1 bit per word, for data/bss bitmap mask[pos/8] |= v << (pos % 8) @@ -647,7 +665,7 @@ func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace, sparse bool) prog = (*byte)(add(unsafe.Pointer(prog), ptrSize)) var prog1 *byte for i := uintptr(0); i < siz; i++ { - prog1 = unrollgcprog1(&mask[0], prog, &pos, inplace, sparse) + prog1 = unrollgcprog1(&mask[0], prog, &pos, inplace) } if *prog1 != insArrayEnd { throw("unrollgcprog: array does not end with insArrayEnd") @@ -667,7 +685,7 @@ func unrollglobgcprog(prog *byte, size uintptr) bitvector { mask := (*[1 << 30]byte)(persistentalloc(masksize+1, 0, &memstats.gc_sys)) mask[masksize] = 0xa1 pos := uintptr(0) - prog = unrollgcprog1(&mask[0], prog, &pos, false, false) + prog = unrollgcprog1(&mask[0], prog, &pos, false) if pos != size/ptrSize { print("unrollglobgcprog: bad program size, got ", pos, ", expect ", size/ptrSize, "\n") throw("unrollglobgcprog: bad program size") @@ -682,17 +700,21 @@ func unrollglobgcprog(prog *byte, size uintptr) bitvector { } func unrollgcproginplace_m(v unsafe.Pointer, typ *_type, size, size0 uintptr) { + throw("unrollinplace") + // TODO(rsc): Update for 1-bit bitmaps. // TODO(rsc): Explain why these non-atomic updates are okay. pos := uintptr(0) prog := (*byte)(unsafe.Pointer(uintptr(typ.gc[1]))) for pos != size0 { - unrollgcprog1((*byte)(v), prog, &pos, true, true) + unrollgcprog1((*byte)(v), prog, &pos, true) } // Mark first word as bitAllocated. // Mark word after last as typeDead. if size0 < size { h := heapBitsForAddr(uintptr(v) + size0) + const typeMask = 0 + const typeShift = 0 *h.bitp &^= typeMask << typeShift } } @@ -707,7 +729,7 @@ func unrollgcprog_m(typ *_type) { if *mask == 0 { pos := uintptr(8) // skip the unroll flag prog := (*byte)(unsafe.Pointer(uintptr(typ.gc[1]))) - prog = unrollgcprog1(mask, prog, &pos, false, false) + prog = unrollgcprog1(mask, prog, &pos, false) if *prog != insEnd { throw("unrollgcprog: program does not end with insEnd") } @@ -737,26 +759,24 @@ func getgcmask(ep interface{}) (mask []byte) { for datap := &firstmoduledata; datap != nil; datap = datap.next { // data if datap.data <= uintptr(p) && uintptr(p) < datap.edata { + bitmap := datap.gcdatamask.bytedata n := (*ptrtype)(unsafe.Pointer(t)).elem.size mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { off := (uintptr(p) + i - datap.data) / ptrSize - bits := (*addb(datap.gcdatamask.bytedata, off/8) >> (off % 8)) & 1 - bits += 1 // convert 1-bit to 2-bit - mask[i/ptrSize] = bits + mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1 } return } // bss if datap.bss <= uintptr(p) && uintptr(p) < datap.ebss { + bitmap := datap.gcbssmask.bytedata n := (*ptrtype)(unsafe.Pointer(t)).elem.size mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { off := (uintptr(p) + i - datap.bss) / ptrSize - bits := (*addb(datap.gcbssmask.bytedata, off/8) >> (off % 8)) & 1 - bits += 1 // convert 1-bit to 2-bit - mask[i/ptrSize] = bits + mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1 } return } @@ -768,8 +788,14 @@ func getgcmask(ep interface{}) (mask []byte) { if mlookup(uintptr(p), &base, &n, nil) != 0 { mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { - bits := heapBitsForAddr(base + i).typeBits() - mask[i/ptrSize] = bits + hbits := heapBitsForAddr(base + i) + if hbits.isPointer() { + mask[i/ptrSize] = 1 + } + if i >= 2*ptrSize && !hbits.isMarked() { + mask[i/ptrSize] = 255 + break + } } return } @@ -801,10 +827,9 @@ func getgcmask(ep interface{}) (mask []byte) { n := (*ptrtype)(unsafe.Pointer(t)).elem.size mask = make([]byte, n/ptrSize) for i := uintptr(0); i < n; i += ptrSize { + bitmap := bv.bytedata off := (uintptr(p) + i - frame.varp + size) / ptrSize - bits := (*addb(bv.bytedata, off/8) >> (off % 8)) & 1 - bits += 1 // convert 1-bit to 2-bit - mask[i/ptrSize] = bits + mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1 } } return diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 9d78ddecae..917299c9df 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -597,20 +597,19 @@ func scanobject(b uintptr, gcw *gcWork) { // Avoid needless hbits.next() on last iteration. hbits = hbits.next() } - bits := uintptr(hbits.typeBits()) - if bits == typeDead { - break // no more pointers in this object + // During checkmarking, 1-word objects store the checkmark + // in the type bit for the one word. The only one-word objects + // are pointers, or else they'd be merged with other non-pointer + // data into larger allocations. + if n != 1 { + b := hbits.bits() + if i >= 2*ptrSize && b&bitMarked == 0 { + break // no more pointers in this object + } + if b&bitPointer == 0 { + continue // not a pointer + } } - - if bits <= typeScalar { // typeScalar, typeDead, typeScalarMarked - continue - } - - if bits&typePointer != typePointer { - print("gc useCheckmark=", useCheckmark, " b=", hex(b), "\n") - throw("unexpected garbage collection bits") - } - // Work here is duplicated in scanblock. // If you make changes here, make changes there too. @@ -673,11 +672,11 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork throw("checkmark found unmarked object") } - if hbits.isCheckmarked() { + if hbits.isCheckmarked(span.elemsize) { return } - hbits.setCheckmarked() - if !hbits.isCheckmarked() { + hbits.setCheckmarked(span.elemsize) + if !hbits.isCheckmarked(span.elemsize) { throw("setCheckmarked and isCheckmarked disagree") } } else { @@ -685,12 +684,11 @@ func greyobject(obj, base, off uintptr, hbits heapBits, span *mspan, gcw *gcWork if hbits.isMarked() { return } - hbits.setMarked() // If this is a noscan object, fast-track it to black // instead of greying it. - if hbits.typeBits() == typeDead { + if !hbits.hasPointers(span.elemsize) { gcw.bytesMarked += uint64(span.elemsize) return } diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go index f74694b7e9..d6ddf86dba 100644 --- a/src/runtime/stack1.go +++ b/src/runtime/stack1.go @@ -352,6 +352,12 @@ func adjustpointer(adjinfo *adjustinfo, vpp unsafe.Pointer) { } } +// Information from the compiler about the layout of stack frames. +type bitvector struct { + n int32 // # of bits + bytedata *uint8 +} + type gobitvector struct { n uintptr bytedata []uint8 From feb8a3b6169504040024e22b3eef5959554f9dbd Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 4 May 2015 11:30:10 -0400 Subject: [PATCH 020/232] runtime: optimize heapBitsSetType MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the conversion of the heap bitmap from 4-bit to 2-bit fields, I replaced heapBitsSetType with the dumbest thing that could possibly work: two atomic operations (atomicand8+atomicor8) per 2-bit field. This CL replaces that code with a proper implementation that avoids the atomics whenever possible. Benchmarks vs base CL (before the conversion to 2-bit heap bitmap) and vs Go 1.4 below. Compared to Go 1.4, SetTypePtr (a 1-pointer allocation) is 10ns slower because a race against the concurrent GC requires the use of an atomicor8 that used to be an ordinary write. This slowdown was present even in the base CL. Compared to both Go 1.4 and base, SetTypeNode8 (a 10-word allocation) is 10ns slower because it too needs a new atomic, because with the denser representation, the byte on the end of the allocation is now shared with the object next to it; this was not true with the 4-bit representation. Excluding these two (fundamental) slowdowns due to the use of atomics, the new code is noticeably faster than both Go 1.4 and the base CL. The next CL will reintroduce the ``typeDead'' optimization. Stats are from 5 runs on a MacBookPro10,2 (late 2012 Core i5). Compared to base CL (** = new atomic) name old mean new mean delta SetTypePtr 14.1ns × (0.99,1.02) 14.7ns × (0.93,1.10) ~ (p=0.175) SetTypePtr8 18.4ns × (1.00,1.01) 18.6ns × (0.81,1.21) ~ (p=0.866) SetTypePtr16 28.7ns × (1.00,1.00) 22.4ns × (0.90,1.27) -21.88% (p=0.015) SetTypePtr32 52.3ns × (1.00,1.00) 33.8ns × (0.93,1.24) -35.37% (p=0.001) SetTypePtr64 79.2ns × (1.00,1.00) 55.1ns × (1.00,1.01) -30.43% (p=0.000) SetTypePtr126 118ns × (1.00,1.00) 100ns × (1.00,1.00) -15.97% (p=0.000) SetTypePtr128 130ns × (0.92,1.19) 98ns × (1.00,1.00) -24.36% (p=0.008) SetTypePtrSlice 726ns × (0.96,1.08) 760ns × (1.00,1.00) ~ (p=0.152) SetTypeNode1 14.1ns × (0.94,1.15) 12.0ns × (1.00,1.01) -14.60% (p=0.020) SetTypeNode1Slice 135ns × (0.96,1.07) 88ns × (1.00,1.00) -34.53% (p=0.000) SetTypeNode8 20.9ns × (1.00,1.01) 32.6ns × (1.00,1.00) +55.37% (p=0.000) ** SetTypeNode8Slice 414ns × (0.99,1.02) 244ns × (1.00,1.00) -41.09% (p=0.000) SetTypeNode64 80.0ns × (1.00,1.00) 57.4ns × (1.00,1.00) -28.23% (p=0.000) SetTypeNode64Slice 2.15µs × (1.00,1.01) 1.56µs × (1.00,1.00) -27.43% (p=0.000) SetTypeNode124 119ns × (0.99,1.00) 100ns × (1.00,1.00) -16.11% (p=0.000) SetTypeNode124Slice 3.40µs × (1.00,1.00) 2.93µs × (1.00,1.00) -13.80% (p=0.000) SetTypeNode126 120ns × (1.00,1.01) 98ns × (1.00,1.00) -18.19% (p=0.000) SetTypeNode126Slice 3.53µs × (0.98,1.08) 3.02µs × (1.00,1.00) -14.49% (p=0.002) SetTypeNode1024 726ns × (0.97,1.09) 740ns × (1.00,1.00) ~ (p=0.451) SetTypeNode1024Slice 24.9µs × (0.89,1.37) 23.1µs × (1.00,1.00) ~ (p=0.476) Compared to Go 1.4 (** = new atomic) name old mean new mean delta SetTypePtr 5.71ns × (0.89,1.19) 14.68ns × (0.93,1.10) +157.24% (p=0.000) ** SetTypePtr8 19.3ns × (0.96,1.10) 18.6ns × (0.81,1.21) ~ (p=0.638) SetTypePtr16 30.7ns × (0.99,1.03) 22.4ns × (0.90,1.27) -26.88% (p=0.005) SetTypePtr32 51.5ns × (1.00,1.00) 33.8ns × (0.93,1.24) -34.40% (p=0.001) SetTypePtr64 83.6ns × (0.94,1.12) 55.1ns × (1.00,1.01) -34.12% (p=0.001) SetTypePtr126 137ns × (0.87,1.26) 100ns × (1.00,1.00) -27.10% (p=0.028) SetTypePtrSlice 865ns × (0.80,1.23) 760ns × (1.00,1.00) ~ (p=0.243) SetTypeNode1 15.2ns × (0.88,1.12) 12.0ns × (1.00,1.01) -20.89% (p=0.014) SetTypeNode1Slice 156ns × (0.93,1.16) 88ns × (1.00,1.00) -43.57% (p=0.001) SetTypeNode8 23.8ns × (0.90,1.18) 32.6ns × (1.00,1.00) +36.76% (p=0.003) ** SetTypeNode8Slice 502ns × (0.92,1.10) 244ns × (1.00,1.00) -51.46% (p=0.000) SetTypeNode64 85.6ns × (0.94,1.11) 57.4ns × (1.00,1.00) -32.89% (p=0.001) SetTypeNode64Slice 2.36µs × (0.91,1.14) 1.56µs × (1.00,1.00) -33.96% (p=0.002) SetTypeNode124 130ns × (0.91,1.12) 100ns × (1.00,1.00) -23.49% (p=0.004) SetTypeNode124Slice 3.81µs × (0.90,1.22) 2.93µs × (1.00,1.00) -23.09% (p=0.025) There are fewer benchmarks vs Go 1.4 because unrolling directly into the heap bitmap is not yet implemented, so those would not be meaningful comparisons. These benchmarks were not present in Go 1.4 as distributed. The backport to Go 1.4 is in github.com/rsc/go's go14bench branch, commit 71d5ee5. Change-Id: I95ed05a22bf484b0fc9efad549279e766c98d2b6 Reviewed-on: https://go-review.googlesource.com/9704 Reviewed-by: Rick Hudson --- src/runtime/mbitmap.go | 331 ++++++++++++++++++++++++++++++++++++++--- src/runtime/mgcmark.go | 18 +-- 2 files changed, 315 insertions(+), 34 deletions(-) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index dea6879adc..b866d7f732 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -476,12 +476,33 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) { // (The number of values is given by dataSize / typ.size.) // If dataSize < size, the fragment [x+dataSize, x+size) is // recorded as non-pointer data. +// It is known that the type has pointers somewhere; +// malloc does not call heapBitsSetType when there are no pointers, +// because all free objects are marked as noscan during +// heapBitsSweepSpan. +// There can only be one allocation from a given span active at a time, +// so this code is not racing with other instances of itself, +// and we don't allocate from a span until it has been swept, +// so this code is not racing with heapBitsSweepSpan. +// It is, however, racing with the concurrent GC mark phase, +// which can be setting the mark bit in the leading 2-bit entry +// of an allocated block. The block we are modifying is not quite +// allocated yet, so the GC marker is not racing with updates to x's bits, +// but if the start or end of x shares a bitmap byte with an adjacent +// object, the GC marker is racing with updates to those object's mark bits. func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // From here till marked label marking the object as allocated // and storing type info in the GC bitmap. h := heapBitsForAddr(x) - var ptrmask *uint8 + // dataSize is always size rounded up to the next malloc size class, + // except in the case of allocating a defer block, in which case + // size is sizeof(_defer{}) (at least 6 words) and dataSize may be + // arbitrarily larger. + // + // The checks for size == ptrSize and size == 2*ptrSize can therefore + // assume that dataSize == size without checking it explicitly. + if size == ptrSize { // It's one word and it has pointers, it must be a pointer. // The bitmap byte is shared with the one-word object @@ -494,6 +515,8 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { atomicor8(h.bitp, bitPointer<>(j%8))&1 != 0 { - atomicor8(h.bitp, bitPointer<= 2 { - atomicor8(h.bitp, bitMarked<= nw { + goto Phase3 + } + *hbitp = uint8(hb) + hbitp = subtractb(hbitp, 1) + b >>= 4 + nb -= 4 + + case 4: + // Ptrmask and heap bitmap are misaligned. + // The bits for the first two words are in a byte shared with another object + // and must be updated atomically. + // NOTE(rsc): The atomic here may not be necessary. + // We took care of 1-word and 2-word objects above, + // so this is at least a 6-word object, so our start bits + // are shared only with the type bits of another object, + // not with its mark bit. Since there is only one allocation + // from a given span at a time, we should be able to set + // these bits non-atomically. Not worth the risk right now. + hb = (b&1)<<4 | (b&2)<<(4+heapBitsWidth-1) // bits being prepared for *h.bitp + b >>= 2 + nb -= 2 + // Note: no bitMarker in hb because the first two words don't get markers from us. + atomicor8(hbitp, uint8(hb)) + hbitp = subtractb(hbitp, 1) + + // Expand 8-bit chunks of ptrmask into pairs of heap bitmap bytes. + // We know the object size is a multiple of 2 words but not 4, so the + // object size minus the 2 words we just handled is a multiple of 4, + // so we can use non-atomic writes to the heap bitmap for the + // rest of this code, even for the final fragment or a trailing dead marker byte. + + // Loop prepares bits for final byte but stops before writing them, + // so that in the case where we need to write only part of a byte, + // the code below the loop can truncate the bitMarked. + w += 2 + } + + // Phase 2: Full bytes in bitmap, up to but not including write to last byte (full or partial) in bitmap. + // The loop computes the bits for that last write but does not execute the write; + // it leaves the bits in hb for processing by phase 3. + // To avoid repeated adjustment of nb, we subtract out the 4 bits we're going to + // use in the first half of the loop right now, and then we only adjust nb explicitly + // if the 8 bits used by each iteration isn't balanced by 8 bits loaded mid-loop. + nb -= 4 + for { + // Emit bitmap byte. + // b has at least nb+4 bits, with one exception: + // if w+4 >= nw, then b has only nw-w bits, + // but we'll stop at the break and then truncate + // appropriately in Phase 3. + hb = b&1 | (b&2)<<(heapBitsWidth-1) | (b&4)<<(2*heapBitsWidth-2) | (b&8)<<(3*heapBitsWidth-3) + hb |= bitMarked | bitMarked<= nw { + break + } + *hbitp = uint8(hb) + hbitp = subtractb(hbitp, 1) + b >>= 4 + + // Load more bits. b has nb right now. + if p != endp { + // Fast path: keep reading from ptrmask. + // nb unmodified: we just loaded 8 bits, + // and the next iteration will consume 8 bits, + // leaving us with the same nb the next time we're here. + b |= uintptr(*p) << nb + p = addb(p, 1) + } else if p == nil { + // Almost as fast path: track bit count and refill from pbits. + // For short repetitions. + if nb < 8 { + b |= pbits << nb + nb += endnb + } + nb -= 8 // for next iteration + } else { + // Slow path: reached end of ptrmask. + // Process final partial byte and rewind to start. + b |= uintptr(*p) << nb + nb += endnb + if nb < 8 { + b |= uintptr(*ptrmask) << nb + p = addb(ptrmask, 1) + } else { + nb -= 8 + p = ptrmask + } + } + + // Emit bitmap byte. + hb = b&1 | (b&2)<<(heapBitsWidth-1) | (b&4)<<(2*heapBitsWidth-2) | (b&8)<<(3*heapBitsWidth-3) + hb |= bitMarked | bitMarked<= nw { + break + } + *hbitp = uint8(hb) + hbitp = subtractb(hbitp, 1) + b >>= 4 + } + +Phase3: + // Phase 3: Special case for final byte or half-byte describing final fragment of data. + // If there are not four data words for this final fragment, we must clear the mark bits + // in the 2-bit entries for the missing words. Clearing them creates a ``dead'' entry + // to tell the GC scan to stop scanning this object early. + // If there are four words in the final fragment but there is more data, + // then we must write a ``dead'' entry to the next bitmap byte. + if frag := (nw - w) % 4; frag != 0 { + // Data ends at least one word early. + hb &= 1<<(heapBitsWidth*frag) - 1 + if w*ptrSize <= size { + // We own the whole byte and get the dead marker for free. + *hbitp = uint8(hb) + } else { + // We only own the bottom half of the byte. + // If frag == 1, we get a dead marker for free. + // If frag == 2, no dead marker needed (we've reached the end of the object). + atomicand8(hbitp, 0xf0) + atomicor8(hbitp, uint8(hb)) + } + } else { + // Data ends with a full bitmap byte. + *hbitp = uint8(hb) + if w*ptrSize < size { + // There's more data in the allocated object. + // Write a dead marker in the next byte. + hbitp = subtractb(hbitp, 1) + if (w+4)*ptrSize <= size { + // We own the whole byte. + *hbitp = 0 + } else { + // We only own the bottom half of the byte. + atomicand8(hbitp, 0xf0) + } + } + } + + const test = false // slow but helpful + if test { + // Double-check that bits to be written were written correctly. + // Does not check that other bits were not written, unfortunately. + h := heapBitsForAddr(x) + nptr := typ.size / ptrSize + for i := uintptr(0); i <= dataSize/ptrSize; i++ { + j := i % nptr + var have, want uint8 + if i == dataSize/ptrSize { + if dataSize >= size { + break + } + have = (*h.bitp >> h.shift) & 3 + want = 0 // dead bits + } else { + have = (*h.bitp >> h.shift) & 3 + if (*addb(ptrmask, j/8)>>(j%8))&1 != 0 { + want |= bitPointer + } + if i >= 2 { + want |= bitMarked + } else { + have &^= bitMarked + } + } + if have != want { + println("mismatch writing bits for", *typ._string, "x", dataSize/typ.size) + print("typ.size=", typ.size, " dataSize=", dataSize, " size=", size, "\n") + h = heapBitsForAddr(x) + print("initial bits h.bitp=", h.bitp, " h.shift=", h.shift, "\n") + print("p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n") + println("at word", i, "offset", i*ptrSize, "have", have, "want", want) + throw("bad heapBitsSetType") + } + h = h.next() + } } } diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index 917299c9df..bf21e47d83 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -601,18 +601,16 @@ func scanobject(b uintptr, gcw *gcWork) { // in the type bit for the one word. The only one-word objects // are pointers, or else they'd be merged with other non-pointer // data into larger allocations. - if n != 1 { - b := hbits.bits() - if i >= 2*ptrSize && b&bitMarked == 0 { - break // no more pointers in this object - } - if b&bitPointer == 0 { - continue // not a pointer - } + bits := hbits.bits() + if i >= 2*ptrSize && bits&bitMarked == 0 { + break // no more pointers in this object + } + if bits&bitPointer == 0 { + continue // not a pointer } - // Work here is duplicated in scanblock. - // If you make changes here, make changes there too. + // Work here is duplicated in scanblock and above. + // If you make changes here, make changes there too. obj := *(*uintptr)(unsafe.Pointer(b + i)) // At this point we have extracted the next potential pointer. From 54af9a3ba5f3e656077eab2e9305cbbb41d7b154 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 4 May 2015 22:53:54 -0400 Subject: [PATCH 021/232] runtime: reintroduce ``dead'' space during GC scan MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reintroduce an optimization discarded during the initial conversion from 4-bit heap bitmaps to 2-bit heap bitmaps: when we reach the place in the bitmap where there are no more pointers, mark that position for the GC so that it can avoid scanning past that place. During heapBitsSetType we can also avoid initializing heap bitmap beyond that location, which gives a bit of a win compared to Go 1.4. This particular optimization (not initializing the heap bitmap) may not last: we might change typedmemmove to use the heap bitmap, in which case it would all need to be initialized. The early stop in the GC scan will stay no matter what. Compared to Go 1.4 (github.com/rsc/go, branch go14bench): name old mean new mean delta SetTypeNode64 80.7ns × (1.00,1.01) 57.4ns × (1.00,1.01) -28.83% (p=0.000) SetTypeNode64Dead 80.5ns × (1.00,1.01) 13.1ns × (0.99,1.02) -83.77% (p=0.000) SetTypeNode64Slice 2.16µs × (1.00,1.01) 1.54µs × (1.00,1.01) -28.75% (p=0.000) SetTypeNode64DeadSlice 2.16µs × (1.00,1.01) 1.52µs × (1.00,1.00) -29.74% (p=0.000) Compared to previous CL: name old mean new mean delta SetTypeNode64 56.7ns × (1.00,1.00) 57.4ns × (1.00,1.01) +1.19% (p=0.000) SetTypeNode64Dead 57.2ns × (1.00,1.00) 13.1ns × (0.99,1.02) -77.15% (p=0.000) SetTypeNode64Slice 1.56µs × (1.00,1.01) 1.54µs × (1.00,1.01) -0.89% (p=0.000) SetTypeNode64DeadSlice 1.55µs × (1.00,1.01) 1.52µs × (1.00,1.00) -2.23% (p=0.000) This is the last CL in the sequence converting from the 4-bit heap to the 2-bit heap, with all the same optimizations reenabled. Compared to before that process began (compared to CL 9701 patch set 1): name old mean new mean delta BinaryTree17 5.87s × (0.94,1.09) 5.91s × (0.96,1.06) ~ (p=0.578) Fannkuch11 4.32s × (1.00,1.00) 4.32s × (1.00,1.00) ~ (p=0.474) FmtFprintfEmpty 89.1ns × (0.95,1.16) 89.0ns × (0.93,1.10) ~ (p=0.942) FmtFprintfString 283ns × (0.98,1.02) 298ns × (0.98,1.06) +5.33% (p=0.000) FmtFprintfInt 284ns × (0.98,1.04) 286ns × (0.98,1.03) ~ (p=0.208) FmtFprintfIntInt 486ns × (0.98,1.03) 498ns × (0.97,1.06) +2.48% (p=0.000) FmtFprintfPrefixedInt 400ns × (0.99,1.02) 408ns × (0.98,1.02) +2.23% (p=0.000) FmtFprintfFloat 566ns × (0.99,1.01) 587ns × (0.98,1.01) +3.69% (p=0.000) FmtManyArgs 1.91µs × (0.99,1.02) 1.94µs × (0.99,1.02) +1.81% (p=0.000) GobDecode 15.5ms × (0.98,1.05) 15.8ms × (0.98,1.03) +1.94% (p=0.002) GobEncode 11.9ms × (0.97,1.03) 12.0ms × (0.96,1.09) ~ (p=0.263) Gzip 648ms × (0.99,1.01) 648ms × (0.99,1.01) ~ (p=0.992) Gunzip 143ms × (1.00,1.00) 143ms × (1.00,1.01) ~ (p=0.585) HTTPClientServer 89.2µs × (0.99,1.02) 90.3µs × (0.98,1.01) +1.24% (p=0.000) JSONEncode 32.3ms × (0.97,1.06) 31.6ms × (0.99,1.01) -2.29% (p=0.000) JSONDecode 106ms × (0.99,1.01) 107ms × (1.00,1.01) +0.62% (p=0.000) Mandelbrot200 6.02ms × (1.00,1.00) 6.03ms × (1.00,1.01) ~ (p=0.250) GoParse 6.57ms × (0.97,1.06) 6.53ms × (0.99,1.03) ~ (p=0.243) RegexpMatchEasy0_32 162ns × (1.00,1.00) 161ns × (1.00,1.01) -0.80% (p=0.000) RegexpMatchEasy0_1K 561ns × (0.99,1.02) 541ns × (0.99,1.01) -3.67% (p=0.000) RegexpMatchEasy1_32 145ns × (0.95,1.04) 138ns × (1.00,1.00) -5.04% (p=0.000) RegexpMatchEasy1_1K 864ns × (0.99,1.04) 887ns × (0.99,1.01) +2.57% (p=0.000) RegexpMatchMedium_32 255ns × (0.99,1.04) 253ns × (0.99,1.01) -1.05% (p=0.012) RegexpMatchMedium_1K 73.9µs × (0.98,1.04) 72.8µs × (1.00,1.00) -1.51% (p=0.005) RegexpMatchHard_32 3.92µs × (0.98,1.04) 3.85µs × (1.00,1.01) -1.88% (p=0.002) RegexpMatchHard_1K 120µs × (0.98,1.04) 117µs × (1.00,1.01) -2.02% (p=0.001) Revcomp 936ms × (0.95,1.08) 922ms × (0.97,1.08) ~ (p=0.234) Template 130ms × (0.98,1.04) 126ms × (0.99,1.01) -2.99% (p=0.000) TimeParse 638ns × (0.98,1.05) 628ns × (0.99,1.01) -1.54% (p=0.004) TimeFormat 674ns × (0.99,1.01) 668ns × (0.99,1.01) -0.80% (p=0.001) The slowdown of the first few benchmarks seems to be due to the new atomic operations for certain small size allocations. But the larger benchmarks mostly improve, probably due to the decreased memory pressure from having half as much heap bitmap. CL 9706, which removes the (never used anymore) wbshadow mode, gets back what is lost in the early microbenchmarks. Change-Id: I37423a209e8ec2a2e92538b45cac5422a6acd32d Reviewed-on: https://go-review.googlesource.com/9705 Reviewed-by: Rick Hudson --- src/cmd/internal/gc/reflect.go | 31 +++--- src/cmd/internal/ld/data.go | 20 ++-- src/cmd/internal/ld/decodesym.go | 5 + src/runtime/gcinfo_test.go | 43 ++++---- src/runtime/mbitmap.go | 168 +++++++++++++++++++++---------- 5 files changed, 163 insertions(+), 104 deletions(-) diff --git a/src/cmd/internal/gc/reflect.go b/src/cmd/internal/gc/reflect.go index 6ff9df2cfc..061b17b3ae 100644 --- a/src/cmd/internal/gc/reflect.go +++ b/src/cmd/internal/gc/reflect.go @@ -687,7 +687,7 @@ func haspointers(t *Type) bool { // typeptrdata returns the length in bytes of the prefix of t // containing pointer data. Anything after this offset is scalar data. -func typeptrdata(t *Type) uint64 { +func typeptrdata(t *Type) int64 { if !haspointers(t) { return 0 } @@ -699,24 +699,24 @@ func typeptrdata(t *Type) uint64 { TFUNC, TCHAN, TMAP: - return uint64(Widthptr) + return int64(Widthptr) case TSTRING: // struct { byte *str; intgo len; } - return uint64(Widthptr) + return int64(Widthptr) case TINTER: // struct { Itab *tab; void *data; } or // struct { Type *type; void *data; } - return 2 * uint64(Widthptr) + return 2 * int64(Widthptr) case TARRAY: if Isslice(t) { // struct { byte *array; uintgo len; uintgo cap; } - return uint64(Widthptr) + return int64(Widthptr) } // haspointers already eliminated t.Bound == 0. - return uint64(t.Bound-1)*uint64(t.Type.Width) + typeptrdata(t.Type) + return (t.Bound-1)*t.Type.Width + typeptrdata(t.Type) case TSTRUCT: // Find the last field that has pointers. @@ -726,7 +726,7 @@ func typeptrdata(t *Type) uint64 { lastPtrField = t1 } } - return uint64(lastPtrField.Width) + typeptrdata(lastPtrField.Type) + return lastPtrField.Width + typeptrdata(lastPtrField.Type) default: Fatal("typeptrdata: unexpected type, %v", t) @@ -794,7 +794,7 @@ func dcommontype(s *Sym, ot int, t *Type) int { // zero unsafe.Pointer // } ot = duintptr(s, ot, uint64(t.Width)) - ot = duintptr(s, ot, typeptrdata(t)) + ot = duintptr(s, ot, uint64(typeptrdata(t))) ot = duint32(s, ot, typehash(t)) ot = duint8(s, ot, 0) // unused @@ -1428,17 +1428,12 @@ func usegcprog(t *Type) bool { } // Calculate size of the unrolled GC mask. - nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr) - - size := (nptr + 7) / 8 + nptr := typeptrdata(t) / int64(Widthptr) // Decide whether to use unrolled GC mask or GC program. // We could use a more elaborate condition, but this seems to work well in practice. - // For small objects GC program can't give significant reduction. - // While large objects usually contain arrays; and even if it don't - // the program uses 2-bits per word while mask uses 4-bits per word, - // so the program is still smaller. - return size > int64(2*Widthptr) + // For small objects, the GC program can't give significant reduction. + return nptr > int64(2*Widthptr*8) } // Generates GC bitmask (1 bit per word). @@ -1450,11 +1445,11 @@ func gengcmask(t *Type, gcmask []byte) { return } - vec := bvalloc(2 * int32(Widthptr) * 8) + vec := bvalloc(int32(2 * Widthptr * 8)) xoffset := int64(0) onebitwalktype1(t, &xoffset, vec) - nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr) + nptr := typeptrdata(t) / int64(Widthptr) for i := int64(0); i < nptr; i++ { if bvget(vec, int32(i)) == 1 { gcmask[i/8] |= 1 << (uint(i) % 8) diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go index 676c8856de..37d458802f 100644 --- a/src/cmd/internal/ld/data.go +++ b/src/cmd/internal/ld/data.go @@ -1109,8 +1109,7 @@ func proggenaddsym(g *ProgGen, s *LSym) { // Skip alignment hole from the previous symbol. proggenskip(g, g.pos, s.Value-g.pos) - - g.pos += s.Value - g.pos + g.pos = s.Value // The test for names beginning with . here is meant // to keep .dynamic and .dynsym from turning up as @@ -1142,16 +1141,16 @@ func proggenaddsym(g *ProgGen, s *LSym) { proggendata(g, 0) proggenarrayend(g) } - g.pos = s.Value + s.Size } else if decodetype_usegcprog(s.Gotype) != 0 { // gc program, copy directly + // TODO(rsc): Maybe someday the gc program will only describe + // the first decodetype_ptrdata(s.Gotype) bytes instead of the full size. proggendataflush(g) - gcprog := decodetype_gcprog(s.Gotype) size := decodetype_size(s.Gotype) if (size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { - Diag("proggenaddsym: unaligned gcprog symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) + Diag("proggenaddsym: unaligned gcprog symbol %s: size=%d pos=%d", s.Name, size, g.pos) } for i := int64(0); i < int64(len(gcprog.P)-1); i++ { proggenemit(g, uint8(gcprog.P[i])) @@ -1160,16 +1159,15 @@ func proggenaddsym(g *ProgGen, s *LSym) { } else { // gc mask, it's small so emit as data mask := decodetype_gcmask(s.Gotype) - - size := decodetype_size(s.Gotype) - if (size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { - Diag("proggenaddsym: unaligned gcmask symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos) + ptrdata := decodetype_ptrdata(s.Gotype) + if (ptrdata%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) { + Diag("proggenaddsym: unaligned gcmask symbol %s: size=%d pos=%d", s.Name, ptrdata, g.pos) } - for i := int64(0); i < size; i += int64(Thearch.Ptrsize) { + for i := int64(0); i < ptrdata; i += int64(Thearch.Ptrsize) { word := uint(i / int64(Thearch.Ptrsize)) proggendata(g, (mask[word/8]>>(word%8))&1) } - g.pos = s.Value + size + g.pos = s.Value + ptrdata } } diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/internal/ld/decodesym.go index 754c89f12b..b9333857fd 100644 --- a/src/cmd/internal/ld/decodesym.go +++ b/src/cmd/internal/ld/decodesym.go @@ -67,6 +67,11 @@ func decodetype_size(s *LSym) int64 { return int64(decode_inuxi(s.P, Thearch.Ptrsize)) // 0x8 / 0x10 } +// Type.commonType.ptrdata +func decodetype_ptrdata(s *LSym) int64 { + return int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Ptrsize)) // 0x8 / 0x10 +} + // Type.commonType.gc func decodetype_gcprog(s *LSym) *LSym { if s.Type == obj.SDYNIMPORT { diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go index dd5c25e0b1..7618d86a45 100644 --- a/src/runtime/gcinfo_test.go +++ b/src/runtime/gcinfo_test.go @@ -34,23 +34,23 @@ func TestGCInfo(t *testing.T) { verifyGCInfo(t, "data eface", &dataEface, infoEface) verifyGCInfo(t, "data iface", &dataIface, infoIface) - verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), nonStackInfo(infoScalarPtr)) - verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), nonStackInfo(infoPtrScalar)) - verifyGCInfo(t, "stack BigStruct", new(BigStruct), nonStackInfo(infoBigStruct())) - verifyGCInfo(t, "stack string", new(string), nonStackInfo(infoString)) - verifyGCInfo(t, "stack slice", new([]string), nonStackInfo(infoSlice)) - verifyGCInfo(t, "stack eface", new(interface{}), nonStackInfo(infoEface)) - verifyGCInfo(t, "stack iface", new(Iface), nonStackInfo(infoIface)) + verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr) + verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar) + verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct()) + verifyGCInfo(t, "stack string", new(string), infoString) + verifyGCInfo(t, "stack slice", new([]string), infoSlice) + verifyGCInfo(t, "stack eface", new(interface{}), infoEface) + verifyGCInfo(t, "stack iface", new(Iface), infoIface) for i := 0; i < 10; i++ { - verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), infoPtr10) - verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), infoScalarPtr) - verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), infoScalarPtr4) - verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), infoPtrScalar) - verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), infoBigStruct()) - verifyGCInfo(t, "heap string", escape(new(string)), infoString) - verifyGCInfo(t, "heap eface", escape(new(interface{})), infoEface) - verifyGCInfo(t, "heap iface", escape(new(Iface)), infoIface) + verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), trimDead(infoPtr10)) + verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr)) + verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4)) + verifyGCInfo(t, "heap PtrScalar", escape(new(PtrScalar)), trimDead(infoPtrScalar)) + verifyGCInfo(t, "heap BigStruct", escape(new(BigStruct)), trimDead(infoBigStruct())) + verifyGCInfo(t, "heap string", escape(new(string)), trimDead(infoString)) + verifyGCInfo(t, "heap eface", escape(new(interface{})), trimDead(infoEface)) + verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface)) } } @@ -67,16 +67,11 @@ func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) { } } -func nonStackInfo(mask []byte) []byte { - // typeDead is replaced with typeScalar everywhere except stacks. - mask1 := make([]byte, len(mask)) - for i, v := range mask { - if v == typeDead { - v = typeScalar - } - mask1[i] = v +func trimDead(mask []byte) []byte { + for len(mask) > 2 && mask[len(mask)-1] == typeScalar { + mask = mask[:len(mask)-1] } - return mask1 + return mask } var gcinfoSink interface{} diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index b866d7f732..61e1254bed 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -491,6 +491,8 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) { // but if the start or end of x shares a bitmap byte with an adjacent // object, the GC marker is racing with updates to those object's mark bits. func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { + const doubleCheck = false // slow but helpful; enable to test modifications to this function + // From here till marked label marking the object as allocated // and storing type info in the GC bitmap. h := heapBitsForAddr(x) @@ -518,7 +520,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { ptrmask := (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask if typ.kind&kindGCProg != 0 { - nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize + nptr := typ.ptrdata / ptrSize masksize := (nptr + 7) / 8 masksize++ // unroll flag in the beginning if masksize > maxGCMask && typ.gc[1] != 0 { @@ -568,21 +570,56 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // In general, one load can supply two bitmap byte writes. // This is a lot of lines of code, but it compiles into relatively few // machine instructions. + + // Ptrmask buffer. var ( p *byte // last ptrmask byte read b uintptr // ptrmask bits already loaded - nb uint32 // number of bits in b at next read + nb uintptr // number of bits in b at next read endp *byte // final ptrmask byte to read (then repeat) - endnb uint32 // number of valid bits in *endp + endnb uintptr // number of valid bits in *endp pbits uintptr // alternate source of bits ) + // Note about sizes: + // + // typ.size is the number of words in the object, + // and typ.ptrdata is the number of words in the prefix + // of the object that contains pointers. That is, the final + // typ.size - typ.ptrdata words contain no pointers. + // This allows optimization of a common pattern where + // an object has a small header followed by a large scalar + // buffer. If we know the pointers are over, we don't have + // to scan the buffer's heap bitmap at all. + // The 1-bit ptrmasks are sized to contain only bits for + // the typ.ptrdata prefix, zero padded out to a full byte + // of bitmap. This code sets nw (below) so that heap bitmap + // bits are only written for the typ.ptrdata prefix; if there is + // more room in the allocated object, the next heap bitmap + // entry is a 00, indicating that there are no more pointers + // to scan. So only the ptrmask for the ptrdata bytes is needed. + // + // Replicated copies are not as nice: if there is an array of + // objects with scalar tails, all but the last tail does have to + // be initialized, because there is no way to say "skip forward". + // However, because of the possibility of a repeated type with + // size not a multiple of 4 pointers (one heap bitmap byte), + // the code already must handle the last ptrmask byte specially + // by treating it as containing only the bits for endnb pointers, + // where endnb <= 4. We represent large scalar tails that must + // be expanded in the replication by setting endnb larger than 4. + // This will have the effect of reading many bits out of b, + // but once the real bits are shifted out, b will supply as many + // zero bits as we try to read, which is exactly what we need. + p = ptrmask if typ.size < dataSize { // Filling in bits for an array of typ. // Set up for repetition of ptrmask during main loop. - if typ.size/ptrSize+7 <= ptrSize*8 { - // Entire ptrmask + a leftover fragment fits in uintptr. + // Note that ptrmask describes only a prefix of + const maxBits = ptrSize*8 - 7 + if typ.ptrdata/ptrSize <= maxBits { + // Entire ptrmask fits in uintptr with room for a byte fragment. // Load into pbits and never read from ptrmask again. // This is especially important when the ptrmask has // fewer than 8 bits in it; otherwise the reload in the middle @@ -590,26 +627,34 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // at least 8 bits. // Accumulate ptrmask into b. - nb = uint32(typ.size / ptrSize) - for i := uint32(0); i < nb; i += 8 { + // ptrmask is sized to describe only typ.ptrdata, but we record + // it as describing typ.size bytes, since all the high bits are zero. + nb = typ.ptrdata / ptrSize + for i := uintptr(0); i < nb; i += 8 { b |= uintptr(*p) << i p = addb(p, 1) } + nb = typ.size / ptrSize // Replicate ptrmask to fill entire pbits uintptr. // Doubling and truncating is fewer steps than // iterating by nb each time. (nb could be 1.) + // Since we loaded typ.ptrdata/ptrSize bits + // but are pretending to have typ.size/ptrSize, + // there might be no replication necessary/possible. pbits = b endnb = nb - for endnb <= ptrSize*8 { - pbits |= pbits << endnb - endnb += endnb + if nb+nb <= maxBits { + for endnb <= ptrSize*8 { + pbits |= pbits << endnb + endnb += endnb + } + // Truncate to a multiple of original ptrmask. + endnb = maxBits / nb * nb + pbits &= 1<>= 4 nb -= 4 - case 4: + case ptrSize == 8 && h.shift == 4: // Ptrmask and heap bitmap are misaligned. // The bits for the first two words are in a byte shared with another object // and must be updated atomically. @@ -679,17 +741,13 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // Note: no bitMarker in hb because the first two words don't get markers from us. atomicor8(hbitp, uint8(hb)) hbitp = subtractb(hbitp, 1) - - // Expand 8-bit chunks of ptrmask into pairs of heap bitmap bytes. - // We know the object size is a multiple of 2 words but not 4, so the - // object size minus the 2 words we just handled is a multiple of 4, - // so we can use non-atomic writes to the heap bitmap for the - // rest of this code, even for the final fragment or a trailing dead marker byte. - - // Loop prepares bits for final byte but stops before writing them, - // so that in the case where we need to write only part of a byte, - // the code below the loop can truncate the bitMarked. - w += 2 + if w += 2; w >= nw { + // We know that there is more data, because we handled 2-word objects above. + // This must be at least a 6-word object. If we're out of pointer words, + // mark no scan in next bitmap byte and finish. + *hbitp = 0 + goto Phase4 + } } // Phase 2: Full bytes in bitmap, up to but not including write to last byte (full or partial) in bitmap. @@ -792,24 +850,27 @@ Phase3: } } - const test = false // slow but helpful - if test { +Phase4: + // Phase 4: all done (goto target). + + if doubleCheck { // Double-check that bits to be written were written correctly. // Does not check that other bits were not written, unfortunately. h := heapBitsForAddr(x) - nptr := typ.size / ptrSize + nptr := typ.ptrdata / ptrSize + ndata := typ.size / ptrSize + count := dataSize / typ.size for i := uintptr(0); i <= dataSize/ptrSize; i++ { - j := i % nptr + j := i % ndata var have, want uint8 - if i == dataSize/ptrSize { - if dataSize >= size { - break - } - have = (*h.bitp >> h.shift) & 3 - want = 0 // dead bits + if i == dataSize/ptrSize && dataSize >= size { + break + } + have = (*h.bitp >> h.shift) & 3 + if i == dataSize/ptrSize || i/ndata == count-1 && j >= nptr { + want = 0 // dead marker } else { - have = (*h.bitp >> h.shift) & 3 - if (*addb(ptrmask, j/8)>>(j%8))&1 != 0 { + if j < nptr && (*addb(ptrmask, j/8)>>(j%8))&1 != 0 { want |= bitPointer } if i >= 2 { @@ -820,13 +881,18 @@ Phase3: } if have != want { println("mismatch writing bits for", *typ._string, "x", dataSize/typ.size) - print("typ.size=", typ.size, " dataSize=", dataSize, " size=", size, "\n") + print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") + print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") h = heapBitsForAddr(x) print("initial bits h.bitp=", h.bitp, " h.shift=", h.shift, "\n") - print("p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n") + print("ptrmask=", ptrmask, " p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n") println("at word", i, "offset", i*ptrSize, "have", have, "want", want) throw("bad heapBitsSetType") } + if i >= 2 && want == 0 { + // found dead marker; the rest is uninitialized + break + } h = h.next() } } @@ -1076,7 +1142,7 @@ func getgcmask(ep interface{}) (mask []byte) { mask[i/ptrSize] = 1 } if i >= 2*ptrSize && !hbits.isMarked() { - mask[i/ptrSize] = 255 + mask = mask[:i/ptrSize] break } } From 1635ab7dfef52eb8f6666e5fa38ac5866b561a5b Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 5 May 2015 00:26:53 -0400 Subject: [PATCH 022/232] runtime: remove wbshadow mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The write barrier shadow heap was very useful for developing the write barriers initially, but it's no longer used, clunky, and dragging the rest of the implementation down. The gccheckmark mode will find bugs due to missed barriers when they result in missed marks; wbshadow mode found the missed barriers more aggressively, but it required an entire separate copy of the heap. The gccheckmark mode requires no extra memory, making it more useful in practice. Compared to previous CL: name old mean new mean delta BinaryTree17 5.91s × (0.96,1.06) 5.72s × (0.97,1.03) -3.12% (p=0.000) Fannkuch11 4.32s × (1.00,1.00) 4.36s × (1.00,1.00) +0.91% (p=0.000) FmtFprintfEmpty 89.0ns × (0.93,1.10) 86.6ns × (0.96,1.11) ~ (p=0.077) FmtFprintfString 298ns × (0.98,1.06) 283ns × (0.99,1.04) -4.90% (p=0.000) FmtFprintfInt 286ns × (0.98,1.03) 283ns × (0.98,1.04) -1.09% (p=0.032) FmtFprintfIntInt 498ns × (0.97,1.06) 480ns × (0.99,1.02) -3.65% (p=0.000) FmtFprintfPrefixedInt 408ns × (0.98,1.02) 396ns × (0.99,1.01) -3.00% (p=0.000) FmtFprintfFloat 587ns × (0.98,1.01) 562ns × (0.99,1.01) -4.34% (p=0.000) FmtManyArgs 1.94µs × (0.99,1.02) 1.89µs × (0.99,1.01) -2.85% (p=0.000) GobDecode 15.8ms × (0.98,1.03) 15.7ms × (0.99,1.02) ~ (p=0.251) GobEncode 12.0ms × (0.96,1.09) 11.8ms × (0.98,1.03) -1.87% (p=0.024) Gzip 648ms × (0.99,1.01) 647ms × (0.99,1.01) ~ (p=0.688) Gunzip 143ms × (1.00,1.01) 143ms × (1.00,1.01) ~ (p=0.203) HTTPClientServer 90.3µs × (0.98,1.01) 89.1µs × (0.99,1.02) -1.30% (p=0.000) JSONEncode 31.6ms × (0.99,1.01) 31.7ms × (0.98,1.02) ~ (p=0.219) JSONDecode 107ms × (1.00,1.01) 111ms × (0.99,1.01) +3.58% (p=0.000) Mandelbrot200 6.03ms × (1.00,1.01) 6.01ms × (1.00,1.00) ~ (p=0.077) GoParse 6.53ms × (0.99,1.03) 6.54ms × (0.99,1.02) ~ (p=0.585) RegexpMatchEasy0_32 161ns × (1.00,1.01) 161ns × (0.98,1.05) ~ (p=0.948) RegexpMatchEasy0_1K 541ns × (0.99,1.01) 559ns × (0.98,1.01) +3.32% (p=0.000) RegexpMatchEasy1_32 138ns × (1.00,1.00) 137ns × (0.99,1.01) -0.55% (p=0.001) RegexpMatchEasy1_1K 887ns × (0.99,1.01) 878ns × (0.99,1.01) -0.98% (p=0.000) RegexpMatchMedium_32 253ns × (0.99,1.01) 252ns × (0.99,1.01) -0.39% (p=0.001) RegexpMatchMedium_1K 72.8µs × (1.00,1.00) 72.7µs × (1.00,1.00) ~ (p=0.485) RegexpMatchHard_32 3.85µs × (1.00,1.01) 3.85µs × (1.00,1.01) ~ (p=0.283) RegexpMatchHard_1K 117µs × (1.00,1.01) 117µs × (1.00,1.00) ~ (p=0.175) Revcomp 922ms × (0.97,1.08) 903ms × (0.98,1.05) -2.15% (p=0.021) Template 126ms × (0.99,1.01) 126ms × (0.99,1.01) ~ (p=0.943) TimeParse 628ns × (0.99,1.01) 634ns × (0.99,1.01) +0.92% (p=0.000) TimeFormat 668ns × (0.99,1.01) 698ns × (0.98,1.03) +4.53% (p=0.000) It's nice that the microbenchmarks are the ones helped the most, because those were the ones hurt the most by the conversion from 4-bit to 2-bit heap bitmaps. This CL brings the overall effect of that process to (compared to CL 9706 patch set 1): name old mean new mean delta BinaryTree17 5.87s × (0.94,1.09) 5.72s × (0.97,1.03) -2.57% (p=0.011) Fannkuch11 4.32s × (1.00,1.00) 4.36s × (1.00,1.00) +0.87% (p=0.000) FmtFprintfEmpty 89.1ns × (0.95,1.16) 86.6ns × (0.96,1.11) ~ (p=0.090) FmtFprintfString 283ns × (0.98,1.02) 283ns × (0.99,1.04) ~ (p=0.681) FmtFprintfInt 284ns × (0.98,1.04) 283ns × (0.98,1.04) ~ (p=0.620) FmtFprintfIntInt 486ns × (0.98,1.03) 480ns × (0.99,1.02) -1.27% (p=0.002) FmtFprintfPrefixedInt 400ns × (0.99,1.02) 396ns × (0.99,1.01) -0.84% (p=0.001) FmtFprintfFloat 566ns × (0.99,1.01) 562ns × (0.99,1.01) -0.80% (p=0.000) FmtManyArgs 1.91µs × (0.99,1.02) 1.89µs × (0.99,1.01) -1.10% (p=0.000) GobDecode 15.5ms × (0.98,1.05) 15.7ms × (0.99,1.02) +1.55% (p=0.005) GobEncode 11.9ms × (0.97,1.03) 11.8ms × (0.98,1.03) -0.97% (p=0.048) Gzip 648ms × (0.99,1.01) 647ms × (0.99,1.01) ~ (p=0.627) Gunzip 143ms × (1.00,1.00) 143ms × (1.00,1.01) ~ (p=0.482) HTTPClientServer 89.2µs × (0.99,1.02) 89.1µs × (0.99,1.02) ~ (p=0.740) JSONEncode 32.3ms × (0.97,1.06) 31.7ms × (0.98,1.02) -1.95% (p=0.002) JSONDecode 106ms × (0.99,1.01) 111ms × (0.99,1.01) +4.22% (p=0.000) Mandelbrot200 6.02ms × (1.00,1.00) 6.01ms × (1.00,1.00) ~ (p=0.417) GoParse 6.57ms × (0.97,1.06) 6.54ms × (0.99,1.02) ~ (p=0.404) RegexpMatchEasy0_32 162ns × (1.00,1.00) 161ns × (0.98,1.05) ~ (p=0.088) RegexpMatchEasy0_1K 561ns × (0.99,1.02) 559ns × (0.98,1.01) -0.47% (p=0.034) RegexpMatchEasy1_32 145ns × (0.95,1.04) 137ns × (0.99,1.01) -5.56% (p=0.000) RegexpMatchEasy1_1K 864ns × (0.99,1.04) 878ns × (0.99,1.01) +1.57% (p=0.000) RegexpMatchMedium_32 255ns × (0.99,1.04) 252ns × (0.99,1.01) -1.43% (p=0.001) RegexpMatchMedium_1K 73.9µs × (0.98,1.04) 72.7µs × (1.00,1.00) -1.55% (p=0.004) RegexpMatchHard_32 3.92µs × (0.98,1.04) 3.85µs × (1.00,1.01) -1.80% (p=0.003) RegexpMatchHard_1K 120µs × (0.98,1.04) 117µs × (1.00,1.00) -2.13% (p=0.001) Revcomp 936ms × (0.95,1.08) 903ms × (0.98,1.05) -3.58% (p=0.002) Template 130ms × (0.98,1.04) 126ms × (0.99,1.01) -2.98% (p=0.000) TimeParse 638ns × (0.98,1.05) 634ns × (0.99,1.01) ~ (p=0.198) TimeFormat 674ns × (0.99,1.01) 698ns × (0.98,1.03) +3.69% (p=0.000) Change-Id: Ia0e9b50b1d75a3c0c7556184cd966305574fe07c Reviewed-on: https://go-review.googlesource.com/9706 Reviewed-by: Rick Hudson --- src/runtime/atomic_pointer.go | 18 ---- src/runtime/extern.go | 12 --- src/runtime/malloc.go | 7 -- src/runtime/mbarrier.go | 196 +--------------------------------- src/runtime/mgc.go | 2 +- src/runtime/mgcmark.go | 7 -- src/runtime/mheap.go | 8 -- src/runtime/panic.go | 16 --- src/runtime/proc1.go | 1 - src/runtime/symtab.go | 7 -- 10 files changed, 2 insertions(+), 272 deletions(-) diff --git a/src/runtime/atomic_pointer.go b/src/runtime/atomic_pointer.go index 50a30242d9..f84afe0362 100644 --- a/src/runtime/atomic_pointer.go +++ b/src/runtime/atomic_pointer.go @@ -20,18 +20,12 @@ import "unsafe" func atomicstorep(ptr unsafe.Pointer, new unsafe.Pointer) { atomicstorep1(noescape(ptr), new) writebarrierptr_nostore((*uintptr)(ptr), uintptr(new)) - if mheap_.shadow_enabled { - writebarrierptr_noshadow((*uintptr)(noescape(ptr))) - } } //go:nosplit func xchgp(ptr unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer { old := xchgp1(noescape(ptr), new) writebarrierptr_nostore((*uintptr)(ptr), uintptr(new)) - if mheap_.shadow_enabled { - writebarrierptr_noshadow((*uintptr)(noescape(ptr))) - } return old } @@ -41,9 +35,6 @@ func casp(ptr *unsafe.Pointer, old, new unsafe.Pointer) bool { return false } writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) - if mheap_.shadow_enabled { - writebarrierptr_noshadow((*uintptr)(noescape(unsafe.Pointer(ptr)))) - } return true } @@ -60,9 +51,6 @@ func sync_atomic_StorePointer(ptr *unsafe.Pointer, new unsafe.Pointer) { sync_atomic_StoreUintptr((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) atomicstorep1(noescape(unsafe.Pointer(ptr)), new) writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) - if mheap_.shadow_enabled { - writebarrierptr_noshadow((*uintptr)(noescape(unsafe.Pointer(ptr)))) - } } //go:linkname sync_atomic_SwapUintptr sync/atomic.SwapUintptr @@ -73,9 +61,6 @@ func sync_atomic_SwapUintptr(ptr *uintptr, new uintptr) uintptr func sync_atomic_SwapPointer(ptr unsafe.Pointer, new unsafe.Pointer) unsafe.Pointer { old := unsafe.Pointer(sync_atomic_SwapUintptr((*uintptr)(noescape(ptr)), uintptr(new))) writebarrierptr_nostore((*uintptr)(ptr), uintptr(new)) - if mheap_.shadow_enabled { - writebarrierptr_noshadow((*uintptr)(noescape(ptr))) - } return old } @@ -89,8 +74,5 @@ func sync_atomic_CompareAndSwapPointer(ptr *unsafe.Pointer, old, new unsafe.Poin return false } writebarrierptr_nostore((*uintptr)(unsafe.Pointer(ptr)), uintptr(new)) - if mheap_.shadow_enabled { - writebarrierptr_noshadow((*uintptr)(noescape(unsafe.Pointer(ptr)))) - } return true } diff --git a/src/runtime/extern.go b/src/runtime/extern.go index 540d7b5124..476c3c5ae3 100644 --- a/src/runtime/extern.go +++ b/src/runtime/extern.go @@ -58,18 +58,6 @@ a comma-separated list of name=val pairs. Supported names are: scavenge: scavenge=1 enables debugging mode of heap scavenger. - wbshadow: setting wbshadow=1 enables a shadow copy of the heap - used to detect missing write barriers at the next write to a - given location. If a bug can be detected in this mode it is - typically easy to understand, since the crash says quite - clearly what kind of word has missed a write barrier. - Setting wbshadow=2 checks the shadow copy during garbage - collection as well. Bugs detected at garbage collection can be - difficult to understand, because there is no context for what - the found word means. Typically you have to reproduce the - problem with allocfreetrace=1 in order to understand the type - of the badly updated word. - gccheckmark: setting gccheckmark=1 enables verification of the garbage collector's concurrent mark phase by performing a second mark pass while the world is stopped. If the second diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go index 1619ccb9f4..a0cd8bb433 100644 --- a/src/runtime/malloc.go +++ b/src/runtime/malloc.go @@ -424,9 +424,6 @@ func mHeap_SysAlloc(h *mheap, n uintptr) unsafe.Pointer { if raceenabled { racemapshadow((unsafe.Pointer)(p), n) } - if mheap_.shadow_enabled { - sysMap(unsafe.Pointer(p+mheap_.shadow_heap), n, h.shadow_reserved, &memstats.other_sys) - } if uintptr(p)&(_PageSize-1) != 0 { throw("misrounded allocation in MHeap_SysAlloc") @@ -669,10 +666,6 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer { }) } - if mheap_.shadow_enabled { - clearshadow(uintptr(x), size) - } - if raceenabled { racemalloc(x, size) } diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index 4162483ade..eb41a60087 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -10,12 +10,6 @@ // implementation, markwb, and the various wrappers called by the // compiler to implement pointer assignment, slice assignment, // typed memmove, and so on. -// -// To check for missed write barriers, the GODEBUG=wbshadow debugging -// mode allocates a second copy of the heap. Write barrier-based pointer -// updates make changes to both the real heap and the shadow, and both -// the pointer updates and the GC look for inconsistencies between the two, -// indicating pointer writes that bypassed the barrier. package runtime @@ -107,43 +101,16 @@ func writebarrierptr_nostore1(dst *uintptr, src uintptr) { // but if we do that, Go inserts a write barrier on *dst = src. //go:nosplit func writebarrierptr(dst *uintptr, src uintptr) { + *dst = src if !writeBarrierEnabled { - *dst = src return } - if src != 0 && (src < _PhysPageSize || src == poisonStack) { systemstack(func() { throw("bad pointer in write barrier") }) } - - if mheap_.shadow_enabled { - writebarrierptr_shadow(dst, src) - } - - *dst = src writebarrierptr_nostore1(dst, src) } -//go:nosplit -func writebarrierptr_shadow(dst *uintptr, src uintptr) { - systemstack(func() { - addr := uintptr(unsafe.Pointer(dst)) - shadow := shadowptr(addr) - if shadow == nil { - return - } - // There is a race here but only if the program is using - // racy writes instead of sync/atomic. In that case we - // don't mind crashing. - if *shadow != *dst && *shadow != noShadow && istrackedptr(*dst) { - mheap_.shadow_enabled = false - print("runtime: write barrier dst=", dst, " old=", hex(*dst), " shadow=", shadow, " old=", hex(*shadow), " new=", hex(src), "\n") - throw("missed write barrier") - } - *shadow = src - }) -} - // Like writebarrierptr, but the store has already been applied. // Do not reapply. //go:nosplit @@ -151,44 +118,12 @@ func writebarrierptr_nostore(dst *uintptr, src uintptr) { if !writeBarrierEnabled { return } - if src != 0 && (src < _PhysPageSize || src == poisonStack) { systemstack(func() { throw("bad pointer in write barrier") }) } - - // Apply changes to shadow. - // Since *dst has been overwritten already, we cannot check - // whether there were any missed updates, but writebarrierptr_nostore - // is only rarely used. - if mheap_.shadow_enabled { - systemstack(func() { - addr := uintptr(unsafe.Pointer(dst)) - shadow := shadowptr(addr) - if shadow == nil { - return - } - *shadow = src - }) - } - writebarrierptr_nostore1(dst, src) } -// writebarrierptr_noshadow records that the value in *dst -// has been written to using an atomic operation and the shadow -// has not been updated. (In general if dst must be manipulated -// atomically we cannot get the right bits for use in the shadow.) -//go:nosplit -func writebarrierptr_noshadow(dst *uintptr) { - addr := uintptr(unsafe.Pointer(dst)) - shadow := shadowptr(addr) - if shadow == nil { - return - } - - *shadow = noShadow -} - //go:nosplit func writebarrierstring(dst *[2]uintptr, src [2]uintptr) { writebarrierptr(&dst[0], src[0]) @@ -394,132 +329,3 @@ func typedslicecopy(typ *_type, dst, src slice) int { func reflect_typedslicecopy(elemType *_type, dst, src slice) int { return typedslicecopy(elemType, dst, src) } - -// Shadow heap for detecting missed write barriers. - -// noShadow is stored in as the shadow pointer to mark that there is no -// shadow word recorded. It matches any actual pointer word. -// noShadow is used when it is impossible to know the right word -// to store in the shadow heap, such as when the real heap word -// is being manipulated atomically. -const noShadow uintptr = 1 - -func wbshadowinit() { - // Initialize write barrier shadow heap if we were asked for it - // and we have enough address space (not on 32-bit). - if debug.wbshadow == 0 { - return - } - if ptrSize != 8 { - print("runtime: GODEBUG=wbshadow=1 disabled on 32-bit system\n") - return - } - - var reserved bool - p1 := sysReserveHigh(mheap_.arena_end-mheap_.arena_start, &reserved) - if p1 == nil { - throw("cannot map shadow heap") - } - mheap_.shadow_heap = uintptr(p1) - mheap_.arena_start - sysMap(p1, mheap_.arena_used-mheap_.arena_start, reserved, &memstats.other_sys) - memmove(p1, unsafe.Pointer(mheap_.arena_start), mheap_.arena_used-mheap_.arena_start) - - mheap_.shadow_reserved = reserved - - for datap := &firstmoduledata; datap != nil; datap = datap.next { - start := ^uintptr(0) - end := uintptr(0) - if start > datap.noptrdata { - start = datap.noptrdata - } - if start > datap.data { - start = datap.data - } - if start > datap.noptrbss { - start = datap.noptrbss - } - if start > datap.bss { - start = datap.bss - } - if end < datap.enoptrdata { - end = datap.enoptrdata - } - if end < datap.edata { - end = datap.edata - } - if end < datap.enoptrbss { - end = datap.enoptrbss - } - if end < datap.ebss { - end = datap.ebss - } - start &^= _PhysPageSize - 1 - end = round(end, _PhysPageSize) - datap.data_start = start - datap.data_end = end - reserved = false - p1 = sysReserveHigh(end-start, &reserved) - if p1 == nil { - throw("cannot map shadow data") - } - datap.shadow_data = uintptr(p1) - start - sysMap(p1, end-start, reserved, &memstats.other_sys) - memmove(p1, unsafe.Pointer(start), end-start) - } - - mheap_.shadow_enabled = true - writeBarrierEnabled = true -} - -// shadowptr returns a pointer to the shadow value for addr. -//go:nosplit -func shadowptr(addr uintptr) *uintptr { - for datap := &firstmoduledata; datap != nil; datap = datap.next { - if datap.data_start <= addr && addr < datap.data_end { - return (*uintptr)(unsafe.Pointer(addr + datap.shadow_data)) - } - } - if inheap(addr) { - return (*uintptr)(unsafe.Pointer(addr + mheap_.shadow_heap)) - } - return nil -} - -// istrackedptr reports whether the pointer value p requires a write barrier -// when stored into the heap. -func istrackedptr(p uintptr) bool { - return inheap(p) -} - -// checkwbshadow checks that p matches its shadow word. -// The garbage collector calls checkwbshadow for each pointer during the checkmark phase. -// It is only called when mheap_.shadow_enabled is true. -func checkwbshadow(p *uintptr) { - addr := uintptr(unsafe.Pointer(p)) - shadow := shadowptr(addr) - if shadow == nil { - return - } - // There is no race on the accesses here, because the world is stopped, - // but there may be racy writes that lead to the shadow and the - // heap being inconsistent. If so, we will detect that here as a - // missed write barrier and crash. We don't mind. - // Code should use sync/atomic instead of racy pointer writes. - if *shadow != *p && *shadow != noShadow && istrackedptr(*p) { - mheap_.shadow_enabled = false - print("runtime: checkwritebarrier p=", p, " *p=", hex(*p), " shadow=", shadow, " *shadow=", hex(*shadow), "\n") - throw("missed write barrier") - } -} - -// clearshadow clears the shadow copy associated with the n bytes of memory at addr. -func clearshadow(addr, n uintptr) { - if !mheap_.shadow_enabled { - return - } - p := shadowptr(addr) - if p == nil || n <= ptrSize { - return - } - memclr(unsafe.Pointer(p), n) -} diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go index 9bd36d1a5e..11e885f928 100644 --- a/src/runtime/mgc.go +++ b/src/runtime/mgc.go @@ -208,7 +208,7 @@ const ( //go:nosplit func setGCPhase(x uint32) { atomicstore(&gcphase, x) - writeBarrierEnabled = gcphase == _GCmark || gcphase == _GCmarktermination || mheap_.shadow_enabled + writeBarrierEnabled = gcphase == _GCmark || gcphase == _GCmarktermination } // gcMarkWorkerMode represents the mode that a concurrent mark worker diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go index bf21e47d83..460997880b 100644 --- a/src/runtime/mgcmark.go +++ b/src/runtime/mgcmark.go @@ -557,9 +557,6 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) { // Same work as in scanobject; see comments there. obj := *(*uintptr)(unsafe.Pointer(b + i)) if obj != 0 && arena_start <= obj && obj < arena_used { - if mheap_.shadow_enabled && debug.wbshadow >= 2 && debug.gccheckmark > 0 && useCheckmark { - checkwbshadow((*uintptr)(unsafe.Pointer(b + i))) - } if obj, hbits, span := heapBitsForObject(obj); obj != 0 { greyobject(obj, b, i, hbits, span, gcw) } @@ -616,10 +613,6 @@ func scanobject(b uintptr, gcw *gcWork) { // At this point we have extracted the next potential pointer. // Check if it points into heap. if obj != 0 && arena_start <= obj && obj < arena_used { - if mheap_.shadow_enabled && debug.wbshadow >= 2 && debug.gccheckmark > 0 && useCheckmark { - checkwbshadow((*uintptr)(unsafe.Pointer(b + i))) - } - // Mark the object. if obj, hbits, span := heapBitsForObject(obj); obj != 0 { greyobject(obj, b, i, hbits, span, gcw) diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 10878ee5cf..48e391648b 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -36,14 +36,6 @@ type mheap struct { arena_end uintptr arena_reserved bool - // write barrier shadow heap. - // 64-bit systems only, enabled by GODEBUG=wbshadow=1. - // See also shadow_data, data_start, data_end fields on moduledata in - // symtab.go. - shadow_enabled bool // shadow should be updated and checked - shadow_reserved bool // shadow memory is reserved - shadow_heap uintptr // heap-addr + shadow_heap = shadow heap addr - // central free lists for small size classes. // the padding makes sure that the MCentrals are // spaced CacheLineSize bytes apart, so that each MCentral.lock diff --git a/src/runtime/panic.go b/src/runtime/panic.go index 0e4086c7ef..47563f450e 100644 --- a/src/runtime/panic.go +++ b/src/runtime/panic.go @@ -188,16 +188,6 @@ func newdefer(siz int32) *_defer { d = (*_defer)(mallocgc(total, deferType, 0)) } d.siz = siz - if mheap_.shadow_enabled { - // This memory will be written directly, with no write barrier, - // and then scanned like stacks during collection. - // Unlike real stacks, it is from heap spans, so mark the - // shadow as explicitly unusable. - p := deferArgs(d) - for i := uintptr(0); i+ptrSize <= uintptr(siz); i += ptrSize { - writebarrierptr_noshadow((*uintptr)(add(p, i))) - } - } gp := mp.curg d.link = gp._defer gp._defer = d @@ -214,12 +204,6 @@ func freedefer(d *_defer) { if d.fn != nil { freedeferfn() } - if mheap_.shadow_enabled { - // Undo the marking in newdefer. - systemstack(func() { - clearshadow(uintptr(deferArgs(d)), uintptr(d.siz)) - }) - } sc := deferclass(uintptr(d.siz)) if sc < uintptr(len(p{}.deferpool)) { mp := acquirem() diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go index 6bd90ece31..01c46a85ec 100644 --- a/src/runtime/proc1.go +++ b/src/runtime/proc1.go @@ -59,7 +59,6 @@ func schedinit() { goargs() goenvs() parsedebugvars() - wbshadowinit() gcinit() sched.lastpoll = uint64(nanotime()) diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 25f5bf46fb..bbf00bf134 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -50,13 +50,6 @@ type moduledata struct { gcdatamask, gcbssmask bitvector - // write barrier shadow data - // 64-bit systems only, enabled by GODEBUG=wbshadow=1. - // See also the shadow_* fields on mheap in mheap.go. - shadow_data uintptr // data-addr + shadow_data = shadow data addr - data_start uintptr // start of shadowed data addresses - data_end uintptr // end of shadowed data addresses - next *moduledata } From 351897d9d4f61dba5bd19270463c393a58d1f2ce Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 7 May 2015 10:34:12 -0400 Subject: [PATCH 023/232] cmd/internal/gc: emit branches in -g mode The -g mode is a debugging mode that prints instructions as they are constructed. Gbranch was just missing the print. Change-Id: I3fb45fd9bd3996ed96df5be903b9fd6bd97148b0 Reviewed-on: https://go-review.googlesource.com/9827 Reviewed-by: Rick Hudson --- src/cmd/internal/gc/gsubr.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/internal/gc/gsubr.go index 53b3f6c41d..9b75bb6109 100644 --- a/src/cmd/internal/gc/gsubr.go +++ b/src/cmd/internal/gc/gsubr.go @@ -90,6 +90,10 @@ func Gbranch(as int, t *Type, likely int) *obj.Prog { p.From.Offset = int64(obj.Bool2int(likely > 0)) } + if Debug['g'] != 0 { + fmt.Printf("%v\n", p) + } + return p } From 8f037fa1ab223f48750117219cd4fff8c6575970 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 5 May 2015 21:55:09 -0400 Subject: [PATCH 024/232] runtime: fix TestLFStack on 386 The new(uint64) was moving to the stack, which may not be aligned. Change-Id: Iad070964202001b52029494d43e299fed980f939 Reviewed-on: https://go-review.googlesource.com/9787 Reviewed-by: Brad Fitzpatrick Reviewed-by: David Chase --- src/runtime/lfstack_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/runtime/lfstack_test.go b/src/runtime/lfstack_test.go index 68f221d6ef..4da4d88619 100644 --- a/src/runtime/lfstack_test.go +++ b/src/runtime/lfstack_test.go @@ -24,8 +24,12 @@ func toMyNode(node *LFNode) *MyNode { return (*MyNode)(unsafe.Pointer(node)) } +var global interface{} + func TestLFStack(t *testing.T) { stack := new(uint64) + global = stack // force heap allocation + // Need to keep additional referenfces to nodes, the stack is not all that type-safe. var nodes []*MyNode From c70b4b5f7e419214c8c61ba5306b569fbab7dc30 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:22:05 -0400 Subject: [PATCH 025/232] cmd/internal/gc: show register dump before crashing on register left allocated If you are using -h to get a stack trace at the site of the failure, Yyerror will never return. Dump the register allocation sites before calling Yyerror. Change-Id: I51266c03e06cb5084c2eaa89b367b9ed85ba286a Reviewed-on: https://go-review.googlesource.com/9788 Reviewed-by: Josh Bleecher Snyder Reviewed-by: Dave Cheney --- src/cmd/internal/gc/gsubr.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/internal/gc/gsubr.go index 9b75bb6109..93e4852b6f 100644 --- a/src/cmd/internal/gc/gsubr.go +++ b/src/cmd/internal/gc/gsubr.go @@ -625,20 +625,20 @@ func gclean() { for r := Thearch.REGMIN; r <= Thearch.REGMAX; r++ { n := reg[r-Thearch.REGMIN] if n != 0 { - Yyerror("reg %v left allocated", obj.Rconv(r)) if Debug['v'] != 0 { Regdump() } + Yyerror("reg %v left allocated", obj.Rconv(r)) } } for r := Thearch.FREGMIN; r <= Thearch.FREGMAX; r++ { n := reg[r-Thearch.REGMIN] if n != 0 { - Yyerror("reg %v left allocated", obj.Rconv(r)) if Debug['v'] != 0 { Regdump() } + Yyerror("reg %v left allocated", obj.Rconv(r)) } } } From dcf6e20606bb7e3920fef186d87e69742f7986fb Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:25:56 -0400 Subject: [PATCH 026/232] cmd/internal/gc: drop unused Reslice field from Node Dead code. This field is left over from Go 1.4, when we elided the fake write barrier in this case. Today, it's unused (always false). The upcoming append/slice changes handle this case again, but without needing this field. Change-Id: Ic6f160b64efdc1bbed02097ee03050f8cd0ab1b8 Reviewed-on: https://go-review.googlesource.com/9789 Reviewed-by: David Chase --- src/cmd/internal/gc/syntax.go | 1 - src/cmd/internal/gc/typecheck.go | 23 ----------------------- src/cmd/internal/gc/walk.go | 20 -------------------- 3 files changed, 44 deletions(-) diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go index 7c9fb8d2b8..70c6f3f567 100644 --- a/src/cmd/internal/gc/syntax.go +++ b/src/cmd/internal/gc/syntax.go @@ -48,7 +48,6 @@ type Node struct { Assigned bool // is the variable ever assigned to Captured bool // is the variable captured by a closure Byval bool // is the variable captured by value or by reference - Reslice bool // this is a reslice x = x[0:y] or x = append(x, ...) Likely int8 // likeliness of if statement Hasbreak bool // has break statement Needzero bool // if it contains pointers, needs to be zeroed on function entry diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go index 6daf842474..fdd393d0cf 100644 --- a/src/cmd/internal/gc/typecheck.go +++ b/src/cmd/internal/gc/typecheck.go @@ -3360,29 +3360,6 @@ func typecheckas(n *Node) { if n.Left.Typecheck == 0 { typecheck(&n.Left, Erv|Easgn) } - - // Recognize slices being updated in place, for better code generation later. - // Don't rewrite if using race detector, to avoid needing to teach race detector - // about this optimization. - if n.Left != nil && n.Left.Op != OINDEXMAP && n.Right != nil && flag_race == 0 { - switch n.Right.Op { - // For x = x[0:y], x can be updated in place, without touching pointer. - // TODO(rsc): Reenable once it is actually updated in place without touching the pointer. - case OSLICE, OSLICE3, OSLICESTR: - if false && samesafeexpr(n.Left, n.Right.Left) && (n.Right.Right.Left == nil || iszero(n.Right.Right.Left)) { - n.Right.Reslice = true - } - - // For x = append(x, ...), x can be updated in place when there is capacity, - // without touching the pointer; otherwise the emitted code to growslice - // can take care of updating the pointer, and only in that case. - // TODO(rsc): Reenable once the emitted code does update the pointer. - case OAPPEND: - if false && n.Right.List != nil && samesafeexpr(n.Left, n.Right.List.N) { - n.Right.Reslice = true - } - } - } } func checkassignto(src *Type, dst *Node) { diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go index c32a8137d6..c8a5c7e2f3 100644 --- a/src/cmd/internal/gc/walk.go +++ b/src/cmd/internal/gc/walk.go @@ -2185,26 +2185,6 @@ func needwritebarrier(l *Node, r *Node) bool { return false } - // No write barrier for reslice: x = x[0:y] or x = append(x, ...). - // Both are compiled to modify x directly. - // In the case of append, a write barrier may still be needed - // if the underlying array grows, but the append code can - // generate the write barrier directly in that case. - // (It does not yet, but the cost of the write barrier will be - // small compared to the cost of the allocation.) - if r.Reslice { - switch r.Op { - case OSLICE, OSLICE3, OSLICESTR, OAPPEND: - break - - default: - Dump("bad reslice-l", l) - Dump("bad reslice-r", r) - } - - return false - } - // Otherwise, be conservative and use write barrier. return true } From fc595b78d216e9470afce5d99c002b4bf5760c2c Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:30:59 -0400 Subject: [PATCH 027/232] cmd/internal/gc: mark panicindex calls as not returning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most of the calls to panicindex are already marked as not returning, but these two were missed at some point. Performance changes below. name old mean new mean delta BinaryTree17 5.70s × (0.98,1.04) 5.68s × (0.97,1.04) ~ (p=0.681) Fannkuch11 4.32s × (1.00,1.00) 4.41s × (0.98,1.03) +1.98% (p=0.018) FmtFprintfEmpty 92.6ns × (0.91,1.11) 92.7ns × (0.91,1.16) ~ (p=0.969) FmtFprintfString 280ns × (0.97,1.05) 281ns × (0.96,1.08) ~ (p=0.860) FmtFprintfInt 284ns × (0.99,1.02) 288ns × (0.97,1.06) ~ (p=0.207) FmtFprintfIntInt 488ns × (0.98,1.01) 493ns × (0.97,1.04) ~ (p=0.271) FmtFprintfPrefixedInt 418ns × (0.98,1.04) 423ns × (0.97,1.04) ~ (p=0.311) FmtFprintfFloat 597ns × (1.00,1.00) 598ns × (0.99,1.01) ~ (p=0.789) FmtManyArgs 1.87µs × (0.99,1.01) 1.89µs × (0.98,1.05) ~ (p=0.158) GobDecode 14.6ms × (0.99,1.01) 14.8ms × (0.98,1.03) +1.51% (p=0.015) GobEncode 12.3ms × (0.98,1.03) 12.3ms × (0.98,1.01) ~ (p=0.474) Gzip 647ms × (1.00,1.01) 656ms × (0.99,1.05) ~ (p=0.104) Gunzip 142ms × (1.00,1.00) 142ms × (1.00,1.00) ~ (p=0.110) HTTPClientServer 89.6µs × (0.99,1.03) 91.2µs × (0.97,1.04) ~ (p=0.061) JSONEncode 31.7ms × (0.99,1.01) 32.6ms × (0.97,1.08) +2.87% (p=0.038) JSONDecode 111ms × (1.00,1.01) 114ms × (0.97,1.05) +2.47% (p=0.040) Mandelbrot200 6.01ms × (1.00,1.00) 6.11ms × (0.98,1.04) ~ (p=0.073) GoParse 6.54ms × (0.99,1.02) 6.66ms × (0.97,1.04) ~ (p=0.064) RegexpMatchEasy0_32 159ns × (0.99,1.02) 159ns × (0.99,1.00) ~ (p=0.693) RegexpMatchEasy0_1K 540ns × (0.99,1.03) 538ns × (1.00,1.01) ~ (p=0.360) RegexpMatchEasy1_32 137ns × (0.99,1.01) 138ns × (1.00,1.00) ~ (p=0.511) RegexpMatchEasy1_1K 867ns × (1.00,1.01) 869ns × (0.99,1.01) ~ (p=0.193) RegexpMatchMedium_32 252ns × (1.00,1.00) 252ns × (0.99,1.01) ~ (p=0.076) RegexpMatchMedium_1K 72.7µs × (1.00,1.00) 72.7µs × (1.00,1.00) ~ (p=0.963) RegexpMatchHard_32 3.84µs × (1.00,1.00) 3.85µs × (1.00,1.00) ~ (p=0.371) RegexpMatchHard_1K 117µs × (1.00,1.01) 118µs × (1.00,1.00) ~ (p=0.898) Revcomp 909ms × (0.98,1.03) 920ms × (0.97,1.07) ~ (p=0.368) Template 128ms × (0.99,1.01) 129ms × (0.98,1.03) +1.41% (p=0.042) TimeParse 619ns × (0.98,1.01) 619ns × (0.99,1.01) ~ (p=0.730) TimeFormat 651ns × (1.00,1.01) 661ns × (0.98,1.04) ~ (p=0.097) Change-Id: I0ec5baff41f5d282307137ce0d927e6301e4fa10 Reviewed-on: https://go-review.googlesource.com/9811 Reviewed-by: David Chase --- src/cmd/internal/gc/cgen.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go index 501cdcb1c8..6f8187d953 100644 --- a/src/cmd/internal/gc/cgen.go +++ b/src/cmd/internal/gc/cgen.go @@ -1062,7 +1062,7 @@ func Agenr(n *Node, a *Node, res *Node) { Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT32]), &n4, &n2) Regfree(&n4) p1 := Gbranch(Thearch.Optoas(OGT, Types[TUINT32]), nil, +1) - Ginscall(Panicindex, 0) + Ginscall(Panicindex, -1) Patch(p1, Pc) } @@ -1108,7 +1108,7 @@ func Agenr(n *Node, a *Node, res *Node) { if p2 != nil { Patch(p2, Pc) } - Ginscall(Panicindex, 0) + Ginscall(Panicindex, -1) Patch(p1, Pc) } From 363fd1dd1b810dd099f6aec1f110141fd8efb301 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 11 May 2015 12:03:30 -0400 Subject: [PATCH 028/232] runtime: move a few atomic fields up Moving them up makes them properly aligned on 32-bit systems. There are some odd fields above them right now (like fixalloc and mutex maybe). Change-Id: I57851a5bbb2e7cc339712f004f99bb6c0cce0ca5 Reviewed-on: https://go-review.googlesource.com/9889 Reviewed-by: Austin Clements --- src/runtime/mheap.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go index 48e391648b..a610da2e47 100644 --- a/src/runtime/mheap.go +++ b/src/runtime/mheap.go @@ -28,6 +28,15 @@ type mheap struct { spans **mspan spans_mapped uintptr + // Proportional sweep + pagesSwept uint64 // pages swept this cycle; updated atomically + sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without + + // Malloc stats. + largefree uint64 // bytes freed for large objects (>maxsmallsize) + nlargefree uint64 // number of frees for large objects (>maxsmallsize) + nsmallfree [_NumSizeClasses]uint64 // number of frees for small objects (<=maxsmallsize) + // range of addresses we might see in the heap bitmap uintptr bitmap_mapped uintptr @@ -50,15 +59,6 @@ type mheap struct { specialfinalizeralloc fixalloc // allocator for specialfinalizer* specialprofilealloc fixalloc // allocator for specialprofile* speciallock mutex // lock for sepcial record allocators. - - // Proportional sweep - pagesSwept uint64 // pages swept this cycle; updated atomically - sweepPagesPerByte float64 // proportional sweep ratio; written with lock, read without - - // Malloc stats. - largefree uint64 // bytes freed for large objects (>maxsmallsize) - nlargefree uint64 // number of frees for large objects (>maxsmallsize) - nsmallfree [_NumSizeClasses]uint64 // number of frees for small objects (<=maxsmallsize) } var mheap_ mheap From e375ca2a25ba08806e2dbc060a79ef79849d688e Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 7 May 2015 11:03:17 -0400 Subject: [PATCH 029/232] runtime: reorder bits in heap bitmap bytes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The runtime deals with 1-bit pointer bitmaps and 2-bit heap bitmaps that have entries for both pointers and mark bits. Each byte in a 1-bit pointer bitmap looks like pppppppp (all pointer bits). Each byte in a 2-bit heap bitmap looks like mpmpmpmp (mark, pointer, ...). This means that when converting from 1-bit to 2-bit, as we do during malloc, we have to pick up 4 bits in pppp form and use shifts to create the mpmpmpmp form. This CL changes the 2-bit heap bitmap form to mmmmpppp, so that 4 bits picked up in 1-bit form can be used directly in the low bits of the heap bitmap byte, without expansion. This simplifies the code, and it also happens to be faster. name old mean new mean delta SetTypePtr 14.0ns × (0.98,1.09) 14.0ns × (0.98,1.08) ~ (p=0.966) SetTypePtr8 16.5ns × (0.99,1.05) 15.3ns × (0.96,1.16) -6.86% (p=0.012) SetTypePtr16 21.3ns × (0.98,1.05) 18.8ns × (0.94,1.14) -11.49% (p=0.000) SetTypePtr32 34.6ns × (0.93,1.22) 27.7ns × (0.91,1.26) -20.08% (p=0.001) SetTypePtr64 55.7ns × (0.97,1.11) 41.6ns × (0.98,1.04) -25.30% (p=0.000) SetTypePtr126 98.0ns × (1.00,1.00) 67.7ns × (0.99,1.05) -30.88% (p=0.000) SetTypePtr128 98.6ns × (1.00,1.01) 68.6ns × (0.99,1.03) -30.44% (p=0.000) SetTypePtrSlice 781ns × (0.99,1.01) 571ns × (0.99,1.04) -26.93% (p=0.000) SetTypeNode1 13.1ns × (0.99,1.01) 12.1ns × (0.99,1.01) -7.45% (p=0.000) SetTypeNode1Slice 113ns × (0.99,1.01) 94ns × (1.00,1.00) -16.35% (p=0.000) SetTypeNode8 32.7ns × (1.00,1.00) 29.8ns × (0.99,1.01) -8.97% (p=0.000) SetTypeNode8Slice 266ns × (1.00,1.00) 204ns × (1.00,1.00) -23.40% (p=0.000) SetTypeNode64 58.0ns × (0.98,1.08) 42.8ns × (1.00,1.01) -26.24% (p=0.000) SetTypeNode64Slice 1.55µs × (0.99,1.02) 0.96µs × (1.00,1.00) -37.84% (p=0.000) SetTypeNode64Dead 13.1ns × (0.99,1.01) 12.1ns × (1.00,1.00) -7.33% (p=0.000) SetTypeNode64DeadSlice 1.52µs × (1.00,1.01) 1.08µs × (1.00,1.01) -28.95% (p=0.000) SetTypeNode124 97.9ns × (1.00,1.00) 67.1ns × (1.00,1.01) -31.49% (p=0.000) SetTypeNode124Slice 2.87µs × (0.99,1.02) 1.75µs × (1.00,1.01) -39.15% (p=0.000) SetTypeNode126 98.4ns × (1.00,1.01) 68.1ns × (1.00,1.01) -30.79% (p=0.000) SetTypeNode126Slice 2.91µs × (0.99,1.01) 1.77µs × (0.99,1.01) -39.09% (p=0.000) SetTypeNode1024 732ns × (1.00,1.00) 511ns × (0.87,1.42) -30.14% (p=0.000) SetTypeNode1024Slice 23.1µs × (1.00,1.00) 13.9µs × (0.99,1.02) -39.83% (p=0.000) Change-Id: I12e3b850a4e6fa6c8146b8635ff728f3ef658819 Reviewed-on: https://go-review.googlesource.com/9828 Reviewed-by: Austin Clements --- src/runtime/mbitmap.go | 110 ++++++++++++++++++++++------------------- 1 file changed, 60 insertions(+), 50 deletions(-) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 61e1254bed..234aa9509a 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -8,7 +8,8 @@ // // Stack frames and global variables in the data and bss sections are described // by 1-bit bitmaps in which 0 means uninteresting and 1 means live pointer -// to be visited during GC. +// to be visited during GC. The bits in each byte are consumed starting with +// the low bit: 1<<0, 1<<1, and so on. // // Heap bitmap // @@ -19,8 +20,6 @@ // That is, the byte at address start-1 holds the 2-bit entries for the four words // start through start+3*ptrSize, the byte at start-2 holds the entries for // start+4*ptrSize through start+7*ptrSize, and so on. -// In each byte, the low 2 bits describe the first word, the next 2 bits describe -// the next word, and so on. // // In each 2-bit entry, the lower bit holds the same information as in the 1-bit // bitmaps: 0 means uninteresting and 1 means live pointer to be visited during GC. @@ -33,6 +32,11 @@ // This 00 is called the ``dead'' encoding: it signals that the rest of the words // in the object are uninteresting to the garbage collector. // +// The 2-bit entries are split when written into the byte, so that the top half +// of the byte contains 4 mark bits and the bottom half contains 4 pointer bits. +// This form allows a copy from the 1-bit to the 4-bit form to keep the +// pointer bits contiguous, instead of having to space them out. +// // The code makes use of the fact that the zero value for a heap bitmap // has no live pointer bit set and is (depending on position), not marked, // not checkmarked, and is the dead encoding. @@ -65,11 +69,15 @@ package runtime import "unsafe" const ( - bitPointer = 1 - bitMarked = 2 + bitPointer = 1 << 0 + bitMarked = 1 << 4 - heapBitsWidth = 2 // heap bitmap bits to describe one pointer - heapBitmapScale = ptrSize * (8 / heapBitsWidth) // number of data bytes described by one heap bitmap byte + heapBitsShift = 1 // shift offset between successive bitPointer or bitMarked entries + heapBitmapScale = ptrSize * (8 / 2) // number of data bytes described by one heap bitmap byte + + // all mark/pointer bits in a byte + bitMarkedAll = bitMarked | bitMarked<> h.shift) - if b&(bitPointer|bitPointer<>(heapBitsWidth+h.shift))&bitMarked != 0 + return (*h.bitp>>(heapBitsShift+h.shift))&bitMarked != 0 } // setCheckmarked sets the checkmarked bit. @@ -307,7 +315,7 @@ func (h heapBits) setCheckmarked(size uintptr) { atomicor8(h.bitp, bitPointer< 2*ptrSize { x = 0 @@ -454,10 +462,10 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) { } bitp = subtractb(bitp, step) x = uint32(*bitp) - if x&(bitMarked<<4) != 0 { - x &^= bitMarked << 4 + if x&(bitMarked<<(2*heapBitsShift)) != 0 { + x &^= bitMarked << (2 * heapBitsShift) } else { - x &^= 0xf0 + x &^= (bitMarked|bitPointer)<<(2*heapBitsShift) | (bitMarked|bitPointer)<<(3*heapBitsShift) f(base + (i+1)*size) if size > 2*ptrSize { *subtractb(bitp, 1) = 0 @@ -554,12 +562,12 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { if size == 2*ptrSize { if typ.size == ptrSize { // 2-element slice of pointer. - atomicor8(h.bitp, (bitPointer|bitPointer<= nw { goto Phase3 } @@ -724,7 +732,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { b >>= 4 nb -= 4 - case ptrSize == 8 && h.shift == 4: + case ptrSize == 8 && h.shift == 2: // Ptrmask and heap bitmap are misaligned. // The bits for the first two words are in a byte shared with another object // and must be updated atomically. @@ -735,7 +743,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // not with its mark bit. Since there is only one allocation // from a given span at a time, we should be able to set // these bits non-atomically. Not worth the risk right now. - hb = (b&1)<<4 | (b&2)<<(4+heapBitsWidth-1) // bits being prepared for *h.bitp + hb = (b & 3) << (2 * heapBitsShift) b >>= 2 nb -= 2 // Note: no bitMarker in hb because the first two words don't get markers from us. @@ -763,8 +771,8 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // if w+4 >= nw, then b has only nw-w bits, // but we'll stop at the break and then truncate // appropriately in Phase 3. - hb = b&1 | (b&2)<<(heapBitsWidth-1) | (b&4)<<(2*heapBitsWidth-2) | (b&8)<<(3*heapBitsWidth-3) - hb |= bitMarked | bitMarked<= nw { break } @@ -803,8 +811,8 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } // Emit bitmap byte. - hb = b&1 | (b&2)<<(heapBitsWidth-1) | (b&4)<<(2*heapBitsWidth-2) | (b&8)<<(3*heapBitsWidth-3) - hb |= bitMarked | bitMarked<= nw { break } @@ -822,15 +830,16 @@ Phase3: // then we must write a ``dead'' entry to the next bitmap byte. if frag := (nw - w) % 4; frag != 0 { // Data ends at least one word early. - hb &= 1<<(heapBitsWidth*frag) - 1 + mask := uintptr(1)<= size { break } - have = (*h.bitp >> h.shift) & 3 + have = (*h.bitp >> h.shift) & (bitPointer | bitMarked) if i == dataSize/ptrSize || i/ndata == count-1 && j >= nptr { want = 0 // dead marker } else { @@ -883,8 +892,9 @@ Phase4: println("mismatch writing bits for", *typ._string, "x", dataSize/typ.size) print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n") print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n") - h = heapBitsForAddr(x) - print("initial bits h.bitp=", h.bitp, " h.shift=", h.shift, "\n") + h0 := heapBitsForAddr(x) + print("initial bits h0.bitp=", h0.bitp, " h0.shift=", h0.shift, "\n") + print("current bits h.bitp=", h.bitp, " h.shift=", h.shift, " *h.bitp=", hex(*h.bitp), "\n") print("ptrmask=", ptrmask, " p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n") println("at word", i, "offset", i*ptrSize, "have", have, "want", want) throw("bad heapBitsSetType") From 266a842f5525fa8009f0c97b6d9967bb59fea349 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 7 May 2015 22:39:57 -0400 Subject: [PATCH 030/232] runtime: zero entire bitmap for object, even past dead marker We want typedmemmove to use the heap bitmap to determine where pointers are, instead of reinterpreting the type information. The heap bitmap is simpler to access. In general, typedmemmove will need to be able to look up the bits for any word and find valid pointer information, so fill even after the dead marker. Not filling after the dead marker was an optimization I introduced only a few days ago, when reintroducing the dead marker code. At the time I said it probably wouldn't last, and it didn't. Change-Id: I6ba01bff17ddee1ff429f454abe29867ec60606e Reviewed-on: https://go-review.googlesource.com/9885 Reviewed-by: Austin Clements --- src/runtime/mbitmap.go | 92 +++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 41 deletions(-) diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index 234aa9509a..f112eb899a 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -682,13 +682,14 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } var w uintptr // words processed - var nw uintptr // total number of words to process + var nw uintptr // number of words to process if typ.size == dataSize { // Single entry: can stop once we reach the non-pointer data. nw = typ.ptrdata / ptrSize } else { // Repeated instances of typ in an array. - // Have to process the + // Have to process first N-1 entries in full, but can stop + // once we reach the non-pointer data in the final entry. nw = ((dataSize/typ.size-1)*typ.size + typ.ptrdata) / ptrSize } if nw == 0 { @@ -753,8 +754,9 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { // We know that there is more data, because we handled 2-word objects above. // This must be at least a 6-word object. If we're out of pointer words, // mark no scan in next bitmap byte and finish. - *hbitp = 0 - goto Phase4 + hb = 0 + w += 4 + goto Phase3 } } @@ -822,47 +824,59 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) { } Phase3: - // Phase 3: Special case for final byte or half-byte describing final fragment of data. - // If there are not four data words for this final fragment, we must clear the mark bits - // in the 2-bit entries for the missing words. Clearing them creates a ``dead'' entry - // to tell the GC scan to stop scanning this object early. - // If there are four words in the final fragment but there is more data, - // then we must write a ``dead'' entry to the next bitmap byte. - if frag := (nw - w) % 4; frag != 0 { - // Data ends at least one word early. - mask := uintptr(1)< nw { + // Counting the 4 entries in hb not yet written to memory, + // there are more entries than possible pointer slots. + // Discard the excess entries (can't be more than 3). + mask := uintptr(1)<<(4-(w-nw)) - 1 hb &= mask | mask<<4 // apply mask to both pointer bits and mark bits - if w*ptrSize <= size { - // We own the whole byte and get the dead marker for free. - *hbitp = uint8(hb) - } else { - // We only own the bottom two entries in the byte, bits 00110011. - // If frag == 1, we get a dead marker for free. - // If frag == 2, no dead marker needed (we've reached the end of the object). - atomicand8(hbitp, ^uint8(bitPointer|bitMarked|(bitPointer|bitMarked)< nw, or else we'd still be in the loop above. + // It can be bigger only due to the 4 entries in hb that it counts. + // If w == nw+4 then there's nothing left to do: we wrote all nw entries + // and can discard the 4 sitting in hb. + // But if w == nw+2, we need to write first two in hb. + // The byte is shared with the next object so we may need an atomic. + if w == nw+2 { + if gcphase == _GCoff { + *hbitp = *hbitp&^(bitPointer|bitMarked|(bitPointer|bitMarked)<= 2 && want == 0 { - // found dead marker; the rest is uninitialized - break - } h = h.next() } } From 4212a3c3d9a520c3124134c97bb48677c0c1203f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 7 May 2015 22:40:54 -0400 Subject: [PATCH 031/232] runtime: use heap bitmap for typedmemmove The current implementation of typedmemmove walks the ptrmask in the type to find out where pointers are. This led to turning off GC programs for the Go 1.5 dev cycle, so that there would always be a ptrmask. Instead of also interpreting the GC programs, interpret the heap bitmap, which we know must be available and up to date. (There is no point to write barriers when writing outside the heap.) This CL is only about correctness. The next CL will optimize the code. Change-Id: Id1305c7c071fd2734ab96634b0e1c745b23fa793 Reviewed-on: https://go-review.googlesource.com/9886 Reviewed-by: Austin Clements --- src/runtime/mbarrier.go | 98 ++++++----------------------------------- src/runtime/mbitmap.go | 61 ++++++++++++------------- 2 files changed, 45 insertions(+), 114 deletions(-) diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go index eb41a60087..409c1948c6 100644 --- a/src/runtime/mbarrier.go +++ b/src/runtime/mbarrier.go @@ -106,7 +106,10 @@ func writebarrierptr(dst *uintptr, src uintptr) { return } if src != 0 && (src < _PhysPageSize || src == poisonStack) { - systemstack(func() { throw("bad pointer in write barrier") }) + systemstack(func() { + print("runtime: writebarrierptr *", dst, " = ", hex(src), "\n") + throw("bad pointer in write barrier") + }) } writebarrierptr_nostore1(dst, src) } @@ -152,33 +155,11 @@ func writebarrieriface(dst *[2]uintptr, src [2]uintptr) { // typedmemmove copies a value of type t to dst from src. //go:nosplit func typedmemmove(typ *_type, dst, src unsafe.Pointer) { - if !writeBarrierEnabled || (typ.kind&kindNoPointers) != 0 { - memmove(dst, src, typ.size) + memmove(dst, src, typ.size) + if typ.kind&kindNoPointers != 0 { return } - - systemstack(func() { - dst := dst // make local copies - src := src - nptr := typ.size / ptrSize - i := uintptr(0) - Copy: - for _, bits := range ptrBitmapForType(typ) { - for j := 0; j < 8; j++ { - if bits&1 != 0 { - writebarrierptr((*uintptr)(dst), *(*uintptr)(src)) - } else { - *(*uintptr)(dst) = *(*uintptr)(src) - } - if i++; i >= nptr { - break Copy - } - dst = add(dst, ptrSize) - src = add(src, ptrSize) - bits >>= 1 - } - } - }) + heapBitsBulkBarrier(uintptr(dst), typ.size) } //go:linkname reflect_typedmemmove reflect.typedmemmove @@ -190,45 +171,16 @@ func reflect_typedmemmove(typ *_type, dst, src unsafe.Pointer) { // dst and src point off bytes into the value and only copies size bytes. //go:linkname reflect_typedmemmovepartial reflect.typedmemmovepartial func reflect_typedmemmovepartial(typ *_type, dst, src unsafe.Pointer, off, size uintptr) { - if !writeBarrierEnabled || (typ.kind&kindNoPointers) != 0 || size < ptrSize { - memmove(dst, src, size) + memmove(dst, src, size) + if !writeBarrierEnabled || typ.kind&kindNoPointers != 0 || size < ptrSize || !inheap(uintptr(dst)) { return } - if off&(ptrSize-1) != 0 { - frag := -off & (ptrSize - 1) - // frag < size, because size >= ptrSize, checked above. - memmove(dst, src, frag) + if frag := -off & (ptrSize - 1); frag != 0 { + dst = add(dst, frag) size -= frag - dst = add(noescape(dst), frag) - src = add(noescape(src), frag) - off += frag - } - - mask := ptrBitmapForType(typ) - nptr := (off + size) / ptrSize - i := uintptr(off / ptrSize) -Copy: - for { - bits := mask[i/8] >> (i % 8) - for j := i % 8; j < 8; j++ { - if bits&1 != 0 { - writebarrierptr((*uintptr)(dst), *(*uintptr)(src)) - } else { - *(*uintptr)(dst) = *(*uintptr)(src) - } - if i++; i >= nptr { - break Copy - } - dst = add(dst, ptrSize) - src = add(src, ptrSize) - bits >>= 1 - } - } - size &= ptrSize - 1 - if size > 0 { - memmove(dst, src, size) } + heapBitsBulkBarrier(uintptr(dst), size&^(ptrSize-1)) } // callwritebarrier is invoked at the end of reflectcall, to execute @@ -240,32 +192,10 @@ Copy: // not to be preempted before the write barriers have been run. //go:nosplit func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uintptr) { - if !writeBarrierEnabled || typ == nil || (typ.kind&kindNoPointers) != 0 || framesize-retoffset < ptrSize { + if !writeBarrierEnabled || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < ptrSize || !inheap(uintptr(frame)) { return } - - systemstack(func() { - mask := ptrBitmapForType(typ) - // retoffset is known to be pointer-aligned (at least). - // TODO(rsc): The noescape call should be unnecessary. - dst := add(noescape(frame), retoffset) - nptr := framesize / ptrSize - i := uintptr(retoffset / ptrSize) - Copy: - for { - bits := mask[i/8] >> (i % 8) - for j := i % 8; j < 8; j++ { - if bits&1 != 0 { - writebarrierptr_nostore((*uintptr)(dst), *(*uintptr)(dst)) - } - if i++; i >= nptr { - break Copy - } - dst = add(dst, ptrSize) - bits >>= 1 - } - } - }) + heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize) } //go:nosplit diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go index f112eb899a..5472d28e02 100644 --- a/src/runtime/mbitmap.go +++ b/src/runtime/mbitmap.go @@ -124,6 +124,9 @@ type heapBits struct { // heapBitsForAddr returns the heapBits for the address addr. // The caller must have already checked that addr is in the range [mheap_.arena_start, mheap_.arena_used). +// +// nosplit because it is used during write barriers and must not be preempted. +//go:nosplit func heapBitsForAddr(addr uintptr) heapBits { // 2 bits per work, 4 pairs per byte, and a mask is hard coded. off := (addr - mheap_.arena_start) / ptrSize @@ -318,6 +321,34 @@ func (h heapBits) setCheckmarked(size uintptr) { atomicor8(h.bitp, bitMarked<<(heapBitsShift+h.shift)) } +// heapBitsBulkBarrier executes writebarrierptr_nostore +// for every pointer slot in the memory range [p, p+size), +// using the heap bitmap to locate those pointer slots. +// This executes the write barriers necessary after a memmove. +// Both p and size must be pointer-aligned. +// The range [p, p+size) must lie within a single allocation. +// +// Callers should call heapBitsBulkBarrier immediately after +// calling memmove(p, src, size). This function is marked nosplit +// to avoid being preempted; the GC must not stop the goroutine +// betwen the memmove and the execution of the barriers. +//go:nosplit +func heapBitsBulkBarrier(p, size uintptr) { + if (p|size)&(ptrSize-1) != 0 { + throw("heapBitsBulkBarrier: unaligned arguments") + } + if !writeBarrierEnabled || !inheap(p) { + return + } + + for i := uintptr(0); i < size; i += ptrSize { + if heapBitsForAddr(p + i).isPointer() { + x := (*uintptr)(unsafe.Pointer(p + i)) + writebarrierptr_nostore(x, *x) + } + } +} + // The methods operating on spans all require that h has been returned // by heapBitsForSpan and that size, n, total are the span layout description // returned by the mspan's layout method. @@ -918,36 +949,6 @@ Phase3: } } -// ptrBitmapForType returns a bitmap indicating where pointers are -// in the memory representation of the type typ. -// The bit x[i/8]&(1<<(i%8)) is 1 if the i'th word in a value of type typ -// is a pointer. -func ptrBitmapForType(typ *_type) []uint8 { - var ptrmask *uint8 - nptr := (uintptr(typ.size) + ptrSize - 1) / ptrSize - if typ.kind&kindGCProg != 0 { - masksize := (nptr + 7) / 8 - masksize++ // unroll flag in the beginning - if masksize > maxGCMask && typ.gc[1] != 0 { - // write barriers have not been updated to deal with this case yet. - throw("maxGCMask too small for now") - } - ptrmask = (*uint8)(unsafe.Pointer(uintptr(typ.gc[0]))) - // Check whether the program is already unrolled - // by checking if the unroll flag byte is set - maskword := uintptr(atomicloadp(unsafe.Pointer(ptrmask))) - if *(*uint8)(unsafe.Pointer(&maskword)) == 0 { - systemstack(func() { - unrollgcprog_m(typ) - }) - } - ptrmask = (*uint8)(add(unsafe.Pointer(ptrmask), 1)) // skip the unroll flag byte - } else { - ptrmask = (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask - } - return (*[1 << 30]byte)(unsafe.Pointer(ptrmask))[:(nptr+7)/8] -} - // GC type info programs // // TODO(rsc): Clean up and enable. From 516f0d1c906b841d33ecd185cda0257dfa7e2210 Mon Sep 17 00:00:00 2001 From: Daniel Morsing Date: Mon, 11 May 2015 10:31:24 +0100 Subject: [PATCH 032/232] net/http: silence race detector on client header timeout test When running the client header timeout test, there is a race between us timing out and waiting on the remaining requests to be serviced. If the client times out before the server blocks on the channel in the handler, we will be simultaneously adding to a waitgroup with the value 0 and waiting on it when we call TestServer.Close(). This is largely a theoretical race. We have to time out before we enter the handler and the only reason we would time out if we're blocked on the channel. Nevertheless, make the race detector happy by turning the close into a channel send. This turns the defer call into a synchronization point and we can be sure that we've entered the handler before we close the server. Fixes #10780 Change-Id: Id73b017d1eb7503e446aa51538712ef49f2f5c9e Reviewed-on: https://go-review.googlesource.com/9905 Reviewed-by: Brad Fitzpatrick --- src/net/http/client_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/net/http/client_test.go b/src/net/http/client_test.go index dc499a90b6..b1d8799fa5 100644 --- a/src/net/http/client_test.go +++ b/src/net/http/client_test.go @@ -927,7 +927,14 @@ func TestClientTimeout_Headers(t *testing.T) { <-donec })) defer ts.Close() - defer close(donec) + // Note that we use a channel send here and not a close. + // The race detector doesn't know that we're waiting for a timeout + // and thinks that the waitgroup inside httptest.Server is added to concurrently + // with us closing it. If we timed out immediately, we could close the testserver + // before we entered the handler. We're not timing out immediately and there's + // no way we would be done before we entered the handler, but the race detector + // doesn't know this, so synchronize explicitly. + defer func() { donec <- true }() c := &Client{Timeout: 500 * time.Millisecond} From 3475ec7f36b68126310878e611c8594514b98438 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 11 May 2015 11:24:10 +1200 Subject: [PATCH 033/232] cmd/internal/ld: change Cpos to not flush the output buffer DWARF generation appears to assume Cpos is cheap and this makes linking godoc about 8% faster and linking the standard library into a single shared library about 22% faster on my machine. Updates #10571 Change-Id: I3f81efd0174e356716e7971c4f59810b72378177 Reviewed-on: https://go-review.googlesource.com/9913 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- src/cmd/internal/ld/lib.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go index edafaebb0b..e4e68eae27 100644 --- a/src/cmd/internal/ld/lib.go +++ b/src/cmd/internal/ld/lib.go @@ -1573,12 +1573,11 @@ func Cflush() { } func Cpos() int64 { - Cflush() off, err := coutbuf.f.Seek(0, 1) if err != nil { Exitf("seeking in output [0, 1]: %v", err) } - return off + return off + int64(coutbuf.Buffered()) } func Cseek(p int64) { From 7c0db1b7e23e14f7a9cc0c424f57a57a613846d3 Mon Sep 17 00:00:00 2001 From: Didier Spezia Date: Sat, 9 May 2015 17:21:42 +0000 Subject: [PATCH 034/232] cmd/gc: do not display ~b identifiers in error messages Instead of errors like: ./blank2.go:15: cannot use ~b1 (type []int) as type int in assignment we now have: ./blank2.go:15: cannot use _ (type []int) as type int in assignment Less confusing for users. Fixes #9521 Change-Id: Ieab9859040e8e0df95deeaee7eeb408d3be61c0f Reviewed-on: https://go-review.googlesource.com/9902 Reviewed-by: Josh Bleecher Snyder Reviewed-by: Ian Lance Taylor --- src/cmd/internal/gc/fmt.go | 2 +- test/fixedbugs/issue9521.go | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue9521.go diff --git a/src/cmd/internal/gc/fmt.go b/src/cmd/internal/gc/fmt.go index 1a991a0a65..4e3045a929 100644 --- a/src/cmd/internal/gc/fmt.go +++ b/src/cmd/internal/gc/fmt.go @@ -1127,7 +1127,7 @@ func exprfmt(n *Node, prec int) string { // Special case: name used as local variable in export. // _ becomes ~b%d internally; print as _ for export case ONAME: - if fmtmode == FExp && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' { + if (fmtmode == FExp || fmtmode == FErr) && n.Sym != nil && n.Sym.Name[0] == '~' && n.Sym.Name[1] == 'b' { return "_" } if fmtmode == FExp && n.Sym != nil && !isblank(n) && n.Vargen > 0 { diff --git a/test/fixedbugs/issue9521.go b/test/fixedbugs/issue9521.go new file mode 100644 index 0000000000..51b5204e7a --- /dev/null +++ b/test/fixedbugs/issue9521.go @@ -0,0 +1,16 @@ +// errorcheck + +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that an incorrect use of the blank identifer is caught. +// Does not compile. + +package main + +func f() (_, _ []int) { return } + +func main() { + _ = append(f()) // ERROR "cannot use _" +} From e92a7247faee31985680f110ff429df484963487 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 11 May 2015 10:38:24 -0700 Subject: [PATCH 035/232] fmt: skip malloc test under race detector Fixes #10778. Change-Id: I09aab55dec429ec4a023e5ad591b929563cef0d9 Reviewed-on: https://go-review.googlesource.com/9855 Reviewed-by: Brad Fitzpatrick --- src/fmt/fmt_test.go | 8 +++++--- src/fmt/norace_test.go | 9 +++++++++ src/fmt/race_test.go | 9 +++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 src/fmt/norace_test.go create mode 100644 src/fmt/race_test.go diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index ba99cb0f6a..f15a0ba8e8 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -949,11 +949,13 @@ var mallocTest = []struct { var _ bytes.Buffer func TestCountMallocs(t *testing.T) { - if testing.Short() { + switch { + case testing.Short(): t.Skip("skipping malloc count in short mode") - } - if runtime.GOMAXPROCS(0) > 1 { + case runtime.GOMAXPROCS(0) > 1: t.Skip("skipping; GOMAXPROCS>1") + case raceenabled: + t.Skip("skipping malloc count under race detector") } for _, mt := range mallocTest { mallocs := testing.AllocsPerRun(100, mt.fn) diff --git a/src/fmt/norace_test.go b/src/fmt/norace_test.go new file mode 100644 index 0000000000..1267cc34ee --- /dev/null +++ b/src/fmt/norace_test.go @@ -0,0 +1,9 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !race + +package fmt_test + +const raceenabled = false diff --git a/src/fmt/race_test.go b/src/fmt/race_test.go new file mode 100644 index 0000000000..ae3147a5b0 --- /dev/null +++ b/src/fmt/race_test.go @@ -0,0 +1,9 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build race + +package fmt_test + +const raceenabled = true From e3a9a08a0bfd683b7cb3330fedffaeb456261226 Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Mon, 11 May 2015 11:13:22 -0700 Subject: [PATCH 036/232] fmt: allow for space and plus flags when computing widths Fixes #10770. Fixes #10771. This time maybe for sure? Change-Id: I43d6e5fd6846cf58427fec183832d500a932df59 Reviewed-on: https://go-review.googlesource.com/9896 Reviewed-by: Russ Cox --- src/fmt/fmt_test.go | 5 ++++- src/fmt/format.go | 7 ++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/fmt/fmt_test.go b/src/fmt/fmt_test.go index f15a0ba8e8..93121bb3d0 100644 --- a/src/fmt/fmt_test.go +++ b/src/fmt/fmt_test.go @@ -557,10 +557,13 @@ var fmtTests = []struct { {"%0.100f", 1.0, zeroFill("1.", 100, "")}, {"%0.100f", -1.0, zeroFill("-1.", 100, "")}, - // Used to panic: integer function didn't look at f.prec or f.unicode. + // Used to panic: integer function didn't look at f.prec or f.unicode or f.width. {"%#.80x", 42, "0x0000000000000000000000000000000000000000000000000000000000000000000000000000002a"}, {"%.80U", 42, "U+0000000000000000000000000000000000000000000000000000000000000000000000000000002A"}, {"%#.80U", '日', "U+000000000000000000000000000000000000000000000000000000000000000000000000000065E5 '日'"}, + {"%+.65d", 44, "+00000000000000000000000000000000000000000000000000000000000000044"}, + {"% .65d", 44, " 00000000000000000000000000000000000000000000000000000000000000044"}, + {"% +.65d", 44, "+00000000000000000000000000000000000000000000000000000000000000044"}, // Comparison of padding rules with C printf. /* diff --git a/src/fmt/format.go b/src/fmt/format.go index 099f8a5e00..ba984cf84f 100644 --- a/src/fmt/format.go +++ b/src/fmt/format.go @@ -163,7 +163,7 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { } var buf []byte = f.intbuf[0:] - if f.widPresent || f.precPresent { + if f.widPresent || f.precPresent || f.plus || f.space { width := f.wid + f.prec // Only one will be set, both are positive; this provides the maximum. if base == 16 && f.sharp { // Also adds "0x". @@ -177,6 +177,11 @@ func (f *fmt) integer(a int64, base uint64, signedness bool, digits string) { width += 1 + 1 + utf8.UTFMax + 1 } } + if f.plus { + width++ + } else if f.space { + width++ + } if width > nByte { // We're going to need a bigger boat. buf = make([]byte, width) From 2b0361084258b78cc8ed6e2dc6924985dbbcd3bf Mon Sep 17 00:00:00 2001 From: Alexandre Cesaro Date: Fri, 20 Mar 2015 10:22:55 +0100 Subject: [PATCH 037/232] mime: Export RFC 2047 code Fixes #4943 Fixes #4687 Fixes #7079 Change-Id: Ia96f07d650a3af935cd75fd7e3253f4af2977429 Reviewed-on: https://go-review.googlesource.com/7890 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick --- src/go/build/deps_test.go | 77 ++++---- src/internal/mime/header.go | 122 ------------- src/mime/encodedword.go | 329 +++++++++++++++++++++++++++++++++++ src/mime/encodedword_test.go | 241 +++++++++++++++++++++++++ src/net/mail/message.go | 35 +++- 5 files changed, 638 insertions(+), 166 deletions(-) delete mode 100644 src/internal/mime/header.go create mode 100644 src/mime/encodedword.go create mode 100644 src/mime/encodedword_test.go diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 84c1e2ab31..52c5a7dd80 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -184,42 +184,43 @@ var pkgDeps = map[string][]string{ }, // One of a kind. - "archive/tar": {"L4", "OS", "syscall"}, - "archive/zip": {"L4", "OS", "compress/flate"}, - "compress/bzip2": {"L4"}, - "compress/flate": {"L4"}, - "compress/gzip": {"L4", "compress/flate"}, - "compress/lzw": {"L4"}, - "compress/zlib": {"L4", "compress/flate"}, - "database/sql": {"L4", "container/list", "database/sql/driver"}, - "database/sql/driver": {"L4", "time"}, - "debug/dwarf": {"L4"}, - "debug/elf": {"L4", "OS", "debug/dwarf"}, - "debug/gosym": {"L4"}, - "debug/macho": {"L4", "OS", "debug/dwarf"}, - "debug/pe": {"L4", "OS", "debug/dwarf"}, - "encoding": {"L4"}, - "encoding/ascii85": {"L4"}, - "encoding/asn1": {"L4", "math/big"}, - "encoding/csv": {"L4"}, - "encoding/gob": {"L4", "OS", "encoding"}, - "encoding/hex": {"L4"}, - "encoding/json": {"L4", "encoding"}, - "encoding/pem": {"L4"}, - "encoding/xml": {"L4", "encoding"}, - "flag": {"L4", "OS"}, - "go/build": {"L4", "OS", "GOPARSER"}, - "html": {"L4"}, - "image/draw": {"L4", "image/internal/imageutil"}, - "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"}, - "image/jpeg": {"L4", "image/internal/imageutil"}, - "image/png": {"L4", "compress/zlib"}, - "index/suffixarray": {"L4", "regexp"}, - "math/big": {"L4"}, - "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"}, - "net/url": {"L4"}, - "text/scanner": {"L4", "OS"}, - "text/template/parse": {"L4"}, + "archive/tar": {"L4", "OS", "syscall"}, + "archive/zip": {"L4", "OS", "compress/flate"}, + "compress/bzip2": {"L4"}, + "compress/flate": {"L4"}, + "compress/gzip": {"L4", "compress/flate"}, + "compress/lzw": {"L4"}, + "compress/zlib": {"L4", "compress/flate"}, + "database/sql": {"L4", "container/list", "database/sql/driver"}, + "database/sql/driver": {"L4", "time"}, + "debug/dwarf": {"L4"}, + "debug/elf": {"L4", "OS", "debug/dwarf"}, + "debug/gosym": {"L4"}, + "debug/macho": {"L4", "OS", "debug/dwarf"}, + "debug/pe": {"L4", "OS", "debug/dwarf"}, + "encoding": {"L4"}, + "encoding/ascii85": {"L4"}, + "encoding/asn1": {"L4", "math/big"}, + "encoding/csv": {"L4"}, + "encoding/gob": {"L4", "OS", "encoding"}, + "encoding/hex": {"L4"}, + "encoding/json": {"L4", "encoding"}, + "encoding/pem": {"L4"}, + "encoding/xml": {"L4", "encoding"}, + "flag": {"L4", "OS"}, + "go/build": {"L4", "OS", "GOPARSER"}, + "html": {"L4"}, + "image/draw": {"L4", "image/internal/imageutil"}, + "image/gif": {"L4", "compress/lzw", "image/color/palette", "image/draw"}, + "image/jpeg": {"L4", "image/internal/imageutil"}, + "image/png": {"L4", "compress/zlib"}, + "index/suffixarray": {"L4", "regexp"}, + "math/big": {"L4"}, + "mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"}, + "mime/quotedprintable": {"L4"}, + "net/url": {"L4"}, + "text/scanner": {"L4", "OS"}, + "text/template/parse": {"L4"}, "html/template": { "L4", "OS", "encoding/json", "html", "text/template", @@ -255,7 +256,7 @@ var pkgDeps = map[string][]string{ // Uses of networking. "log/syslog": {"L4", "OS", "net"}, - "net/mail": {"L4", "NET", "OS", "internal/mime"}, + "net/mail": {"L4", "NET", "OS", "mime"}, "net/textproto": {"L4", "OS", "net"}, // Core crypto. @@ -347,13 +348,11 @@ var pkgDeps = map[string][]string{ "go/types": {"bytes", "container/heap", "fmt", "go/ast", "go/constants", "go/parser", "go/token", "io", "math", "path", "sort", "strconv", "strings", "sync", "unicode"}, "image/internal/imageutil": {"image"}, "internal/format": {"bytes", "go/ast", "go/parser", "go/printer", "go/token", "strings"}, - "internal/mime": {"bytes", "encoding/base64", "errors", "fmt", "io", "io/ioutil", "strconv", "strings", "unicode"}, "internal/singleflight": {"sync"}, "internal/syscall/unix": {"runtime", "sync/atomic", "syscall", "unsafe"}, "internal/syscall/windows": {"syscall", "unsafe"}, "internal/syscall/windows/registry": {"errors", "io", "syscall", "unicode/utf16", "unsafe"}, "internal/trace": {"bufio", "bytes", "fmt", "io", "os", "os/exec", "sort", "strconv", "strings"}, - "mime/quotedprintable": {"bufio", "bytes", "fmt", "io"}, "net/http/cookiejar": {"errors", "fmt", "net", "net/http", "net/url", "sort", "strings", "sync", "time", "unicode/utf8"}, "net/http/internal": {"bufio", "bytes", "errors", "fmt", "io"}, "net/internal/socktest": {"fmt", "sync", "syscall"}, diff --git a/src/internal/mime/header.go b/src/internal/mime/header.go deleted file mode 100644 index 9bc3e5e576..0000000000 --- a/src/internal/mime/header.go +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2015 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package mime - -import ( - "bytes" - "encoding/base64" - "errors" - "fmt" - "io" - "io/ioutil" - "strconv" - "strings" - "unicode" -) - -// EncodeWord encodes a string into an RFC 2047 encoded-word. -func EncodeWord(s string) string { - // UTF-8 "Q" encoding - b := bytes.NewBufferString("=?utf-8?q?") - for i := 0; i < len(s); i++ { - switch c := s[i]; { - case c == ' ': - b.WriteByte('_') - case isVchar(c) && c != '=' && c != '?' && c != '_': - b.WriteByte(c) - default: - fmt.Fprintf(b, "=%02X", c) - } - } - b.WriteString("?=") - return b.String() -} - -// DecodeWord decodes an RFC 2047 encoded-word. -func DecodeWord(s string) (string, error) { - fields := strings.Split(s, "?") - if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" { - return "", errors.New("address not RFC 2047 encoded") - } - charset, enc := strings.ToLower(fields[1]), strings.ToLower(fields[2]) - if charset != "us-ascii" && charset != "iso-8859-1" && charset != "utf-8" { - return "", fmt.Errorf("charset not supported: %q", charset) - } - - in := bytes.NewBufferString(fields[3]) - var r io.Reader - switch enc { - case "b": - r = base64.NewDecoder(base64.StdEncoding, in) - case "q": - r = qDecoder{r: in} - default: - return "", fmt.Errorf("RFC 2047 encoding not supported: %q", enc) - } - - dec, err := ioutil.ReadAll(r) - if err != nil { - return "", err - } - - switch charset { - case "us-ascii": - b := new(bytes.Buffer) - for _, c := range dec { - if c >= 0x80 { - b.WriteRune(unicode.ReplacementChar) - } else { - b.WriteRune(rune(c)) - } - } - return b.String(), nil - case "iso-8859-1": - b := new(bytes.Buffer) - for _, c := range dec { - b.WriteRune(rune(c)) - } - return b.String(), nil - case "utf-8": - return string(dec), nil - } - panic("unreachable") -} - -type qDecoder struct { - r io.Reader - scratch [2]byte -} - -func (qd qDecoder) Read(p []byte) (n int, err error) { - // This method writes at most one byte into p. - if len(p) == 0 { - return 0, nil - } - if _, err := qd.r.Read(qd.scratch[:1]); err != nil { - return 0, err - } - switch c := qd.scratch[0]; { - case c == '=': - if _, err := io.ReadFull(qd.r, qd.scratch[:2]); err != nil { - return 0, err - } - x, err := strconv.ParseInt(string(qd.scratch[:2]), 16, 64) - if err != nil { - return 0, fmt.Errorf("mime: invalid RFC 2047 encoding: %q", qd.scratch[:2]) - } - p[0] = byte(x) - case c == '_': - p[0] = ' ' - default: - p[0] = c - } - return 1, nil -} - -// isVchar returns true if c is an RFC 5322 VCHAR character. -func isVchar(c byte) bool { - // Visible (printing) characters. - return '!' <= c && c <= '~' -} diff --git a/src/mime/encodedword.go b/src/mime/encodedword.go new file mode 100644 index 0000000000..9796f506dc --- /dev/null +++ b/src/mime/encodedword.go @@ -0,0 +1,329 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mime + +import ( + "bytes" + "encoding/base64" + "errors" + "fmt" + "io" + "strings" + "sync" + "unicode" + "unicode/utf8" +) + +// A WordEncoder is a RFC 2047 encoded-word encoder. +type WordEncoder byte + +const ( + // BEncoding represents Base64 encoding scheme as defined by RFC 2045. + BEncoding = WordEncoder('b') + // QEncoding represents the Q-encoding scheme as defined by RFC 2047. + QEncoding = WordEncoder('q') +) + +var ( + errInvalidWord = errors.New("mime: invalid RFC 2047 encoded-word") +) + +// Encode returns the encoded-word form of s. If s is ASCII without special +// characters, it is returned unchanged. The provided charset is the IANA +// charset name of s. It is case insensitive. +func (e WordEncoder) Encode(charset, s string) string { + if !needsEncoding(s) { + return s + } + return e.encodeWord(charset, s) +} + +func needsEncoding(s string) bool { + for _, b := range s { + if (b < ' ' || b > '~') && b != '\t' { + return true + } + } + return false +} + +// encodeWord encodes a string into an encoded-word. +func (e WordEncoder) encodeWord(charset, s string) string { + buf := getBuffer() + defer putBuffer(buf) + + buf.WriteString("=?") + buf.WriteString(charset) + buf.WriteByte('?') + buf.WriteByte(byte(e)) + buf.WriteByte('?') + + if e == BEncoding { + w := base64.NewEncoder(base64.StdEncoding, buf) + io.WriteString(w, s) + w.Close() + } else { + enc := make([]byte, 3) + for i := 0; i < len(s); i++ { + b := s[i] + switch { + case b == ' ': + buf.WriteByte('_') + case b <= '~' && b >= '!' && b != '=' && b != '?' && b != '_': + buf.WriteByte(b) + default: + enc[0] = '=' + enc[1] = upperhex[b>>4] + enc[2] = upperhex[b&0x0f] + buf.Write(enc) + } + } + } + buf.WriteString("?=") + return buf.String() +} + +const upperhex = "0123456789ABCDEF" + +// A WordDecoder decodes MIME headers containing RFC 2047 encoded-words. +type WordDecoder struct { + // CharsetReader, if non-nil, defines a function to generate + // charset-conversion readers, converting from the provided + // charset into UTF-8. + // Charsets are always lower-case. utf-8, iso-8859-1 and us-ascii charsets + // are handled by default. + // One of the the CharsetReader's result values must be non-nil. + CharsetReader func(charset string, input io.Reader) (io.Reader, error) +} + +// Decode decodes an encoded-word. If word is not a valid RFC 2047 encoded-word, +// word is returned unchanged. +func (d *WordDecoder) Decode(word string) (string, error) { + fields := strings.Split(word, "?") // TODO: remove allocation? + if len(fields) != 5 || fields[0] != "=" || fields[4] != "=" || len(fields[2]) != 1 { + return "", errInvalidWord + } + + content, err := decode(fields[2][0], fields[3]) + if err != nil { + return "", err + } + + buf := getBuffer() + defer putBuffer(buf) + + if err := d.convert(buf, fields[1], content); err != nil { + return "", err + } + + return buf.String(), nil +} + +// DecodeHeader decodes all encoded-words of the given string. It returns an +// error if and only if CharsetReader of d returns an error. +func (d *WordDecoder) DecodeHeader(header string) (string, error) { + // If there is no encoded-word, returns before creating a buffer. + i := strings.Index(header, "=?") + if i == -1 { + return header, nil + } + + buf := getBuffer() + defer putBuffer(buf) + + buf.WriteString(header[:i]) + header = header[i:] + + betweenWords := false + for { + start := strings.Index(header, "=?") + if start == -1 { + break + } + cur := start + len("=?") + + i := strings.Index(header[cur:], "?") + if i == -1 { + break + } + charset := header[cur : cur+i] + cur += i + len("?") + + if len(header) < cur+len("Q??=") { + break + } + encoding := header[cur] + cur++ + + if header[cur] != '?' { + break + } + cur++ + + j := strings.Index(header[cur:], "?=") + if j == -1 { + break + } + text := header[cur : cur+j] + end := cur + j + len("?=") + + content, err := decode(encoding, text) + if err != nil { + betweenWords = false + buf.WriteString(header[:start+2]) + header = header[start+2:] + continue + } + + // Write characters before the encoded-word. White-space and newline + // characters separating two encoded-words must be deleted. + if start > 0 && (!betweenWords || hasNonWhitespace(header[:start])) { + buf.WriteString(header[:start]) + } + + if err := d.convert(buf, charset, content); err != nil { + return "", err + } + + header = header[end:] + betweenWords = true + } + + if len(header) > 0 { + buf.WriteString(header) + } + + return buf.String(), nil +} + +func decode(encoding byte, text string) ([]byte, error) { + switch encoding { + case 'B', 'b': + return base64.StdEncoding.DecodeString(text) + case 'Q', 'q': + return qDecode(text) + default: + return nil, errInvalidWord + } +} + +func (d *WordDecoder) convert(buf *bytes.Buffer, charset string, content []byte) error { + switch { + case strings.EqualFold("utf-8", charset): + buf.Write(content) + case strings.EqualFold("iso-8859-1", charset): + for _, c := range content { + buf.WriteRune(rune(c)) + } + case strings.EqualFold("us-ascii", charset): + for _, c := range content { + if c >= utf8.RuneSelf { + buf.WriteRune(unicode.ReplacementChar) + } else { + buf.WriteByte(c) + } + } + default: + if d.CharsetReader == nil { + return fmt.Errorf("mime: unhandled charset %q", charset) + } + r, err := d.CharsetReader(strings.ToLower(charset), bytes.NewReader(content)) + if err != nil { + return err + } + if _, err = buf.ReadFrom(r); err != nil { + return err + } + } + return nil +} + +// hasNonWhitespace reports whether s (assumed to be ASCII) contains at least +// one byte of non-whitespace. +func hasNonWhitespace(s string) bool { + for _, b := range s { + switch b { + // Encoded-words can only be separated by linear white spaces which does + // not include vertical tabs (\v). + case ' ', '\t', '\n', '\r': + default: + return true + } + } + return false +} + +// qDecode decodes a Q encoded string. +func qDecode(s string) ([]byte, error) { + dec := make([]byte, len(s)) + n := 0 + for i := 0; i < len(s); i++ { + switch c := s[i]; { + case c == '_': + dec[n] = ' ' + case c == '=': + if i+2 >= len(s) { + return nil, errInvalidWord + } + b, err := readHexByte(s[i+1], s[i+2]) + if err != nil { + return nil, err + } + dec[n] = b + i += 2 + case (c <= '~' && c >= ' ') || c == '\n' || c == '\r' || c == '\t': + dec[n] = c + default: + return nil, errInvalidWord + } + n++ + } + + return dec[:n], nil +} + +// readHexByte returns the byte from its quoted-printable representation. +func readHexByte(a, b byte) (byte, error) { + var hb, lb byte + var err error + if hb, err = fromHex(a); err != nil { + return 0, err + } + if lb, err = fromHex(b); err != nil { + return 0, err + } + return hb<<4 | lb, nil +} + +func fromHex(b byte) (byte, error) { + switch { + case b >= '0' && b <= '9': + return b - '0', nil + case b >= 'A' && b <= 'F': + return b - 'A' + 10, nil + // Accept badly encoded bytes. + case b >= 'a' && b <= 'f': + return b - 'a' + 10, nil + } + return 0, fmt.Errorf("mime: invalid hex byte %#02x", b) +} + +var bufPool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, +} + +func getBuffer() *bytes.Buffer { + return bufPool.Get().(*bytes.Buffer) +} + +func putBuffer(buf *bytes.Buffer) { + if buf.Len() > 1024 { + return + } + buf.Reset() + bufPool.Put(buf) +} diff --git a/src/mime/encodedword_test.go b/src/mime/encodedword_test.go new file mode 100644 index 0000000000..02236ea521 --- /dev/null +++ b/src/mime/encodedword_test.go @@ -0,0 +1,241 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package mime + +import ( + "errors" + "fmt" + "io" + "io/ioutil" + "strings" + "testing" +) + +func ExampleEncodeWord() { + fmt.Println(QEncoding.Encode("utf-8", "¡Hola, señor!")) + fmt.Println(QEncoding.Encode("utf-8", "Hello!")) + fmt.Println(BEncoding.Encode("UTF-8", "¡Hola, señor!")) + fmt.Println(QEncoding.Encode("ISO-8859-1", "Caf\xE9")) + // Output: + // =?utf-8?q?=C2=A1Hola,_se=C3=B1or!?= + // Hello! + // =?UTF-8?b?wqFIb2xhLCBzZcOxb3Ih?= + // =?ISO-8859-1?q?Caf=E9?= +} + +func ExampleDecodeWord() { + dec := new(WordDecoder) + header, err := dec.DecodeHeader("=?utf-8?q?=C2=A1Hola,_se=C3=B1or!?=") + if err != nil { + panic(err) + } + fmt.Println(header) + // Output: ¡Hola, señor! +} + +func ExampleDecodeHeader() { + dec := new(WordDecoder) + header, err := dec.DecodeHeader("=?utf-8?q?=C3=89ric?= , =?utf-8?q?Ana=C3=AFs?= ") + if err != nil { + panic(err) + } + fmt.Println(header) + + header, err = dec.DecodeHeader("=?utf-8?q?=C2=A1Hola,?= =?utf-8?q?_se=C3=B1or!?=") + if err != nil { + panic(err) + } + fmt.Println(header) + // Output: + // Éric , Anaïs + // ¡Hola, señor! +} + +func TestEncodeWord(t *testing.T) { + utf8, iso88591 := "utf-8", "iso-8859-1" + tests := []struct { + enc WordEncoder + charset string + src, exp string + }{ + {QEncoding, utf8, "François-Jérôme", "=?utf-8?q?Fran=C3=A7ois-J=C3=A9r=C3=B4me?="}, + {BEncoding, utf8, "Café", "=?utf-8?b?Q2Fmw6k=?="}, + {QEncoding, iso88591, "La Seleção", "=?iso-8859-1?q?La_Sele=C3=A7=C3=A3o?="}, + {QEncoding, utf8, "", ""}, + {QEncoding, utf8, "A", "A"}, + {QEncoding, iso88591, "a", "a"}, + {QEncoding, utf8, "123 456", "123 456"}, + {QEncoding, utf8, "\t !\"#$%&'()*+,-./ :;<>?@[\\]^_`{|}~", "\t !\"#$%&'()*+,-./ :;<>?@[\\]^_`{|}~"}, + } + + for _, test := range tests { + if s := test.enc.Encode(test.charset, test.src); s != test.exp { + t.Errorf("Encode(%q) = %q, want %q", test.src, s, test.exp) + } + } +} + +func TestDecodeWord(t *testing.T) { + tests := []struct { + src, exp string + hasErr bool + }{ + {"=?UTF-8?Q?=C2=A1Hola,_se=C3=B1or!?=", "¡Hola, señor!", false}, + {"=?UTF-8?Q?Fran=C3=A7ois-J=C3=A9r=C3=B4me?=", "François-Jérôme", false}, + {"=?UTF-8?q?ascii?=", "ascii", false}, + {"=?utf-8?B?QW5kcsOp?=", "André", false}, + {"=?ISO-8859-1?Q?Rapha=EBl_Dupont?=", "Raphaël Dupont", false}, + {"=?utf-8?b?IkFudG9uaW8gSm9zw6kiIDxqb3NlQGV4YW1wbGUub3JnPg==?=", `"Antonio José" `, false}, + {"=?UTF-8?A?Test?=", "", true}, + {"=?UTF-8?Q?A=B?=", "", true}, + {"=?UTF-8?Q?=A?=", "", true}, + {"=?UTF-8?A?A?=", "", true}, + } + + for _, test := range tests { + dec := new(WordDecoder) + s, err := dec.Decode(test.src) + if test.hasErr && err == nil { + t.Errorf("Decode(%q) should return an error", test.src) + continue + } + if !test.hasErr && err != nil { + t.Errorf("Decode(%q): %v", test.src, err) + continue + } + if s != test.exp { + t.Errorf("Decode(%q) = %q, want %q", test.src, s, test.exp) + } + } +} + +func TestDecodeHeader(t *testing.T) { + tests := []struct { + src, exp string + }{ + {"=?UTF-8?Q?=C2=A1Hola,_se=C3=B1or!?=", "¡Hola, señor!"}, + {"=?UTF-8?Q?Fran=C3=A7ois-J=C3=A9r=C3=B4me?=", "François-Jérôme"}, + {"=?UTF-8?q?ascii?=", "ascii"}, + {"=?utf-8?B?QW5kcsOp?=", "André"}, + {"=?ISO-8859-1?Q?Rapha=EBl_Dupont?=", "Raphaël Dupont"}, + {"Jean", "Jean"}, + {"=?utf-8?b?IkFudG9uaW8gSm9zw6kiIDxqb3NlQGV4YW1wbGUub3JnPg==?=", `"Antonio José" `}, + {"=?UTF-8?A?Test?=", "=?UTF-8?A?Test?="}, + {"=?UTF-8?Q?A=B?=", "=?UTF-8?Q?A=B?="}, + {"=?UTF-8?Q?=A?=", "=?UTF-8?Q?=A?="}, + {"=?UTF-8?A?A?=", "=?UTF-8?A?A?="}, + // Incomplete words + {"=?", "=?"}, + {"=?UTF-8?", "=?UTF-8?"}, + {"=?UTF-8?=", "=?UTF-8?="}, + {"=?UTF-8?Q", "=?UTF-8?Q"}, + {"=?UTF-8?Q?", "=?UTF-8?Q?"}, + {"=?UTF-8?Q?=", "=?UTF-8?Q?="}, + {"=?UTF-8?Q?A", "=?UTF-8?Q?A"}, + {"=?UTF-8?Q?A?", "=?UTF-8?Q?A?"}, + // Tests from RFC 2047 + {"=?ISO-8859-1?Q?a?=", "a"}, + {"=?ISO-8859-1?Q?a?= b", "a b"}, + {"=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=", "ab"}, + {"=?ISO-8859-1?Q?a?= =?ISO-8859-1?Q?b?=", "ab"}, + {"=?ISO-8859-1?Q?a?= \r\n\t =?ISO-8859-1?Q?b?=", "ab"}, + {"=?ISO-8859-1?Q?a_b?=", "a b"}, + } + + for _, test := range tests { + dec := new(WordDecoder) + s, err := dec.DecodeHeader(test.src) + if err != nil { + t.Errorf("DecodeHeader(%q): %v", test.src, err) + } + if s != test.exp { + t.Errorf("DecodeHeader(%q) = %q, want %q", test.src, s, test.exp) + } + } +} + +func TestCharsetDecoder(t *testing.T) { + tests := []struct { + src string + want string + charsets []string + content []string + }{ + {"=?utf-8?b?Q2Fmw6k=?=", "Café", nil, nil}, + {"=?ISO-8859-1?Q?caf=E9?=", "café", nil, nil}, + {"=?US-ASCII?Q?foo_bar?=", "foo bar", nil, nil}, + {"=?utf-8?Q?=?=", "=?utf-8?Q?=?=", nil, nil}, + {"=?utf-8?Q?=A?=", "=?utf-8?Q?=A?=", nil, nil}, + { + "=?ISO-8859-15?Q?f=F5=F6?= =?windows-1252?Q?b=E0r?=", + "f\xf5\xf6b\xe0r", + []string{"iso-8859-15", "windows-1252"}, + []string{"f\xf5\xf6", "b\xe0r"}, + }, + } + + for _, test := range tests { + i := 0 + dec := &WordDecoder{ + CharsetReader: func(charset string, input io.Reader) (io.Reader, error) { + if charset != test.charsets[i] { + t.Errorf("DecodeHeader(%q), got charset %q, want %q", test.src, charset, test.charsets[i]) + } + content, err := ioutil.ReadAll(input) + if err != nil { + t.Errorf("DecodeHeader(%q), error in reader: %v", test.src, err) + } + got := string(content) + if got != test.content[i] { + t.Errorf("DecodeHeader(%q), got content %q, want %q", test.src, got, test.content[i]) + } + i++ + + return strings.NewReader(got), nil + }, + } + got, err := dec.DecodeHeader(test.src) + if err != nil { + t.Errorf("DecodeHeader(%q): %v", test.src, err) + } + if got != test.want { + t.Errorf("DecodeHeader(%q) = %q, want %q", test.src, got, test.want) + } + } +} + +func TestCharsetDecoderError(t *testing.T) { + dec := &WordDecoder{ + CharsetReader: func(charset string, input io.Reader) (io.Reader, error) { + return nil, errors.New("Test error") + }, + } + + if _, err := dec.DecodeHeader("=?charset?Q?foo?="); err == nil { + t.Error("DecodeHeader should return an error") + } +} + +func BenchmarkQEncodeWord(b *testing.B) { + for i := 0; i < b.N; i++ { + QEncoding.Encode("UTF-8", "¡Hola, señor!") + } +} + +func BenchmarkQDecodeWord(b *testing.B) { + dec := new(WordDecoder) + + for i := 0; i < b.N; i++ { + dec.Decode("=?utf-8?q?=C2=A1Hola,_se=C3=B1or!?=") + } +} + +func BenchmarkQDecodeHeader(b *testing.B) { + dec := new(WordDecoder) + + for i := 0; i < b.N; i++ { + dec.Decode("=?utf-8?q?=C2=A1Hola,_se=C3=B1or!?=") + } +} diff --git a/src/net/mail/message.go b/src/net/mail/message.go index f3f698cf23..77c9578196 100644 --- a/src/net/mail/message.go +++ b/src/net/mail/message.go @@ -20,9 +20,9 @@ import ( "bytes" "errors" "fmt" - "internal/mime" "io" "log" + "mime" "net/textproto" "strings" "time" @@ -177,7 +177,7 @@ func (a *Address) String() string { return b.String() } - return mime.EncodeWord(a.Name) + " " + s + return mime.QEncoding.Encode("utf-8", a.Name) + " " + s } type addrParser []byte @@ -333,9 +333,8 @@ func (p *addrParser) consumePhrase() (phrase string, err error) { word, err = p.consumeAtom(true) } - // RFC 2047 encoded-word starts with =?, ends with ?=, and has two other ?s. - if err == nil && strings.HasPrefix(word, "=?") && strings.HasSuffix(word, "?=") && strings.Count(word, "?") == 4 { - word, err = mime.DecodeWord(word) + if err == nil { + word, err = decodeRFC2047Word(word) } if err != nil { @@ -423,6 +422,32 @@ func (p *addrParser) len() int { return len(*p) } +func decodeRFC2047Word(s string) (string, error) { + dec, err := rfc2047Decoder.Decode(s) + if err == nil { + return dec, nil + } + + if _, ok := err.(charsetError); ok { + return s, err + } + + // Ignore invalid RFC 2047 encoded-word errors. + return s, nil +} + +var rfc2047Decoder = mime.WordDecoder{ + CharsetReader: func(charset string, input io.Reader) (io.Reader, error) { + return nil, charsetError(charset) + }, +} + +type charsetError string + +func (e charsetError) Error() string { + return fmt.Sprintf("charset not supported: %q", string(e)) +} + var atextChars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + From b1d144e1584ef6dbd8dfc2157133b15c48ebd6b9 Mon Sep 17 00:00:00 2001 From: Alan Donovan Date: Mon, 11 May 2015 14:19:11 -0400 Subject: [PATCH 038/232] go/constant: rename go/constants Change-Id: I4b1ce33253890de9bc64fee9b476fe52eec87fc0 Reviewed-on: https://go-review.googlesource.com/9920 Reviewed-by: Russ Cox Reviewed-by: Rob Pike --- src/go/build/deps_test.go | 6 +++--- src/go/{constants => constant}/go13.go | 2 +- src/go/{constants => constant}/go14.go | 2 +- src/go/{constants => constant}/value.go | 4 ++-- src/go/{constants => constant}/value_test.go | 2 +- src/go/internal/gcimporter/gcimporter.go | 2 +- src/go/types/api.go | 2 +- src/go/types/builtins.go | 2 +- src/go/types/check.go | 2 +- src/go/types/conversions.go | 2 +- src/go/types/decl.go | 2 +- src/go/types/expr.go | 2 +- src/go/types/object.go | 2 +- src/go/types/operand.go | 2 +- src/go/types/resolver.go | 2 +- src/go/types/self_test.go | 2 +- src/go/types/stmt.go | 2 +- src/go/types/typexpr.go | 2 +- src/go/types/universe.go | 2 +- 19 files changed, 22 insertions(+), 22 deletions(-) rename src/go/{constants => constant}/go13.go (96%) rename src/go/{constants => constant}/go14.go (93%) rename src/go/{constants => constant}/value.go (99%) rename src/go/{constants => constant}/value_test.go (99%) diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 52c5a7dd80..5a28c34adf 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -341,11 +341,11 @@ var pkgDeps = map[string][]string{ // dependencies. Do not simply update them in situ. "container/heap": {"sort"}, "debug/plan9obj": {"encoding/binary", "errors", "fmt", "io", "os"}, - "go/constants": {"fmt", "go/token", "math/big", "strconv"}, + "go/constant": {"fmt", "go/token", "math/big", "strconv"}, "go/format": {"bytes", "fmt", "go/ast", "go/parser", "go/printer", "go/token", "internal/format", "io"}, "go/importer": {"go/internal/gcimporter", "go/types", "io", "runtime"}, - "go/internal/gcimporter": {"bufio", "errors", "fmt", "go/build", "go/constants", "go/token", "go/types", "io", "os", "path/filepath", "strconv", "strings", "text/scanner"}, - "go/types": {"bytes", "container/heap", "fmt", "go/ast", "go/constants", "go/parser", "go/token", "io", "math", "path", "sort", "strconv", "strings", "sync", "unicode"}, + "go/internal/gcimporter": {"bufio", "errors", "fmt", "go/build", "go/constant", "go/token", "go/types", "io", "os", "path/filepath", "strconv", "strings", "text/scanner"}, + "go/types": {"bytes", "container/heap", "fmt", "go/ast", "go/constant", "go/parser", "go/token", "io", "math", "path", "sort", "strconv", "strings", "sync", "unicode"}, "image/internal/imageutil": {"image"}, "internal/format": {"bytes", "go/ast", "go/parser", "go/printer", "go/token", "strings"}, "internal/singleflight": {"sync"}, diff --git a/src/go/constants/go13.go b/src/go/constant/go13.go similarity index 96% rename from src/go/constants/go13.go rename to src/go/constant/go13.go index f445b82154..a4a838a290 100644 --- a/src/go/constants/go13.go +++ b/src/go/constant/go13.go @@ -4,7 +4,7 @@ // +build !go1.4 -package constants +package constant import ( "math" diff --git a/src/go/constants/go14.go b/src/go/constant/go14.go similarity index 93% rename from src/go/constants/go14.go rename to src/go/constant/go14.go index c698fa6de9..2ab6da02f6 100644 --- a/src/go/constants/go14.go +++ b/src/go/constant/go14.go @@ -4,7 +4,7 @@ // +build go1.4 -package constants +package constant import "math/big" diff --git a/src/go/constants/value.go b/src/go/constant/value.go similarity index 99% rename from src/go/constants/value.go rename to src/go/constant/value.go index ad4533c900..79a80af1ab 100644 --- a/src/go/constants/value.go +++ b/src/go/constant/value.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// Package constants implements Values representing untyped +// Package constant implements Values representing untyped // Go constants and the corresponding operations. Values // and operations may have arbitrary or unlimited precision. // @@ -11,7 +11,7 @@ // values produce unknown values unless specified // otherwise. // -package constants // import "go/constants" +package constant // import "go/constant" import ( "fmt" diff --git a/src/go/constants/value_test.go b/src/go/constant/value_test.go similarity index 99% rename from src/go/constants/value_test.go rename to src/go/constant/value_test.go index 6a74e2d13c..08cdd5e625 100644 --- a/src/go/constants/value_test.go +++ b/src/go/constant/value_test.go @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -package constants +package constant import ( "go/token" diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go index ee83a725fa..ec71d793bd 100644 --- a/src/go/internal/gcimporter/gcimporter.go +++ b/src/go/internal/gcimporter/gcimporter.go @@ -18,7 +18,7 @@ import ( "strings" "text/scanner" - exact "go/constants" + exact "go/constant" "go/types" ) diff --git a/src/go/types/api.go b/src/go/types/api.go index a2a55e31e7..ad9baa9527 100644 --- a/src/go/types/api.go +++ b/src/go/types/api.go @@ -28,7 +28,7 @@ import ( "bytes" "fmt" "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" ) diff --git a/src/go/types/builtins.go b/src/go/types/builtins.go index 203a9c196d..c224699e3c 100644 --- a/src/go/types/builtins.go +++ b/src/go/types/builtins.go @@ -8,7 +8,7 @@ package types import ( "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" ) diff --git a/src/go/types/check.go b/src/go/types/check.go index b4c356a6ed..7ae81eb2d0 100644 --- a/src/go/types/check.go +++ b/src/go/types/check.go @@ -8,7 +8,7 @@ package types import ( "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" ) diff --git a/src/go/types/conversions.go b/src/go/types/conversions.go index 0cf9953c4f..da65f4276e 100644 --- a/src/go/types/conversions.go +++ b/src/go/types/conversions.go @@ -6,7 +6,7 @@ package types -import exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove +import exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove // Conversion type-checks the conversion T(x). // The result is in x. diff --git a/src/go/types/decl.go b/src/go/types/decl.go index c2c18ecd06..4af5b57798 100644 --- a/src/go/types/decl.go +++ b/src/go/types/decl.go @@ -6,7 +6,7 @@ package types import ( "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" ) diff --git a/src/go/types/expr.go b/src/go/types/expr.go index f91d89e8b8..425ae91bb4 100644 --- a/src/go/types/expr.go +++ b/src/go/types/expr.go @@ -9,7 +9,7 @@ package types import ( "fmt" "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" "math" ) diff --git a/src/go/types/object.go b/src/go/types/object.go index 2404753b36..829e7a96b3 100644 --- a/src/go/types/object.go +++ b/src/go/types/object.go @@ -8,7 +8,7 @@ import ( "bytes" "fmt" "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" ) diff --git a/src/go/types/operand.go b/src/go/types/operand.go index 88c387058e..8d167067d5 100644 --- a/src/go/types/operand.go +++ b/src/go/types/operand.go @@ -9,7 +9,7 @@ package types import ( "bytes" "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" ) diff --git a/src/go/types/resolver.go b/src/go/types/resolver.go index be46b59f11..64dcebe216 100644 --- a/src/go/types/resolver.go +++ b/src/go/types/resolver.go @@ -7,7 +7,7 @@ package types import ( "fmt" "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" pathLib "path" "strconv" diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go index 85dc6ae0ec..e52c5afdc8 100644 --- a/src/go/types/self_test.go +++ b/src/go/types/self_test.go @@ -31,7 +31,7 @@ func TestSelf(t *testing.T) { conf := Config{Importer: importer.Default()} _, err = conf.Check("go/types", fset, files, nil) if err != nil { - // Importing go/constants doesn't work in the + // Importing go/constant doesn't work in the // build dashboard environment. Don't report an error // for now so that the build remains green. // TODO(gri) fix this diff --git a/src/go/types/stmt.go b/src/go/types/stmt.go index 8b59df3eb6..586f6cc15c 100644 --- a/src/go/types/stmt.go +++ b/src/go/types/stmt.go @@ -9,7 +9,7 @@ package types import ( "fmt" "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" ) diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index afd1dabc06..f4e4dcb040 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -8,7 +8,7 @@ package types import ( "go/ast" - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" "sort" "strconv" diff --git a/src/go/types/universe.go b/src/go/types/universe.go index c02543e951..5e445e2838 100644 --- a/src/go/types/universe.go +++ b/src/go/types/universe.go @@ -7,7 +7,7 @@ package types import ( - exact "go/constants" // Renamed to reduce diffs from x/tools. TODO: remove + exact "go/constant" // Renamed to reduce diffs from x/tools. TODO: remove "go/token" "strings" ) From 754e98cb821f6a48d21bfb8473ad3063c2021061 Mon Sep 17 00:00:00 2001 From: Rahul Chaudhry Date: Fri, 8 May 2015 12:33:30 -0700 Subject: [PATCH 039/232] cmd/dist: de-dup iOS detection Change-Id: I89778988baec1cf4a35d9342c7dbe8c4c08ff3cd Reviewed-on: https://go-review.googlesource.com/9893 Run-TryBot: Hyang-Ah Hana Kim TryBot-Result: Gobot Gobot Reviewed-by: Hyang-Ah Hana Kim --- src/cmd/dist/test.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 1ed099583e..848790ad2c 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -244,9 +244,7 @@ func (t *tester) registerTests() { }, }) - iOS := t.goos == "darwin" && (t.goarch == "arm" || t.goarch == "arm64") - - if t.cgoEnabled && t.goos != "android" && !iOS { + if t.cgoEnabled && t.goos != "android" && !t.iOS() { // Disabled on android and iOS. golang.org/issue/8345 t.tests = append(t.tests, distTest{ name: "cgo_stdio", @@ -265,7 +263,7 @@ func (t *tester) registerTests() { }, }) } - if t.cgoEnabled && t.goos != "android" && !iOS { + if t.cgoEnabled && t.goos != "android" && !t.iOS() { // TODO(crawshaw): reenable on android and iOS // golang.org/issue/8345 // @@ -295,7 +293,7 @@ func (t *tester) registerTests() { heading: "../misc/cgo/testso", fn: t.cgoTestSOWindows, }) - } else if t.hasBash() && t.goos != "android" && !iOS { + } else if t.hasBash() && t.goos != "android" && !t.iOS() { t.registerTest("testso", "../misc/cgo/testso", "./test.bash") } if t.supportedBuildmode("c-archive") { @@ -310,23 +308,23 @@ func (t *tester) registerTests() { if t.gohostos == "linux" && t.goarch == "amd64" { t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go") } - if t.hasBash() && t.goos != "android" && !iOS && t.gohostos != "windows" { + if t.hasBash() && t.goos != "android" && !t.iOS() && t.gohostos != "windows" { t.registerTest("cgo_errors", "../misc/cgo/errors", "./test.bash") } if t.gohostos == "linux" && t.extLink() { t.registerTest("testsigfwd", "../misc/cgo/testsigfwd", "go", "run", "main.go") } } - if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !iOS { + if t.hasBash() && t.goos != "nacl" && t.goos != "android" && !t.iOS() { t.registerTest("doc_progs", "../doc/progs", "time", "go", "run", "run.go") t.registerTest("wiki", "../doc/articles/wiki", "./test.bash") t.registerTest("codewalk", "../doc/codewalk", "time", "./run") t.registerTest("shootout", "../test/bench/shootout", "time", "./timing.sh", "-test") } - if t.goos != "android" && !iOS { + if t.goos != "android" && !t.iOS() { t.registerTest("bench_go1", "../test/bench/go1", "go", "test") } - if t.goos != "android" && !iOS { + if t.goos != "android" && !t.iOS() { // TODO(bradfitz): shard down into these tests, as // this is one of the slowest (and most shardable) // tests. @@ -336,7 +334,7 @@ func (t *tester) registerTests() { fn: t.testDirTest, }) } - if t.goos != "nacl" && t.goos != "android" && !iOS { + if t.goos != "nacl" && t.goos != "android" && !t.iOS() { t.tests = append(t.tests, distTest{ name: "api", heading: "API check", From 64b1aa12b3c9a524179a583da13fa82dd4812559 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Mon, 11 May 2015 18:02:27 +0900 Subject: [PATCH 040/232] net: drop unnecessary cast Change-Id: I9b058472f5b4943db6e6f1c1243411ce61624c18 Reviewed-on: https://go-review.googlesource.com/9916 Reviewed-by: Brad Fitzpatrick --- src/net/fd_unix.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index 64e94fecf8..f2d7b348bf 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -225,7 +225,7 @@ func (fd *netFD) Read(p []byte) (n int, err error) { return 0, err } for { - n, err = syscall.Read(int(fd.sysfd), p) + n, err = syscall.Read(fd.sysfd, p) if err != nil { n = 0 if err == syscall.EAGAIN { @@ -307,7 +307,7 @@ func (fd *netFD) Write(p []byte) (nn int, err error) { } for { var n int - n, err = syscall.Write(int(fd.sysfd), p[nn:]) + n, err = syscall.Write(fd.sysfd, p[nn:]) if n > 0 { nn += n } From be0cb9224b68d5be4e03fd35396d2c2f0755adad Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Tue, 12 May 2015 11:59:14 +1200 Subject: [PATCH 041/232] runtime: fix addmoduledata to follow the platform ABI addmoduledata is called from a .init_array function and need to follow the platform ABI. It contains accesses to global data which are rewritten to use R15 by the assembler, and as R15 is callee-save we need to save it. Change-Id: I03893efb1576aed4f102f2465421f256f3bb0f30 Reviewed-on: https://go-review.googlesource.com/9941 Reviewed-by: Ian Lance Taylor --- misc/cgo/testshared/test.bash | 1 + src/runtime/asm_amd64.s | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/misc/cgo/testshared/test.bash b/misc/cgo/testshared/test.bash index 21004adaf8..0b0d0411f7 100755 --- a/misc/cgo/testshared/test.bash +++ b/misc/cgo/testshared/test.bash @@ -78,6 +78,7 @@ ensure_ldd $rootdir/libdep.so $std_install_dir/$soname # And exe that links against both go install -installsuffix="$mysuffix" -linkshared exe +./bin/exe || die "./bin/exe failed with code $?" ensure_ldd ./bin/exe $rootdir/libdep.so ensure_ldd ./bin/exe $std_install_dir/$soname diff --git a/src/runtime/asm_amd64.s b/src/runtime/asm_amd64.s index 36353d108f..0f9aeb8f37 100644 --- a/src/runtime/asm_amd64.s +++ b/src/runtime/asm_amd64.s @@ -1693,8 +1693,10 @@ TEXT runtime·prefetchnta(SB),NOSPLIT,$0-8 RET // This is called from .init_array and follows the platform, not Go, ABI. -TEXT runtime·addmoduledata(SB),NOSPLIT,$0-8 +TEXT runtime·addmoduledata(SB),NOSPLIT,$0-0 + PUSHQ R15 // The access to global variables below implicitly uses R15, which is callee-save MOVQ runtime·lastmoduledatap(SB), AX MOVQ DI, moduledata_next(AX) MOVQ DI, runtime·lastmoduledatap(SB) + POPQ R15 RET From 77fc03f4cd7f6ea0b142bd17ea172205d5f45cff Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Sat, 11 Apr 2015 12:05:21 +0800 Subject: [PATCH 042/232] cmd/internal/ld, runtime: abort on shared library ABI mismatch This: 1) Defines the ABI hash of a package (as the SHA1 of the __.PKGDEF) 2) Defines the ABI hash of a shared library (sort the packages by import path, concatenate the hashes of the packages and SHA1 that) 3) When building a shared library, compute the above value and define a global symbol that points to a go string that has the hash as its value. 4) When linking against a shared library, read the abi hash from the library and put both the value seen at link time and a reference to the global symbol into the moduledata. 5) During runtime initialization, check that the hash seen at link time still matches the hash the global symbol points to. Change-Id: Iaa54c783790e6dde3057a2feadc35473d49614a5 Reviewed-on: https://go-review.googlesource.com/8773 Reviewed-by: Ian Lance Taylor Run-TryBot: Michael Hudson-Doyle --- misc/cgo/testshared/test.bash | 30 ++++++++++++- src/cmd/internal/ld/data.go | 16 +++++++ src/cmd/internal/ld/ld.go | 4 +- src/cmd/internal/ld/lib.go | 85 ++++++++++++++++++++--------------- src/cmd/internal/ld/link.go | 10 ++++- src/cmd/internal/ld/symtab.go | 63 ++++++++++++++++++++++++++ src/runtime/symtab.go | 20 +++++++++ 7 files changed, 186 insertions(+), 42 deletions(-) diff --git a/misc/cgo/testshared/test.bash b/misc/cgo/testshared/test.bash index 0b0d0411f7..0d67ff1719 100755 --- a/misc/cgo/testshared/test.bash +++ b/misc/cgo/testshared/test.bash @@ -8,7 +8,6 @@ set -eu -export GOPATH="$(pwd)" die () { echo $@ @@ -23,8 +22,14 @@ rootdir="$(dirname $(go list -f '{{.Target}}' runtime))" template="${rootdir}_XXXXXXXX_dynlink" std_install_dir=$(mktemp -d "$template") +scratch_dir=$(mktemp -d) +cp -a . $scratch_dir +opwd="$(pwd)" +cd $scratch_dir +export GOPATH="$(pwd)" + cleanup () { - rm -rf $std_install_dir ./bin/ ./pkg/ + rm -rf $std_install_dir $scratch_dir } trap cleanup EXIT @@ -109,3 +114,24 @@ will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a go install -installsuffix="$mysuffix" -linkshared exe assert_not_rebuilt $rootdir/dep.a assert_rebuilt $rootdir/libdep.so + +# If we make an ABI-breaking change to dep and rebuild libp.so but not exe, exe will +# abort with a complaint on startup. +# This assumes adding an exported function breaks ABI, which is not true in some +# senses but suffices for the narrow definition of ABI compatiblity the toolchain +# uses today. +echo "func ABIBreak() {}" >> src/dep/dep.go +go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep +output="$(./bin/exe 2>&1)" && die "exe succeeded after ABI break" || true +msg="abi mismatch detected between the executable and libdep.so" +{ echo "$output" | grep -q "$msg"; } || die "exe did not fail with expected message" + +# Rebuilding exe makes it work again. +go install -installsuffix="$mysuffix" -linkshared exe +./bin/exe || die "exe failed after rebuild" + +# If we make a change which does not break ABI (such as adding an +# unexported function) and rebuild libdep.so, exe still works. +echo "func noABIBreak() {}" >> src/dep/dep.go +go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep +./bin/exe || die "exe failed after non-ABI breaking change" diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go index 37d458802f..b0157547c3 100644 --- a/src/cmd/internal/ld/data.go +++ b/src/cmd/internal/ld/data.go @@ -963,6 +963,22 @@ func Addstring(s *LSym, str string) int64 { return int64(r) } +// addgostring adds str, as a Go string value, to s. symname is the name of the +// symbol used to define the string data and must be unique per linked object. +func addgostring(s *LSym, symname, str string) { + sym := Linklookup(Ctxt, symname, 0) + if sym.Type != obj.Sxxx { + Diag("duplicate symname in addgostring: %s", symname) + } + sym.Reachable = true + sym.Local = true + sym.Type = obj.SRODATA + sym.Size = int64(len(str)) + sym.P = []byte(str) + Addaddr(Ctxt, s, sym) + adduint(Ctxt, s, uint64(len(str))) +} + func addinitarrdata(s *LSym) { p := s.Name + ".ptr" sp := Linklookup(Ctxt, p, 0) diff --git a/src/cmd/internal/ld/ld.go b/src/cmd/internal/ld/ld.go index 7242301d0f..1068bdd767 100644 --- a/src/cmd/internal/ld/ld.go +++ b/src/cmd/internal/ld/ld.go @@ -109,8 +109,8 @@ func addlibpath(ctxt *Link, srcref string, objref string, file string, pkg strin fmt.Fprintf(ctxt.Bso, "%5.2f addlibpath: srcref: %s objref: %s file: %s pkg: %s shlibnamefile: %s\n", obj.Cputime(), srcref, objref, file, pkg, shlibnamefile) } - ctxt.Library = append(ctxt.Library, Library{}) - l := &ctxt.Library[len(ctxt.Library)-1] + ctxt.Library = append(ctxt.Library, &Library{}) + l := ctxt.Library[len(ctxt.Library)-1] l.Objref = objref l.Srcref = srcref l.File = file diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go index e4e68eae27..d4e67800d2 100644 --- a/src/cmd/internal/ld/lib.go +++ b/src/cmd/internal/ld/lib.go @@ -34,6 +34,7 @@ import ( "bufio" "bytes" "cmd/internal/obj" + "crypto/sha1" "debug/elf" "fmt" "io" @@ -474,7 +475,7 @@ func loadlib() { if Ctxt.Library[i].Shlib != "" { ldshlibsyms(Ctxt.Library[i].Shlib) } else { - objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg) + objfile(Ctxt.Library[i]) } } @@ -520,7 +521,7 @@ func loadlib() { if DynlinkingGo() { Exitf("cannot implicitly include runtime/cgo in a shared library") } - objfile(Ctxt.Library[i].File, Ctxt.Library[i].Pkg) + objfile(Ctxt.Library[i]) } } } @@ -631,18 +632,18 @@ func nextar(bp *obj.Biobuf, off int64, a *ArHdr) int64 { return int64(arsize) + SAR_HDR } -func objfile(file string, pkg string) { - pkg = pathtoprefix(pkg) +func objfile(lib *Library) { + pkg := pathtoprefix(lib.Pkg) if Debug['v'] > 1 { - fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), file, pkg) + fmt.Fprintf(&Bso, "%5.2f ldobj: %s (%s)\n", obj.Cputime(), lib.File, pkg) } Bso.Flush() var err error var f *obj.Biobuf - f, err = obj.Bopenr(file) + f, err = obj.Bopenr(lib.File) if err != nil { - Exitf("cannot open file %s: %v", file, err) + Exitf("cannot open file %s: %v", lib.File, err) } magbuf := make([]byte, len(ARMAG)) @@ -651,7 +652,7 @@ func objfile(file string, pkg string) { l := obj.Bseek(f, 0, 2) obj.Bseek(f, 0, 0) - ldobj(f, pkg, l, file, file, FileObj) + ldobj(f, pkg, l, lib.File, lib.File, FileObj) obj.Bterm(f) return @@ -664,7 +665,7 @@ func objfile(file string, pkg string) { l := nextar(f, off, &arhdr) var pname string if l <= 0 { - Diag("%s: short read on archive file symbol header", file) + Diag("%s: short read on archive file symbol header", lib.File) goto out } @@ -672,20 +673,29 @@ func objfile(file string, pkg string) { off += l l = nextar(f, off, &arhdr) if l <= 0 { - Diag("%s: short read on archive file symbol header", file) + Diag("%s: short read on archive file symbol header", lib.File) goto out } } if !strings.HasPrefix(arhdr.name, pkgname) { - Diag("%s: cannot find package header", file) + Diag("%s: cannot find package header", lib.File) goto out } + if Buildmode == BuildmodeShared { + before := obj.Boffset(f) + pkgdefBytes := make([]byte, atolwhex(arhdr.size)) + obj.Bread(f, pkgdefBytes) + hash := sha1.Sum(pkgdefBytes) + lib.hash = hash[:] + obj.Bseek(f, before, 0) + } + off += l if Debug['u'] != 0 { - ldpkg(f, pkg, atolwhex(arhdr.size), file, Pkgdef) + ldpkg(f, pkg, atolwhex(arhdr.size), lib.File, Pkgdef) } /* @@ -706,14 +716,14 @@ func objfile(file string, pkg string) { break } if l < 0 { - Exitf("%s: malformed archive", file) + Exitf("%s: malformed archive", lib.File) } off += l - pname = fmt.Sprintf("%s(%s)", file, arhdr.name) + pname = fmt.Sprintf("%s(%s)", lib.File, arhdr.name) l = atolwhex(arhdr.size) - ldobj(f, pkg, l, pname, file, ArchiveObj) + ldobj(f, pkg, l, pname, lib.File, ArchiveObj) } out: @@ -974,7 +984,7 @@ func hostlink() { if Linkshared { for _, shlib := range Ctxt.Shlibs { - dir, base := filepath.Split(shlib) + dir, base := filepath.Split(shlib.Path) argv = append(argv, "-L"+dir) if !rpath.set { argv = append(argv, "-Wl,-rpath="+dir) @@ -1120,6 +1130,19 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when ldobjfile(Ctxt, f, pkg, eof-obj.Boffset(f), pn) } +func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte { + data := make([]byte, sym.Size) + sect := f.Sections[sym.Section] + if sect.Type != elf.SHT_PROGBITS { + Diag("reading %s from non-PROGBITS section", sym.Name) + } + n, err := sect.ReadAt(data, int64(sym.Value-sect.Offset)) + if uint64(n) != sym.Size { + Diag("reading contents of %s: %v", sym.Name, err) + } + return data +} + func ldshlibsyms(shlib string) { found := false libpath := "" @@ -1134,8 +1157,8 @@ func ldshlibsyms(shlib string) { Diag("cannot find shared library: %s", shlib) return } - for _, processedname := range Ctxt.Shlibs { - if processedname == libpath { + for _, processedlib := range Ctxt.Shlibs { + if processedlib.Path == libpath { return } } @@ -1167,6 +1190,7 @@ func ldshlibsyms(shlib string) { // table removed. gcmasks := make(map[uint64][]byte) types := []*LSym{} + var hash []byte for _, s := range syms { if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION { continue @@ -1178,15 +1202,10 @@ func ldshlibsyms(shlib string) { continue } if strings.HasPrefix(s.Name, "runtime.gcbits.0x") { - data := make([]byte, s.Size) - sect := f.Sections[s.Section] - if sect.Type == elf.SHT_PROGBITS { - n, err := sect.ReadAt(data, int64(s.Value-sect.Offset)) - if uint64(n) != s.Size { - Diag("Error reading contents of %s: %v", s.Name, err) - } - } - gcmasks[s.Value] = data + gcmasks[s.Value] = readelfsymboldata(f, &s) + } + if s.Name == "go.link.abihashbytes" { + hash = readelfsymboldata(f, &s) } if elf.ST_BIND(s.Info) != elf.STB_GLOBAL { continue @@ -1201,14 +1220,8 @@ func ldshlibsyms(shlib string) { lsym.ElfType = elf.ST_TYPE(s.Info) lsym.File = libpath if strings.HasPrefix(lsym.Name, "type.") { - data := make([]byte, s.Size) - sect := f.Sections[s.Section] - if sect.Type == elf.SHT_PROGBITS { - n, err := sect.ReadAt(data, int64(s.Value-sect.Offset)) - if uint64(n) != s.Size { - Diag("Error reading contents of %s: %v", s.Name, err) - } - lsym.P = data + if f.Sections[s.Section].Type == elf.SHT_PROGBITS { + lsym.P = readelfsymboldata(f, &s) } if !strings.HasPrefix(lsym.Name, "type..") { types = append(types, lsym) @@ -1255,7 +1268,7 @@ func ldshlibsyms(shlib string) { Ctxt.Etextp = last } - Ctxt.Shlibs = append(Ctxt.Shlibs, libpath) + Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash}) } func mywhatsys() { diff --git a/src/cmd/internal/ld/link.go b/src/cmd/internal/ld/link.go index 03da52a981..a314ca1370 100644 --- a/src/cmd/internal/ld/link.go +++ b/src/cmd/internal/ld/link.go @@ -106,6 +106,11 @@ type Auto struct { Gotype *LSym } +type Shlib struct { + Path string + Hash []byte +} + type Link struct { Thechar int32 Thestring string @@ -122,8 +127,8 @@ type Link struct { Nsymbol int32 Tlsg *LSym Libdir []string - Library []Library - Shlibs []string + Library []*Library + Shlibs []Shlib Tlsoffset int Diag func(string, ...interface{}) Cursym *LSym @@ -149,6 +154,7 @@ type Library struct { File string Pkg string Shlib string + hash []byte } type Pcln struct { diff --git a/src/cmd/internal/ld/symtab.go b/src/cmd/internal/ld/symtab.go index d6e79dc00f..ca66541935 100644 --- a/src/cmd/internal/ld/symtab.go +++ b/src/cmd/internal/ld/symtab.go @@ -32,6 +32,10 @@ package ld import ( "cmd/internal/obj" + "crypto/sha1" + "fmt" + "path/filepath" + "sort" "strings" ) @@ -294,6 +298,20 @@ func Vputl(v uint64) { Lputl(uint32(v >> 32)) } +type byPkg []*Library + +func (libs byPkg) Len() int { + return len(libs) +} + +func (libs byPkg) Less(a, b int) bool { + return libs[a].Pkg < libs[b].Pkg +} + +func (libs byPkg) Swap(a, b int) { + libs[a], libs[b] = libs[b], libs[a] +} + func symtab() { dosymtype() @@ -410,6 +428,19 @@ func symtab() { } } + if Buildmode == BuildmodeShared { + sort.Sort(byPkg(Ctxt.Library)) + h := sha1.New() + for _, l := range Ctxt.Library { + h.Write(l.hash) + } + abihashgostr := Linklookup(Ctxt, "go.link.abihash."+filepath.Base(outfile), 0) + abihashgostr.Reachable = true + abihashgostr.Type = obj.SRODATA + var hashbytes []byte + addgostring(abihashgostr, "go.link.abihashbytes", string(h.Sum(hashbytes))) + } + // Information about the layout of the executable image for the // runtime to use. Any changes here must be matched by changes to // the definition of moduledata in runtime/symtab.go. @@ -454,6 +485,38 @@ func symtab() { Addaddr(Ctxt, moduledata, Linklookup(Ctxt, "runtime.typelink", 0)) adduint(Ctxt, moduledata, uint64(ntypelinks)) adduint(Ctxt, moduledata, uint64(ntypelinks)) + if len(Ctxt.Shlibs) > 0 { + thismodulename := filepath.Base(outfile) + if Buildmode == BuildmodeExe { + // When linking an executable, outfile is just "a.out". Make + // it something slightly more comprehensible. + thismodulename = "the executable" + } + addgostring(moduledata, "go.link.thismodulename", thismodulename) + + modulehashes := Linklookup(Ctxt, "go.link.abihashes", 0) + modulehashes.Reachable = true + modulehashes.Local = true + modulehashes.Type = obj.SRODATA + + for i, shlib := range Ctxt.Shlibs { + // modulehashes[i].modulename + modulename := filepath.Base(shlib.Path) + addgostring(modulehashes, fmt.Sprintf("go.link.libname.%d", i), modulename) + + // modulehashes[i].linktimehash + addgostring(modulehashes, fmt.Sprintf("go.link.linkhash.%d", i), string(shlib.Hash)) + + // modulehashes[i].runtimehash + abihash := Linklookup(Ctxt, "go.link.abihash."+modulename, 0) + abihash.Reachable = true + Addaddr(Ctxt, modulehashes, abihash) + } + + Addaddr(Ctxt, moduledata, modulehashes) + adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs))) + adduint(Ctxt, moduledata, uint64(len(Ctxt.Shlibs))) + } // The rest of moduledata is zero initialized. // When linking an object that does not contain the runtime we are // creating the moduledata from scratch and it does not have a diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index bbf00bf134..9afa954259 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -48,11 +48,24 @@ type moduledata struct { typelinks []*_type + modulename string + modulehashes []modulehash + gcdatamask, gcbssmask bitvector next *moduledata } +// For each shared library a module links against, the linker creates an entry in the +// moduledata.modulehashes slice containing the name of the module, the abi hash seen +// at link time and a pointer to the runtime abi hash. These are checked in +// moduledataverify1 below. +type modulehash struct { + modulename string + linktimehash string + runtimehash *string +} + var firstmoduledata moduledata // linker symbol var lastmoduledatap *moduledata // linker symbol @@ -117,6 +130,13 @@ func moduledataverify1(datap *moduledata) { datap.maxpc != datap.ftab[nftab].entry { throw("minpc or maxpc invalid") } + + for _, modulehash := range datap.modulehashes { + if modulehash.linktimehash != *modulehash.runtimehash { + println("abi mismatch detected between", datap.modulename, "and", modulehash.modulename) + throw("abi mismatch") + } + } } // FuncForPC returns a *Func describing the function that contains the From 7bbd4f780b08c08b841766602b3cc4484ae34ed3 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Mon, 11 May 2015 20:59:59 -0400 Subject: [PATCH 043/232] syscall: fix running mkall.sh on linux/{ppc64,ppc64le} Change-Id: I58c6e914d0e977d5748c87d277e30c933ed86f99 Reviewed-on: https://go-review.googlesource.com/9924 Reviewed-by: Ian Lance Taylor --- src/syscall/mkall.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/syscall/mkall.sh b/src/syscall/mkall.sh index 1b7cd64c8d..739663ed9c 100755 --- a/src/syscall/mkall.sh +++ b/src/syscall/mkall.sh @@ -285,7 +285,7 @@ esac syscall_goos="syscall_bsd.go $syscall_goos" ;; esac - if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos syscall_$GOOSARCH.go |gofmt >zsyscall_$GOOSARCH.go"; fi + if [ -n "$mksyscall" ]; then echo "$mksyscall $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"; fi if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi if [ -n "$mktypes" ]; then echo "$mktypes types_$GOOS.go |gofmt >ztypes_$GOOSARCH.go"; fi From 71bf182028c2c7dd70e6a391a6519eb17dd06b3c Mon Sep 17 00:00:00 2001 From: Alex Brainman Date: Tue, 12 May 2015 11:34:23 +1000 Subject: [PATCH 044/232] net: relax error checking in TestAcceptIgnoreSomeErrors TestAcceptIgnoreSomeErrors was created to test that network accept function ignores some errors. But conditions created by the test also affects network reads. Change the test to ignore these read errors when acceptable. Fixes #10785 Change-Id: I3da85cb55bd3e78c1980ad949e53e82391f9b41e Reviewed-on: https://go-review.googlesource.com/9942 Reviewed-by: Brad Fitzpatrick --- src/net/net_windows_test.go | 43 ++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/src/net/net_windows_test.go b/src/net/net_windows_test.go index 21b47964a4..da03e10b36 100644 --- a/src/net/net_windows_test.go +++ b/src/net/net_windows_test.go @@ -15,16 +15,31 @@ import ( "time" ) +func toErrno(err error) (syscall.Errno, bool) { + operr, ok := err.(*OpError) + if !ok { + return 0, false + } + syserr, ok := operr.Err.(*os.SyscallError) + if !ok { + return 0, false + } + errno, ok := syserr.Err.(syscall.Errno) + if !ok { + return 0, false + } + return errno, true +} + +// TestAcceptIgnoreSomeErrors tests that windows TCPListener.AcceptTCP +// handles broken connections. It verifies that broken connections do +// not affect future connections. func TestAcceptIgnoreSomeErrors(t *testing.T) { - recv := func(ln Listener) (string, error) { + recv := func(ln Listener, ignoreSomeReadErrors bool) (string, error) { c, err := ln.Accept() if err != nil { // Display windows errno in error message. - operr, ok := err.(*OpError) - if !ok { - return "", err - } - errno, ok := operr.Err.(syscall.Errno) + errno, ok := toErrno(err) if !ok { return "", err } @@ -34,10 +49,14 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) { b := make([]byte, 100) n, err := c.Read(b) - if err != nil && err != io.EOF { - return "", err + if err == nil || err == io.EOF { + return string(b[:n]), nil } - return string(b[:n]), nil + errno, ok := toErrno(err) + if ok && ignoreSomeReadErrors && (errno == syscall.ERROR_NETNAME_DELETED || errno == syscall.WSAECONNRESET) { + return "", nil + } + return "", err } send := func(addr string, data string) error { @@ -121,13 +140,13 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) { }() // Receive first or second connection. - s, err := recv(ln) + s, err := recv(ln, true) if err != nil { t.Fatalf("recv failed: %v", err) } switch s { case "": - // First connection data is received, lets get second connection data. + // First connection data is received, let's get second connection data. case "abc": // First connection is lost forever, but that is ok. return @@ -136,7 +155,7 @@ func TestAcceptIgnoreSomeErrors(t *testing.T) { } // Get second connection data. - s, err = recv(ln) + s, err = recv(ln, false) if err != nil { t.Fatalf("recv failed: %v", err) } From 51021cc83f54964aa5e6a71cdf2206ae169acbb8 Mon Sep 17 00:00:00 2001 From: Patrick Mezard Date: Tue, 12 May 2015 08:19:00 +0200 Subject: [PATCH 045/232] time: fix registry zone info lookup on Windows registry.ReadSubKeyNames requires QUERY access right in addition to ENUMERATE_SUB_KEYS. This was making TestLocalZoneAbbr fail on Windows 7 in Paris/Madrid timezone. It succeeded on Windows 8 because timezone name changed from "Paris/Madrid" to "Romance Standard Time", the latter being matched by an abbrs entry. Change-Id: I791287ba9d1b3556246fa4e9e1604a1fbba1f5e6 Reviewed-on: https://go-review.googlesource.com/9809 Reviewed-by: Alex Brainman Reviewed-by: Brad Fitzpatrick --- src/time/export_windows_test.go | 4 ++++ src/time/zoneinfo_windows.go | 2 +- src/time/zoneinfo_windows_test.go | 25 +++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/time/export_windows_test.go b/src/time/export_windows_test.go index 7e689b829f..6fd4509137 100644 --- a/src/time/export_windows_test.go +++ b/src/time/export_windows_test.go @@ -8,3 +8,7 @@ func ForceAusForTesting() { ResetLocalOnceForTest() localOnce.Do(initAusTestingZone) } + +func ToEnglishName(stdname, dstname string) (string, error) { + return toEnglishName(stdname, dstname) +} diff --git a/src/time/zoneinfo_windows.go b/src/time/zoneinfo_windows.go index 9f987ab302..d04ebec614 100644 --- a/src/time/zoneinfo_windows.go +++ b/src/time/zoneinfo_windows.go @@ -49,7 +49,7 @@ func matchZoneKey(zones registry.Key, kname string, stdname, dstname string) (ma // toEnglishName searches the registry for an English name of a time zone // whose zone names are stdname and dstname and returns the English name. func toEnglishName(stdname, dstname string) (string, error) { - k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS) + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`, registry.ENUMERATE_SUB_KEYS|registry.QUERY_VALUE) if err != nil { return "", err } diff --git a/src/time/zoneinfo_windows_test.go b/src/time/zoneinfo_windows_test.go index 9db81b7cfd..5f1141d3ca 100644 --- a/src/time/zoneinfo_windows_test.go +++ b/src/time/zoneinfo_windows_test.go @@ -5,6 +5,7 @@ package time_test import ( + "internal/syscall/windows/registry" "testing" . "time" ) @@ -33,3 +34,27 @@ func TestAusZoneAbbr(t *testing.T) { defer ForceUSPacificForTesting() testZoneAbbr(t) } + +func TestToEnglishName(t *testing.T) { + const want = "Central Europe Standard Time" + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones\`+want, registry.READ) + if err != nil { + t.Fatalf("cannot open CEST time zone information from registry: %s", err) + } + defer k.Close() + std, _, err := k.GetStringValue("Std") + if err != nil { + t.Fatalf("cannot read CEST Std registry key: %s", err) + } + dlt, _, err := k.GetStringValue("Dlt") + if err != nil { + t.Fatalf("cannot read CEST Dlt registry key: %s", err) + } + name, err := ToEnglishName(std, dlt) + if err != nil { + t.Fatalf("toEnglishName failed: %s", err) + } + if name != want { + t.Fatalf("english name: %q, want: %q", name, want) + } +} From 29dc4b40f85fdb0985eea3e718385dc3c6cd22b7 Mon Sep 17 00:00:00 2001 From: Jens Frederich Date: Thu, 19 Feb 2015 21:37:38 +0100 Subject: [PATCH 046/232] cmd/go: "go get" don't ignore git default branch Any Git branch can be the default branch not only master. Removing hardwired 'checkout master', and using 'checkout {tag}' is the best choice. It works with and without a master branch. Furthermore it resolves the Github default branch issue. Changing Github default branch is effectively changing HEAD. Fixes #9032 Change-Id: I19a1221bcefe0806e7556c124c6da7ac0c2160b5 Reviewed-on: https://go-review.googlesource.com/5312 Reviewed-by: Russ Cox --- src/cmd/go/vcs.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cmd/go/vcs.go b/src/cmd/go/vcs.go index 408104d776..2179000afd 100644 --- a/src/cmd/go/vcs.go +++ b/src/cmd/go/vcs.go @@ -115,8 +115,12 @@ var vcsGit = &vcsCmd{ tagLookupCmd: []tagCmd{ {"show-ref tags/{tag} origin/{tag}", `((?:tags|origin)/\S+)$`}, }, - tagSyncCmd: "checkout {tag}", - tagSyncDefault: "checkout master", + tagSyncCmd: "checkout {tag}", + // both createCmd and downloadCmd update the working dir. + // No need to do more here. We used to 'checkout master' + // but that doesn't work if the default branch is not named master. + // See golang.org/issue/9032. + tagSyncDefault: "", scheme: []string{"git", "https", "http", "git+ssh"}, pingCmd: "ls-remote {scheme}://{repo}", From 3f209abb2954bfb89e3dbd28ed0a622a6fe33242 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 11 May 2015 16:12:01 -0400 Subject: [PATCH 047/232] cmd/internal/gc: detect bad append(f()) during type check Today's earlier fix can stay, but it's a band-aid over the real problem, which is that bad code was slipping through the type checker into the back end (and luckily causing a type error there). I discovered this because my new append does not use the same temporaries and failed the test as written. Fixes #9521. Change-Id: I7e33e2ea15743406e15c6f3fdf73e1edecda69bd Reviewed-on: https://go-review.googlesource.com/9921 Reviewed-by: Ian Lance Taylor --- src/cmd/internal/gc/typecheck.go | 23 +++++++++++++++-------- test/fixedbugs/issue9521.go | 6 ++++-- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go index fdd393d0cf..185cfecc68 100644 --- a/src/cmd/internal/gc/typecheck.go +++ b/src/cmd/internal/gc/typecheck.go @@ -1636,11 +1636,10 @@ OpSwitch: } // Unpack multiple-return result before type-checking. + var funarg *Type if Istype(t, TSTRUCT) && t.Funarg != 0 { - t = t.Type - if Istype(t, TFIELD) { - t = t.Type - } + funarg = t + t = t.Type.Type } n.Type = t @@ -1678,11 +1677,19 @@ OpSwitch: break OpSwitch } - for args = args.Next; args != nil; args = args.Next { - if args.N.Type == nil { - continue + if funarg != nil { + for t := funarg.Type.Down; t != nil; t = t.Down { + if assignop(t.Type, n.Type.Type, nil) == 0 { + Yyerror("cannot append %v value to []%v", t.Type, n.Type.Type) + } + } + } else { + for args = args.Next; args != nil; args = args.Next { + if args.N.Type == nil { + continue + } + args.N = assignconv(args.N, t.Type, "append") } - args.N = assignconv(args.N, t.Type, "append") } break OpSwitch diff --git a/test/fixedbugs/issue9521.go b/test/fixedbugs/issue9521.go index 51b5204e7a..ef0a5a6547 100644 --- a/test/fixedbugs/issue9521.go +++ b/test/fixedbugs/issue9521.go @@ -9,8 +9,10 @@ package main -func f() (_, _ []int) { return } +func f() (_, _ []int) { return } +func g() (x []int, y float64) { return } func main() { - _ = append(f()) // ERROR "cannot use _" + _ = append(f()) // ERROR "cannot append \[\]int value to \[\]int" + _ = append(g()) // ERROR "cannot append float64 value to \[\]int" } From 18d98bc9cb34df680ae3dac89712366a9883789f Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 30 Apr 2015 20:35:47 -0400 Subject: [PATCH 048/232] cmd/internal/gc: avoid turning 'x = f()' into 'tmp = f(); x = tmp' for simple x MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This slows down more things than I expected, but it also speeds things up, and it reduces stack frame sizes and the load on the optimizer, so it's still likely a net win. name old mean new mean delta BenchmarkBinaryTree17 13.2s × (0.98,1.03) 13.2s × (0.98,1.02) ~ (p=0.795) BenchmarkFannkuch11 4.41s × (1.00,1.00) 4.45s × (0.99,1.01) +0.88% (p=0.000) BenchmarkFmtFprintfEmpty 86.4ns × (0.99,1.01) 90.1ns × (0.95,1.05) +4.31% (p=0.000) BenchmarkFmtFprintfString 318ns × (0.96,1.07) 337ns × (0.98,1.03) +6.05% (p=0.000) BenchmarkFmtFprintfInt 332ns × (0.97,1.04) 320ns × (0.97,1.02) -3.42% (p=0.000) BenchmarkFmtFprintfIntInt 562ns × (0.96,1.04) 574ns × (0.96,1.06) +2.00% (p=0.013) BenchmarkFmtFprintfPrefixedInt 442ns × (0.96,1.06) 450ns × (0.97,1.05) +1.73% (p=0.039) BenchmarkFmtFprintfFloat 640ns × (0.99,1.02) 659ns × (0.99,1.03) +3.01% (p=0.000) BenchmarkFmtManyArgs 2.19µs × (0.97,1.06) 2.21µs × (0.98,1.02) ~ (p=0.104) BenchmarkGobDecode 20.0ms × (0.98,1.03) 19.7ms × (0.97,1.04) -1.35% (p=0.035) BenchmarkGobEncode 17.8ms × (0.96,1.04) 18.0ms × (0.96,1.06) ~ (p=0.131) BenchmarkGzip 653ms × (0.99,1.02) 652ms × (0.99,1.01) ~ (p=0.572) BenchmarkGunzip 143ms × (0.99,1.02) 142ms × (1.00,1.01) -0.52% (p=0.005) BenchmarkHTTPClientServer 110µs × (0.98,1.03) 108µs × (0.99,1.02) -1.90% (p=0.000) BenchmarkJSONEncode 40.0ms × (0.98,1.05) 41.5ms × (0.97,1.06) +3.89% (p=0.000) BenchmarkJSONDecode 118ms × (0.99,1.01) 118ms × (0.98,1.01) +0.69% (p=0.010) BenchmarkMandelbrot200 6.03ms × (1.00,1.01) 6.03ms × (1.00,1.01) ~ (p=0.924) BenchmarkGoParse 8.43ms × (0.92,1.11) 8.56ms × (0.93,1.05) ~ (p=0.242) BenchmarkRegexpMatchEasy0_32 180ns × (0.91,1.07) 163ns × (1.00,1.00) -9.33% (p=0.000) BenchmarkRegexpMatchEasy0_1K 550ns × (0.98,1.02) 558ns × (0.99,1.01) +1.44% (p=0.000) BenchmarkRegexpMatchEasy1_32 152ns × (0.94,1.05) 139ns × (0.98,1.02) -8.51% (p=0.000) BenchmarkRegexpMatchEasy1_1K 909ns × (0.98,1.06) 868ns × (0.99,1.02) -4.52% (p=0.000) BenchmarkRegexpMatchMedium_32 262ns × (0.97,1.03) 253ns × (0.99,1.02) -3.31% (p=0.000) BenchmarkRegexpMatchMedium_1K 73.8µs × (0.98,1.04) 72.7µs × (1.00,1.01) -1.61% (p=0.001) BenchmarkRegexpMatchHard_32 3.87µs × (0.99,1.02) 3.87µs × (1.00,1.01) ~ (p=0.791) BenchmarkRegexpMatchHard_1K 118µs × (0.98,1.04) 117µs × (0.99,1.02) ~ (p=0.110) BenchmarkRevcomp 1.00s × (0.94,1.10) 0.99s × (0.94,1.09) ~ (p=0.433) BenchmarkTemplate 140ms × (0.97,1.04) 140ms × (0.99,1.01) ~ (p=0.303) BenchmarkTimeParse 622ns × (0.99,1.02) 625ns × (0.99,1.01) +0.51% (p=0.001) BenchmarkTimeFormat 731ns × (0.98,1.04) 719ns × (0.99,1.01) -1.66% (p=0.000) Change-Id: Ibc3edb59a178adafda50156f46a341f69a17d83f Reviewed-on: https://go-review.googlesource.com/9721 Reviewed-by: David Chase --- src/cmd/internal/gc/order.go | 105 +++++++++++++++++++---------------- 1 file changed, 57 insertions(+), 48 deletions(-) diff --git a/src/cmd/internal/gc/order.go b/src/cmd/internal/gc/order.go index f08f5f20fe..82876f81bc 100644 --- a/src/cmd/internal/gc/order.go +++ b/src/cmd/internal/gc/order.go @@ -264,7 +264,7 @@ func orderblock(l **NodeList) { func orderexprinplace(np **Node, outer *Order) { n := *np var order Order - orderexpr(&n, &order) + orderexpr(&n, &order, nil) addinit(&n, order.out) // insert new temporaries from order @@ -358,8 +358,8 @@ func ordercallargs(l **NodeList, order *Order) { // Ordercall orders the call expression n. // n->op is OCALLMETH/OCALLFUNC/OCALLINTER or a builtin like OCOPY. func ordercall(n *Node, order *Order) { - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) // ODDDARG temp + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) // ODDDARG temp ordercallargs(&n.List, order) } @@ -447,8 +447,14 @@ func orderstmt(n *Node, order *Order) { case OVARKILL: order.out = list(order.out, n) - case OAS, - OAS2, + case OAS: + t := marktemp(order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, n.Left) + ordermapassign(n, order) + cleantemp(t, order) + + case OAS2, OCLOSE, OCOPY, OPRINT, @@ -456,29 +462,27 @@ func orderstmt(n *Node, order *Order) { ORECOVER, ORECV: t := marktemp(order) - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderexprlist(n.List, order) orderexprlist(n.Rlist, order) switch n.Op { - case OAS, OAS2, OAS2DOTTYPE: + case OAS2, OAS2DOTTYPE: ordermapassign(n, order) - default: order.out = list(order.out, n) } - cleantemp(t, order) - // Special: rewrite l op= r into l = l op r. - // This simplies quite a few operations; - // most important is that it lets us separate - // out map read from map write when l is - // a map index expression. case OASOP: + // Special: rewrite l op= r into l = l op r. + // This simplies quite a few operations; + // most important is that it lets us separate + // out map read from map write when l is + // a map index expression. t := marktemp(order) - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) n.Left = ordersafeexpr(n.Left, order) tmp1 := treecopy(n.Left) if tmp1.Op == OINDEXMAP { @@ -487,7 +491,7 @@ func orderstmt(n *Node, order *Order) { tmp1 = ordercopyexpr(tmp1, n.Left.Type, order, 0) n.Right = Nod(int(n.Etype), tmp1, n.Right) typecheck(&n.Right, Erv) - orderexpr(&n.Right, order) + orderexpr(&n.Right, order, nil) n.Etype = 0 n.Op = OAS ordermapassign(n, order) @@ -500,8 +504,8 @@ func orderstmt(n *Node, order *Order) { orderexprlist(n.List, order) r := n.Rlist.N - orderexpr(&r.Left, order) - orderexpr(&r.Right, order) + orderexpr(&r.Left, order, nil) + orderexpr(&r.Right, order, nil) // See case OINDEXMAP below. if r.Right.Op == OARRAYBYTESTR { @@ -527,7 +531,7 @@ func orderstmt(n *Node, order *Order) { t := marktemp(order) orderexprlist(n.List, order) - orderexpr(&n.Rlist.N.Left, order) // i in i.(T) + orderexpr(&n.Rlist.N.Left, order, nil) // i in i.(T) if isblank(n.List.N) { order.out = list(order.out, n) } else { @@ -548,7 +552,7 @@ func orderstmt(n *Node, order *Order) { t := marktemp(order) orderexprlist(n.List, order) - orderexpr(&n.Rlist.N.Left, order) // arg to recv + orderexpr(&n.Rlist.N.Left, order, nil) // arg to recv ch := n.Rlist.N.Left.Type tmp1 := ordertemp(ch.Type, order, haspointers(ch.Type)) var tmp2 *Node @@ -617,8 +621,8 @@ func orderstmt(n *Node, order *Order) { case ODELETE: t := marktemp(order) - orderexpr(&n.List.N, order) - orderexpr(&n.List.Next.N, order) + orderexpr(&n.List.N, order, nil) + orderexpr(&n.List.Next.N, order, nil) orderaddrtemp(&n.List.Next.N, order) // map key order.out = list(order.out, n) cleantemp(t, order) @@ -659,7 +663,7 @@ func orderstmt(n *Node, order *Order) { case OPANIC: t := marktemp(order) - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) if !Isinter(n.Left.Type) { orderaddrtemp(&n.Left, order) } @@ -677,7 +681,7 @@ func orderstmt(n *Node, order *Order) { case ORANGE: t := marktemp(order) - orderexpr(&n.Right, order) + orderexpr(&n.Right, order, nil) switch n.Type.Etype { default: Fatal("orderstmt range %v", n.Type) @@ -793,7 +797,7 @@ func orderstmt(n *Node, order *Order) { // r->left is x, r->ntest is ok, r->right is ORECV, r->right->left is c. // r->left == N means 'case <-c'. // c is always evaluated; x and ok are only evaluated when assigned. - orderexpr(&r.Right.Left, order) + orderexpr(&r.Right.Left, order, nil) if r.Right.Left.Op != ONAME { r.Right.Left = ordercopyexpr(r.Right.Left, r.Right.Left.Type, order, 0) @@ -853,12 +857,12 @@ func orderstmt(n *Node, order *Order) { // case c <- x // r->left is c, r->right is x, both are always evaluated. - orderexpr(&r.Left, order) + orderexpr(&r.Left, order, nil) if !istemp(r.Left) { r.Left = ordercopyexpr(r.Left, r.Left.Type, order, 0) } - orderexpr(&r.Right, order) + orderexpr(&r.Right, order, nil) if !istemp(r.Right) { r.Right = ordercopyexpr(r.Right, r.Right.Type, order, 0) } @@ -884,8 +888,8 @@ func orderstmt(n *Node, order *Order) { case OSEND: t := marktemp(order) - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderaddrtemp(&n.Right, order) order.out = list(order.out, n) cleantemp(t, order) @@ -900,7 +904,7 @@ func orderstmt(n *Node, order *Order) { case OSWITCH: t := marktemp(order) - orderexpr(&n.Ntest, order) + orderexpr(&n.Ntest, order, nil) for l := n.List; l != nil; l = l.Next { if l.N.Op != OXCASE { Fatal("order switch case %v", Oconv(int(l.N.Op), 0)) @@ -919,7 +923,7 @@ func orderstmt(n *Node, order *Order) { // Orderexprlist orders the expression list l into order. func orderexprlist(l *NodeList, order *Order) { for ; l != nil; l = l.Next { - orderexpr(&l.N, order) + orderexpr(&l.N, order, nil) } } @@ -933,7 +937,10 @@ func orderexprlistinplace(l *NodeList, order *Order) { // Orderexpr orders a single expression, appending side // effects to order->out as needed. -func orderexpr(np **Node, order *Order) { +// If this is part of an assignment lhs = *np, lhs is given. +// Otherwise lhs == nil. (When lhs != nil it may be possible +// to avoid copying the result of the expression to a temporary.) +func orderexpr(np **Node, order *Order, lhs *Node) { n := *np if n == nil { return @@ -944,8 +951,8 @@ func orderexpr(np **Node, order *Order) { switch n.Op { default: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderexprlist(n.List, order) orderexprlist(n.Rlist, order) @@ -986,8 +993,8 @@ func orderexpr(np **Node, order *Order) { } case OCMPSTR: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) // Mark string(byteSlice) arguments to reuse byteSlice backing // buffer during conversion. String comparison does not @@ -1001,9 +1008,9 @@ func orderexpr(np **Node, order *Order) { // key must be addressable case OINDEXMAP: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) - orderexpr(&n.Right, order) + orderexpr(&n.Right, order, nil) // For x = m[string(k)] where k is []byte, the allocation of // backing bytes for the string can be avoided by reusing @@ -1029,7 +1036,7 @@ func orderexpr(np **Node, order *Order) { // concrete type (not interface) argument must be addressable // temporary to pass to runtime. case OCONVIFACE: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) if !Isinter(n.Left.Type) { orderaddrtemp(&n.Left, order) @@ -1037,7 +1044,7 @@ func orderexpr(np **Node, order *Order) { case OANDAND, OOROR: mark := marktemp(order) - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) // Clean temporaries from first branch at beginning of second. // Leave them on the stack so that they can be killed in the outer @@ -1064,7 +1071,9 @@ func orderexpr(np **Node, order *Order) { OREAL, ORECOVER: ordercall(n, order) - n = ordercopyexpr(n, n.Type, order, 0) + if lhs == nil || lhs.Op != ONAME || flag_race != 0 { + n = ordercopyexpr(n, n.Type, order, 0) + } case OCLOSURE: if n.Noescape && n.Func.Cvars != nil { @@ -1072,8 +1081,8 @@ func orderexpr(np **Node, order *Order) { } case OARRAYLIT, OCALLPART: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) orderexprlist(n.List, order) orderexprlist(n.Rlist, order) if n.Noescape { @@ -1090,7 +1099,7 @@ func orderexpr(np **Node, order *Order) { } case ODOTTYPE, ODOTTYPE2: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) // TODO(rsc): The Isfat is for consistency with componentgen and walkexpr. // It needs to be removed in all three places. // That would allow inlining x.(struct{*int}) the same as x.(*int). @@ -1099,12 +1108,12 @@ func orderexpr(np **Node, order *Order) { } case ORECV: - orderexpr(&n.Left, order) + orderexpr(&n.Left, order, nil) n = ordercopyexpr(n, n.Type, order, 1) case OEQ, ONE: - orderexpr(&n.Left, order) - orderexpr(&n.Right, order) + orderexpr(&n.Left, order, nil) + orderexpr(&n.Right, order, nil) t := n.Left.Type if t.Etype == TSTRUCT || Isfixedarray(t) { // for complex comparisons, we need both args to be From f85a05581eadda1512a9bb5ae63098f3e1772f54 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 12 May 2015 18:20:04 +0200 Subject: [PATCH 049/232] runtime: fix signal handling on Plan 9 Once added to the signal queue, the pointer passed to the signal handler could no longer be valid. Instead of passing the pointer to the note string, we recopy the value of the note string to a static array in the signal queue. Fixes #10784. Change-Id: Iddd6837b58a14dfaa16b069308ae28a7b8e0965b Reviewed-on: https://go-review.googlesource.com/9950 Reviewed-by: Brad Fitzpatrick --- src/runtime/sigqueue_plan9.go | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/runtime/sigqueue_plan9.go b/src/runtime/sigqueue_plan9.go index 38f0a57b90..f000fabd1a 100644 --- a/src/runtime/sigqueue_plan9.go +++ b/src/runtime/sigqueue_plan9.go @@ -17,21 +17,29 @@ var sig struct { sleeping bool } +type noteData struct { + s [_ERRMAX]byte + n int // n bytes of s are valid +} + type noteQueue struct { lock mutex - data [qsize]*byte + data [qsize]noteData ri int wi int full bool } +// It is not allowed to allocate memory in the signal handler. func (q *noteQueue) push(item *byte) bool { lock(&q.lock) if q.full { unlock(&q.lock) return false } - q.data[q.wi] = item + s := gostringnocopy(item) + copy(q.data[q.wi].s[:], s) + q.data[q.wi].n = len(s) q.wi++ if q.wi == qsize { q.wi = 0 @@ -43,14 +51,15 @@ func (q *noteQueue) push(item *byte) bool { return true } -func (q *noteQueue) pop() *byte { +func (q *noteQueue) pop() string { lock(&q.lock) q.full = false if q.ri == q.wi { unlock(&q.lock) - return nil + return "" } - item := q.data[q.ri] + note := &q.data[q.ri] + item := string(note.s[:note.n]) q.ri++ if q.ri == qsize { q.ri = 0 @@ -86,8 +95,8 @@ func sendNote(s *byte) bool { func signal_recv() string { for { note := sig.q.pop() - if note != nil { - return gostring(note) + if note != "" { + return note } lock(&sig.lock) From 7de86a1b1c7f145cb574796dce6992fb12c91381 Mon Sep 17 00:00:00 2001 From: David du Colombier <0intro@gmail.com> Date: Tue, 12 May 2015 16:54:39 +0200 Subject: [PATCH 050/232] runtime: terminate exit status buffer on Plan 9 The status buffer built by the exit function was not nil-terminated. Fixes #10789. Change-Id: I2d34ac50a19d138176c4b47393497ba7070d5b61 Reviewed-on: https://go-review.googlesource.com/9953 Reviewed-by: Brad Fitzpatrick Run-TryBot: Brad Fitzpatrick --- src/runtime/os1_plan9.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/os1_plan9.go b/src/runtime/os1_plan9.go index c026218241..1aae96a999 100644 --- a/src/runtime/os1_plan9.go +++ b/src/runtime/os1_plan9.go @@ -177,7 +177,7 @@ func exit(e int) { } else { // build error string var tmp [32]byte - status = []byte(gostringnocopy(&itoa(tmp[:len(tmp)-1], uint64(e))[0])) + status = append(itoa(tmp[:len(tmp)-1], uint64(e)), 0) } goexitsall(&status[0]) exits(&status[0]) From 6439010e52610650f8aa048173832f94006ebdbd Mon Sep 17 00:00:00 2001 From: Rob Pike Date: Tue, 12 May 2015 10:29:53 -0700 Subject: [PATCH 051/232] encoding/gob: add "too big" check when writing a message Messages that are too big are rejected when read, so they should be rejected when written too. Fixes #10518. Change-Id: I96678fbe2d94f51b957fe26faef33cd8df3823dd Reviewed-on: https://go-review.googlesource.com/9965 Reviewed-by: Brad Fitzpatrick --- src/encoding/gob/encoder.go | 6 ++++++ src/encoding/gob/encoder_test.go | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/encoding/gob/encoder.go b/src/encoding/gob/encoder.go index a340e47b5e..62d0f42e81 100644 --- a/src/encoding/gob/encoder.go +++ b/src/encoding/gob/encoder.go @@ -5,6 +5,7 @@ package gob import ( + "errors" "io" "reflect" "sync" @@ -65,6 +66,11 @@ func (enc *Encoder) writeMessage(w io.Writer, b *encBuffer) { // it by hand. message := b.Bytes() messageLen := len(message) - maxLength + // Length cannot be bigger than the decoder can handle. + if messageLen >= tooBig { + enc.setError(errors.New("gob: encoder: message too big")) + return + } // Encode the length. enc.countState.b.Reset() enc.countState.encodeUint(uint64(messageLen)) diff --git a/src/encoding/gob/encoder_test.go b/src/encoding/gob/encoder_test.go index c0bd379c93..8a72a3118c 100644 --- a/src/encoding/gob/encoder_test.go +++ b/src/encoding/gob/encoder_test.go @@ -976,3 +976,21 @@ func TestBadData(t *testing.T) { } } } + +// TestHugeWriteFails tests that enormous messages trigger an error. +func TestHugeWriteFails(t *testing.T) { + if testing.Short() { + // Requires allocating a monster, so don't do this from all.bash. + t.Skip("skipping huge allocation in short mode") + } + huge := make([]byte, tooBig) + huge[0] = 7 // Make sure it's not all zeros. + buf := new(bytes.Buffer) + err := NewEncoder(buf).Encode(huge) + if err == nil { + t.Fatalf("expected error for huge slice") + } + if !strings.Contains(err.Error(), "message too big") { + t.Fatalf("expected 'too big' error; got %s\n", err.Error()) + } +} From f8d14fc3a00052727c717f62908a9db661483093 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:28:19 -0400 Subject: [PATCH 052/232] cmd/internal/gc: add backend ginscmp function to emit a comparison This lets us abstract away which arguments can be constants and so on and lets the back ends reverse the order of arguments if that helps. Change-Id: I283ec1d694f2dd84eba22e5eb4aad78a2d2d9eb0 Reviewed-on: https://go-review.googlesource.com/9810 Reviewed-by: David Chase --- src/cmd/5g/cgen.go | 22 ----------- src/cmd/5g/galign.go | 1 + src/cmd/5g/ggen.go | 18 +++++++++ src/cmd/6g/galign.go | 1 + src/cmd/6g/gsubr.go | 36 +++++++++++++++++ src/cmd/7g/galign.go | 1 + src/cmd/7g/gsubr.go | 28 +++++++++++++ src/cmd/8g/galign.go | 1 + src/cmd/8g/gsubr.go | 39 ++++++++++++++++++ src/cmd/9g/galign.go | 1 + src/cmd/9g/gsubr.go | 28 +++++++++++++ src/cmd/internal/gc/cgen.go | 79 ++++++------------------------------- src/cmd/internal/gc/gen.go | 12 ++---- src/cmd/internal/gc/go.go | 15 ++++++- 14 files changed, 183 insertions(+), 99 deletions(-) diff --git a/src/cmd/5g/cgen.go b/src/cmd/5g/cgen.go index 2e922391cb..c0d7651584 100644 --- a/src/cmd/5g/cgen.go +++ b/src/cmd/5g/cgen.go @@ -53,28 +53,6 @@ func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog { return cgenindex(n, res, bounded) } -func gencmp0(n *gc.Node, t *gc.Type, o int, likely int, to *obj.Prog) { - var n1 gc.Node - - gc.Regalloc(&n1, t, nil) - gc.Cgen(n, &n1) - a := optoas(gc.OCMP, t) - if a != arm.ACMP { - var n2 gc.Node - gc.Nodconst(&n2, t, 0) - var n3 gc.Node - gc.Regalloc(&n3, t, nil) - gmove(&n2, &n3) - gins(a, &n1, &n3) - gc.Regfree(&n3) - } else { - gins(arm.ATST, &n1, nil) - } - a = optoas(o, t) - gc.Patch(gc.Gbranch(a, t, likely), to) - gc.Regfree(&n1) -} - func blockcopy(n, res *gc.Node, osrc, odst, w int64) { // determine alignment. // want to avoid unaligned access, so have to use diff --git a/src/cmd/5g/galign.go b/src/cmd/5g/galign.go index 3c8ba519eb..55782e1dae 100644 --- a/src/cmd/5g/galign.go +++ b/src/cmd/5g/galign.go @@ -65,6 +65,7 @@ func main() { gc.Thearch.Expandchecks = expandchecks gc.Thearch.Getg = getg gc.Thearch.Gins = gins + gc.Thearch.Ginscmp = ginscmp gc.Thearch.Ginscon = ginscon gc.Thearch.Ginsnop = ginsnop gc.Thearch.Gmove = gmove diff --git a/src/cmd/5g/ggen.go b/src/cmd/5g/ggen.go index 0cf0d9299c..ade4bd6096 100644 --- a/src/cmd/5g/ggen.go +++ b/src/cmd/5g/ggen.go @@ -479,6 +479,24 @@ func ginscon(as int, c int64, n *gc.Node) { gc.Regfree(&n2) } +func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { + var r1, r2, g1, g2 gc.Node + gc.Regalloc(&r1, t, n1) + gc.Regalloc(&g1, n1.Type, &r1) + gc.Cgen(n1, &g1) + gmove(&g1, &r1) + gc.Regalloc(&r2, t, n2) + gc.Regalloc(&g2, n1.Type, &r2) + gc.Cgen(n2, &g2) + gmove(&g2, &r2) + gins(optoas(gc.OCMP, t), &r1, &r2) + gc.Regfree(&g1) + gc.Regfree(&r1) + gc.Regfree(&g2) + gc.Regfree(&r2) + return gc.Gbranch(optoas(op, t), nil, likely) +} + // addr += index*width if possible. func addindex(index *gc.Node, width int64, addr *gc.Node) bool { switch width { diff --git a/src/cmd/6g/galign.go b/src/cmd/6g/galign.go index 0ca87537ff..17d78f399d 100644 --- a/src/cmd/6g/galign.go +++ b/src/cmd/6g/galign.go @@ -101,6 +101,7 @@ func main() { gc.Thearch.Getg = getg gc.Thearch.Gins = gins gc.Thearch.Ginsboolval = ginsboolval + gc.Thearch.Ginscmp = ginscmp gc.Thearch.Ginscon = ginscon gc.Thearch.Ginsnop = ginsnop gc.Thearch.Gmove = gmove diff --git a/src/cmd/6g/gsubr.go b/src/cmd/6g/gsubr.go index 53d0f038d9..14e1a57cbd 100644 --- a/src/cmd/6g/gsubr.go +++ b/src/cmd/6g/gsubr.go @@ -99,6 +99,42 @@ func ginscon(as int, c int64, n2 *gc.Node) { gins(as, &n1, n2) } +func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { + if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && gc.Smallintconst(n1) && n2.Op != gc.OLITERAL { + // Reverse comparison to place constant last. + op = gc.Brrev(op) + n1, n2 = n2, n1 + } + // General case. + var r1, r2, g1, g2 gc.Node + if n1.Op == gc.ONAME && n1.Class&gc.PHEAP == 0 || n1.Op == gc.OINDREG { + r1 = *n1 + } else { + gc.Regalloc(&r1, t, n1) + gc.Regalloc(&g1, n1.Type, &r1) + gc.Cgen(n1, &g1) + gmove(&g1, &r1) + } + if n2.Op == gc.OLITERAL && gc.Isint[t.Etype] && gc.Smallintconst(n2) { + r2 = *n2 + } else { + gc.Regalloc(&r2, t, n2) + gc.Regalloc(&g2, n1.Type, &r2) + gc.Cgen(n2, &g2) + gmove(&g2, &r2) + } + gins(optoas(gc.OCMP, t), &r1, &r2) + if r1.Op == gc.OREGISTER { + gc.Regfree(&g1) + gc.Regfree(&r1) + } + if r2.Op == gc.OREGISTER { + gc.Regfree(&g2) + gc.Regfree(&r2) + } + return gc.Gbranch(optoas(op, t), nil, likely) +} + func ginsboolval(a int, n *gc.Node) { gins(jmptoset(a), nil, n) } diff --git a/src/cmd/7g/galign.go b/src/cmd/7g/galign.go index 34b4ab6142..8a6184efd0 100644 --- a/src/cmd/7g/galign.go +++ b/src/cmd/7g/galign.go @@ -65,6 +65,7 @@ func main() { gc.Thearch.Expandchecks = expandchecks gc.Thearch.Getg = getg gc.Thearch.Gins = gins + gc.Thearch.Ginscmp = ginscmp gc.Thearch.Ginscon = ginscon gc.Thearch.Ginsnop = ginsnop gc.Thearch.Gmove = gmove diff --git a/src/cmd/7g/gsubr.go b/src/cmd/7g/gsubr.go index a34a4306ae..60c3a7ad44 100644 --- a/src/cmd/7g/gsubr.go +++ b/src/cmd/7g/gsubr.go @@ -102,6 +102,34 @@ func ginscon2(as int, n2 *gc.Node, c int64) { gc.Regfree(&ntmp) } +func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { + if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL { + // Reverse comparison to place constant last. + op = gc.Brrev(op) + n1, n2 = n2, n1 + } + + var r1, r2, g1, g2 gc.Node + gc.Regalloc(&r1, t, n1) + gc.Regalloc(&g1, n1.Type, &r1) + gc.Cgen(n1, &g1) + gmove(&g1, &r1) + if gc.Isint[t.Etype] && gc.Isconst(n2, gc.CTINT) { + ginscon2(optoas(gc.OCMP, t), &r1, gc.Mpgetfix(n2.Val.U.Xval)) + } else { + gc.Regalloc(&r2, t, n2) + gc.Regalloc(&g2, n1.Type, &r2) + gc.Cgen(n2, &g2) + gmove(&g2, &r2) + gcmp(optoas(gc.OCMP, t), &r1, &r2) + gc.Regfree(&g2) + gc.Regfree(&r2) + } + gc.Regfree(&g1) + gc.Regfree(&r1) + return gc.Gbranch(optoas(op, t), nil, likely) +} + /* * generate move: * t = f diff --git a/src/cmd/8g/galign.go b/src/cmd/8g/galign.go index e96b628dcc..3651f509c9 100644 --- a/src/cmd/8g/galign.go +++ b/src/cmd/8g/galign.go @@ -81,6 +81,7 @@ func main() { gc.Thearch.Expandchecks = expandchecks gc.Thearch.Getg = getg gc.Thearch.Gins = gins + gc.Thearch.Ginscmp = ginscmp gc.Thearch.Ginscon = ginscon gc.Thearch.Ginsnop = ginsnop gc.Thearch.Gmove = gmove diff --git a/src/cmd/8g/gsubr.go b/src/cmd/8g/gsubr.go index 34ddfe0619..d1134d2c74 100644 --- a/src/cmd/8g/gsubr.go +++ b/src/cmd/8g/gsubr.go @@ -582,6 +582,45 @@ func ginscon(as int, c int64, n2 *gc.Node) { gins(as, &n1, n2) } +func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { + if gc.Isint[t.Etype] || int(t.Etype) == gc.Tptr { + if (n1.Op == gc.OLITERAL || n1.Op == gc.OADDR && n1.Left.Op == gc.ONAME) && n2.Op != gc.OLITERAL { + // Reverse comparison to place constant (including address constant) last. + op = gc.Brrev(op) + n1, n2 = n2, n1 + } + } + + // General case. + var r1, r2, g1, g2 gc.Node + if n1.Op == gc.ONAME && n1.Class&gc.PHEAP == 0 || n1.Op == gc.OINDREG { + r1 = *n1 + } else { + gc.Regalloc(&r1, t, n1) + gc.Regalloc(&g1, n1.Type, &r1) + gc.Cgen(n1, &g1) + gmove(&g1, &r1) + } + if n2.Op == gc.OLITERAL && gc.Isint[t.Etype] || n2.Op == gc.OADDR && n2.Left.Op == gc.ONAME && n2.Left.Class == gc.PEXTERN { + r2 = *n2 + } else { + gc.Regalloc(&r2, t, n2) + gc.Regalloc(&g2, n1.Type, &r2) + gc.Cgen(n2, &g2) + gmove(&g2, &r2) + } + gins(optoas(gc.OCMP, t), &r1, &r2) + if r1.Op == gc.OREGISTER { + gc.Regfree(&g1) + gc.Regfree(&r1) + } + if r2.Op == gc.OREGISTER { + gc.Regfree(&g2) + gc.Regfree(&r2) + } + return gc.Gbranch(optoas(op, t), nil, likely) +} + /* * swap node contents */ diff --git a/src/cmd/9g/galign.go b/src/cmd/9g/galign.go index a2f4a0ef89..6e1612007b 100644 --- a/src/cmd/9g/galign.go +++ b/src/cmd/9g/galign.go @@ -72,6 +72,7 @@ func main() { gc.Thearch.Expandchecks = expandchecks gc.Thearch.Getg = getg gc.Thearch.Gins = gins + gc.Thearch.Ginscmp = ginscmp gc.Thearch.Ginscon = ginscon gc.Thearch.Ginsnop = ginsnop gc.Thearch.Gmove = gmove diff --git a/src/cmd/9g/gsubr.go b/src/cmd/9g/gsubr.go index 8223fe70b1..61ba87ee3e 100644 --- a/src/cmd/9g/gsubr.go +++ b/src/cmd/9g/gsubr.go @@ -116,6 +116,34 @@ func ginscon2(as int, n2 *gc.Node, c int64) { gc.Regfree(&ntmp) } +func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { + if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && n2.Op != gc.OLITERAL { + // Reverse comparison to place constant last. + op = gc.Brrev(op) + n1, n2 = n2, n1 + } + + var r1, r2, g1, g2 gc.Node + gc.Regalloc(&r1, t, n1) + gc.Regalloc(&g1, n1.Type, &r1) + gc.Cgen(n1, &g1) + gmove(&g1, &r1) + if gc.Isint[t.Etype] && gc.Isconst(n2, gc.CTINT) { + ginscon2(optoas(gc.OCMP, t), &r1, gc.Mpgetfix(n2.Val.U.Xval)) + } else { + gc.Regalloc(&r2, t, n2) + gc.Regalloc(&g2, n1.Type, &r2) + gc.Cgen(n2, &g2) + gmove(&g2, &r2) + rawgins(optoas(gc.OCMP, t), &r1, &r2) + gc.Regfree(&g2) + gc.Regfree(&r2) + } + gc.Regfree(&g1) + gc.Regfree(&r1) + return gc.Gbranch(optoas(op, t), nil, likely) +} + /* * set up nodes representing 2^63 */ diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go index 6f8187d953..92a670d2fc 100644 --- a/src/cmd/internal/gc/cgen.go +++ b/src/cmd/internal/gc/cgen.go @@ -569,8 +569,7 @@ func cgen_wb(n, res *Node, wb bool) { var n2 Node Nodconst(&n2, Types[Tptr], 0) - Thearch.Gins(Thearch.Optoas(OCMP, Types[Tptr]), &n1, &n2) - p1 := Gbranch(Thearch.Optoas(OEQ, Types[Tptr]), nil, 0) + p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) n2 = n1 n2.Op = OINDREG @@ -610,8 +609,7 @@ func cgen_wb(n, res *Node, wb bool) { var n2 Node Nodconst(&n2, Types[Tptr], 0) - Thearch.Gins(Thearch.Optoas(OCMP, Types[Tptr]), &n1, &n2) - p1 := Gbranch(Thearch.Optoas(OEQ, Types[Tptr]), nil, 0) + p1 := Thearch.Ginscmp(OEQ, Types[Tptr], &n1, &n2, 0) n2 = n1 n2.Op = OINDREG @@ -804,19 +802,7 @@ func cgen_wbptr(n, res *Node) { } wbEnabled := syslook("writeBarrierEnabled", 0) - switch Ctxt.Arch.Thechar { - default: - Fatal("cgen_wbptr: unknown architecture") - case '5', '7', '9': - var tmp Node - Regalloc(&tmp, Types[TUINT8], nil) - Thearch.Gmove(wbEnabled, &tmp) - Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT8]), &tmp, Nodintconst(0)) - Regfree(&tmp) - case '6', '8': - Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT8]), wbEnabled, Nodintconst(0)) - } - pbr := Gbranch(Thearch.Optoas(ONE, Types[TUINT8]), nil, -1) + pbr := Thearch.Ginscmp(ONE, Types[TUINT8], wbEnabled, Nodintconst(0), -1) Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, &dst) pjmp := Gbranch(obj.AJMP, nil, 0) Patch(pbr, Pc) @@ -1055,13 +1041,7 @@ func Agenr(n *Node, a *Node, res *Node) { n1.Op = OINDREG n1.Type = Types[Tptr] n1.Xoffset = int64(Array_nel) - var n4 Node - Regalloc(&n4, n1.Type, nil) - Thearch.Gmove(&n1, &n4) - Nodconst(&n2, Types[TUINT32], int64(v)) - Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT32]), &n4, &n2) - Regfree(&n4) - p1 := Gbranch(Thearch.Optoas(OGT, Types[TUINT32]), nil, +1) + p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &n1, &n2, +1) Ginscall(Panicindex, -1) Patch(p1, Pc) } @@ -1099,12 +1079,10 @@ func Agenr(n *Node, a *Node, res *Node) { } else { Nodconst(&n4, Types[TUINT32], nl.Type.Bound) } - - Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT32]), &n2, &n4) + p1 := Thearch.Ginscmp(OLT, Types[TUINT32], &n2, &n4, +1) if n4.Op == OREGISTER { Regfree(&n4) } - p1 := Gbranch(Thearch.Optoas(OLT, Types[TUINT32]), nil, +1) if p2 != nil { Patch(p2, Pc) } @@ -1213,8 +1191,7 @@ func Agenr(n *Node, a *Node, res *Node) { nlen.Type = Types[TUINT32] nlen.Xoffset += int64(Array_nel) Nodconst(&n2, Types[TUINT32], int64(v)) - Thearch.Gins(Thearch.Optoas(OCMP, Types[TUINT32]), &nlen, &n2) - p1 := Gbranch(Thearch.Optoas(OGT, Types[TUINT32]), nil, +1) + p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &nlen, &n2, +1) Ginscall(Panicindex, -1) Patch(p1, Pc) } @@ -1261,8 +1238,7 @@ func Agenr(n *Node, a *Node, res *Node) { Nodconst(&nlen, t, nl.Type.Bound) } - Thearch.Gins(Thearch.Optoas(OCMP, t), &n2, &nlen) - p1 := Gbranch(Thearch.Optoas(OLT, t), nil, +1) + p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) if p2 != nil { Patch(p2, Pc) } @@ -1401,25 +1377,7 @@ func Agenr(n *Node, a *Node, res *Node) { v := uint64(Mpgetfix(nr.Val.U.Xval)) if Isslice(nl.Type) || nl.Type.Etype == TSTRING { if Debug['B'] == 0 && !n.Bounded { - if nlen.Op != OREGISTER && (Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9') { - var tmp2 Node - Regalloc(&tmp2, Types[Simtype[TUINT]], nil) - Thearch.Gmove(&nlen, &tmp2) - Regfree(&nlen) // in case it is OINDREG - nlen = tmp2 - } - var n2 Node - Nodconst(&n2, Types[Simtype[TUINT]], int64(v)) - if Smallintconst(nr) { - Thearch.Gins(Thearch.Optoas(OCMP, Types[Simtype[TUINT]]), &nlen, &n2) - } else { - Regalloc(&tmp, Types[Simtype[TUINT]], nil) - Thearch.Gmove(&n2, &tmp) - Thearch.Gins(Thearch.Optoas(OCMP, Types[Simtype[TUINT]]), &nlen, &tmp) - Regfree(&tmp) - } - - p1 := Gbranch(Thearch.Optoas(OGT, Types[Simtype[TUINT]]), nil, +1) + p1 := Thearch.Ginscmp(OGT, Types[Simtype[TUINT]], &nlen, Nodintconst(int64(v)), +1) Ginscall(Panicindex, -1) Patch(p1, Pc) } @@ -1456,26 +1414,12 @@ func Agenr(n *Node, a *Node, res *Node) { if Isconst(nl, CTSTR) { Nodconst(&nlen, t, int64(len(nl.Val.U.Sval))) } else if Isslice(nl.Type) || nl.Type.Etype == TSTRING { - if Is64(nr.Type) || Ctxt.Arch.Thechar == '7' || Ctxt.Arch.Thechar == '9' { - var n5 Node - Regalloc(&n5, t, nil) - Thearch.Gmove(&nlen, &n5) - Regfree(&nlen) - nlen = n5 - } + // nlen already initialized } else { Nodconst(&nlen, t, nl.Type.Bound) - if !Smallintconst(&nlen) { - var n5 Node - Regalloc(&n5, t, nil) - Thearch.Gmove(&nlen, &n5) - nlen = n5 - freelen = 1 - } } - Thearch.Gins(Thearch.Optoas(OCMP, t), &n2, &nlen) - p1 := Gbranch(Thearch.Optoas(OLT, t), nil, +1) + p1 := Thearch.Ginscmp(OLT, t, &n2, &nlen, +1) Ginscall(Panicindex, -1) Patch(p1, Pc) } @@ -2446,8 +2390,7 @@ func Ginscall(f *Node, proc int) { if proc == 2 { Nodreg(®, Types[TINT32], Thearch.REGRETURN) - Thearch.Gins(Thearch.Optoas(OCMP, Types[TINT32]), ®, Nodintconst(0)) - p := Gbranch(Thearch.Optoas(OEQ, Types[TINT32]), nil, +1) + p := Thearch.Ginscmp(OEQ, Types[TINT32], ®, Nodintconst(0), +1) cgen_ret(nil) Patch(p, Pc) } diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go index e6af897033..76e9a82392 100644 --- a/src/cmd/internal/gc/gen.go +++ b/src/cmd/internal/gc/gen.go @@ -428,8 +428,7 @@ func cgen_dottype(n *Node, res, resok *Node, wb bool) { Cgen(&iface, &r1) if !isnilinter(n.Left.Type) { // Holding itab, want concrete type in second word. - Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, Nodintconst(0)) - p := Gbranch(Thearch.Optoas(OEQ, byteptr), nil, -1) + p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) r2 = r1 r2.Op = OINDREG r2.Xoffset = int64(Widthptr) @@ -438,8 +437,7 @@ func cgen_dottype(n *Node, res, resok *Node, wb bool) { } Regalloc(&r2, byteptr, nil) Cgen(typename(n.Type), &r2) - Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, &r2) - p := Gbranch(Thearch.Optoas(ONE, byteptr), nil, -1) + p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) Regfree(&r2) // not needed for success path; reclaimed on one failure path iface.Xoffset += int64(Widthptr) Cgen(&iface, &r1) @@ -521,8 +519,7 @@ func Cgen_As2dottype(n, res, resok *Node) { Cgen(&iface, &r1) if !isnilinter(n.Left.Type) { // Holding itab, want concrete type in second word. - Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, Nodintconst(0)) - p := Gbranch(Thearch.Optoas(OEQ, byteptr), nil, -1) + p := Thearch.Ginscmp(OEQ, byteptr, &r1, Nodintconst(0), -1) r2 = r1 r2.Op = OINDREG r2.Xoffset = int64(Widthptr) @@ -531,8 +528,7 @@ func Cgen_As2dottype(n, res, resok *Node) { } Regalloc(&r2, byteptr, nil) Cgen(typename(n.Type), &r2) - Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, &r2) - p := Gbranch(Thearch.Optoas(ONE, byteptr), nil, -1) + p := Thearch.Ginscmp(ONE, byteptr, &r1, &r2, -1) iface.Type = n.Type iface.Xoffset += int64(Widthptr) Cgen(&iface, &r1) diff --git a/src/cmd/internal/gc/go.go b/src/cmd/internal/gc/go.go index 71bce0bf2c..404dcbb4ff 100644 --- a/src/cmd/internal/gc/go.go +++ b/src/cmd/internal/gc/go.go @@ -778,13 +778,26 @@ type Arch struct { Expandchecks func(*obj.Prog) Getg func(*Node) Gins func(int, *Node, *Node) *obj.Prog + + // Ginscmp generates code comparing n1 to n2 and jumping away if op is satisfied. + // The returned prog should be Patch'ed with the jump target. + // If op is not satisfied, code falls through to the next emitted instruction. + // Likely is the branch prediction hint: +1 for likely, -1 for unlikely, 0 for no opinion. + // + // Ginscmp must be able to handle all kinds of arguments for n1 and n2, + // not just simple registers, although it can assume that there are no + // function calls needed during the evaluation, so no in-memory temporaries + // are necessary. + Ginscmp func(op int, t *Type, n1, n2 *Node, likely int) *obj.Prog + // Ginsboolval inserts instructions to convert the result // of a just-completed comparison to a boolean value. // The first argument is the conditional jump instruction // corresponding to the desired value. // The second argument is the destination. // If not present, Ginsboolval will be emulated with jumps. - Ginsboolval func(int, *Node) + Ginsboolval func(int, *Node) + Ginscon func(int, int64, *Node) Ginsnop func() Gmove func(*Node, *Node) From 8552047a32cccccc1c376e49048c5a22494b7611 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Wed, 6 May 2015 12:34:30 -0400 Subject: [PATCH 053/232] cmd/internal/gc: optimize append + write barrier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code generated for x = append(x, v) is roughly: t := x if len(t)+1 > cap(t) { t = grow(t) } t[len(t)] = v len(t)++ x = t We used to generate this code as Go pseudocode during walk. Generate it instead as actual instructions during gen. Doing so lets us apply a few optimizations. The most important is that when, as in the above example, the source slice and the destination slice are the same, the code can instead do: t := x if len(t)+1 > cap(t) { t = grow(t) x = {base(t), len(t)+1, cap(t)} } else { len(x)++ } t[len(t)] = v That is, in the fast path that does not reallocate the array, only the updated length needs to be written back to x, not the array pointer and not the capacity. This is more like what you'd write by hand in C. It's faster in general, since the fast path elides two of the three stores, but it's especially faster when the form of x is such that the base pointer write would turn into a write barrier. No write, no barrier. name old mean new mean delta BinaryTree17 5.68s × (0.97,1.04) 5.81s × (0.98,1.03) +2.35% (p=0.023) Fannkuch11 4.41s × (0.98,1.03) 4.35s × (1.00,1.00) ~ (p=0.090) FmtFprintfEmpty 92.7ns × (0.91,1.16) 86.0ns × (0.94,1.11) -7.31% (p=0.038) FmtFprintfString 281ns × (0.96,1.08) 276ns × (0.98,1.04) ~ (p=0.219) FmtFprintfInt 288ns × (0.97,1.06) 274ns × (0.98,1.06) -4.94% (p=0.002) FmtFprintfIntInt 493ns × (0.97,1.04) 506ns × (0.99,1.01) +2.65% (p=0.009) FmtFprintfPrefixedInt 423ns × (0.97,1.04) 391ns × (0.99,1.01) -7.52% (p=0.000) FmtFprintfFloat 598ns × (0.99,1.01) 566ns × (0.99,1.01) -5.27% (p=0.000) FmtManyArgs 1.89µs × (0.98,1.05) 1.91µs × (0.99,1.01) ~ (p=0.231) GobDecode 14.8ms × (0.98,1.03) 15.3ms × (0.99,1.02) +3.01% (p=0.000) GobEncode 12.3ms × (0.98,1.01) 11.5ms × (0.97,1.03) -5.93% (p=0.000) Gzip 656ms × (0.99,1.05) 645ms × (0.99,1.01) ~ (p=0.055) Gunzip 142ms × (1.00,1.00) 142ms × (1.00,1.00) -0.32% (p=0.034) HTTPClientServer 91.2µs × (0.97,1.04) 90.5µs × (0.97,1.04) ~ (p=0.468) JSONEncode 32.6ms × (0.97,1.08) 32.0ms × (0.98,1.03) ~ (p=0.190) JSONDecode 114ms × (0.97,1.05) 114ms × (0.99,1.01) ~ (p=0.887) Mandelbrot200 6.11ms × (0.98,1.04) 6.04ms × (1.00,1.01) ~ (p=0.167) GoParse 6.66ms × (0.97,1.04) 6.47ms × (0.97,1.05) -2.81% (p=0.014) RegexpMatchEasy0_32 159ns × (0.99,1.00) 171ns × (0.93,1.07) +7.19% (p=0.002) RegexpMatchEasy0_1K 538ns × (1.00,1.01) 550ns × (0.98,1.01) +2.30% (p=0.000) RegexpMatchEasy1_32 138ns × (1.00,1.00) 135ns × (0.99,1.02) -1.60% (p=0.000) RegexpMatchEasy1_1K 869ns × (0.99,1.01) 879ns × (1.00,1.01) +1.08% (p=0.000) RegexpMatchMedium_32 252ns × (0.99,1.01) 243ns × (1.00,1.00) -3.71% (p=0.000) RegexpMatchMedium_1K 72.7µs × (1.00,1.00) 70.3µs × (1.00,1.00) -3.34% (p=0.000) RegexpMatchHard_32 3.85µs × (1.00,1.00) 3.82µs × (1.00,1.01) -0.81% (p=0.000) RegexpMatchHard_1K 118µs × (1.00,1.00) 117µs × (1.00,1.00) -0.56% (p=0.000) Revcomp 920ms × (0.97,1.07) 917ms × (0.97,1.04) ~ (p=0.808) Template 129ms × (0.98,1.03) 114ms × (0.99,1.01) -12.06% (p=0.000) TimeParse 619ns × (0.99,1.01) 622ns × (0.99,1.01) ~ (p=0.062) TimeFormat 661ns × (0.98,1.04) 665ns × (0.99,1.01) ~ (p=0.524) See next CL for combination with a similar optimization for slice. The benchmarks that are slower in this CL are still faster overall with the combination of the two. Change-Id: I2a7421658091b2488c64741b4db15ab6c3b4cb7e Reviewed-on: https://go-review.googlesource.com/9812 Reviewed-by: David Chase --- src/cmd/internal/gc/cgen.go | 183 +++++++++++++++++++++++++++++++++++ src/cmd/internal/gc/lex.go | 6 +- src/cmd/internal/gc/order.go | 10 +- src/cmd/internal/gc/walk.go | 56 ++++++++--- test/sliceopt.go | 22 +++++ test/writebarrier.go | 26 ++++- 6 files changed, 284 insertions(+), 19 deletions(-) create mode 100644 test/sliceopt.go diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go index 92a670d2fc..0c847c291c 100644 --- a/src/cmd/internal/gc/cgen.go +++ b/src/cmd/internal/gc/cgen.go @@ -67,6 +67,10 @@ func cgen_wb(n, res *Node, wb bool) { case ODOTTYPE: cgen_dottype(n, res, nil, wb) return + + case OAPPEND: + cgen_append(n, res) + return } if n.Ullman >= UINF { @@ -2786,3 +2790,182 @@ func Fixlargeoffset(n *Node) { n.Xoffset = 0 } } + +func cgen_append(n, res *Node) { + if Debug['g'] != 0 { + Dump("cgen_append-n", n) + Dump("cgen_append-res", res) + } + if res.Op != ONAME && !samesafeexpr(res, n.List.N) { + Dump("cgen_append-n", n) + Dump("cgen_append-res", res) + Fatal("append not lowered") + } + for l := n.List; l != nil; l = l.Next { + if l.N.Ullman >= UINF { + Fatal("append with function call arguments") + } + } + + // res = append(src, x, y, z) + // + // If res and src are the same, we can avoid writing to base and cap + // unless we grow the underlying array. + needFullUpdate := !samesafeexpr(res, n.List.N) + + // Copy src triple into base, len, cap. + base := temp(Types[Tptr]) + len := temp(Types[TUINT]) + cap := temp(Types[TUINT]) + + var src Node + Igen(n.List.N, &src, nil) + src.Type = Types[Tptr] + Thearch.Gmove(&src, base) + src.Type = Types[TUINT] + src.Xoffset += int64(Widthptr) + Thearch.Gmove(&src, len) + src.Xoffset += int64(Widthptr) + Thearch.Gmove(&src, cap) + + // if len+argc <= cap goto L1 + var rlen Node + Regalloc(&rlen, Types[TUINT], nil) + Thearch.Gmove(len, &rlen) + Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(count(n.List)-1), &rlen) + p := Thearch.Ginscmp(OLE, Types[TUINT], &rlen, cap, +1) + // Note: rlen and src are Regrealloc'ed below at the target of the + // branch we just emitted; do not reuse these Go variables for + // other purposes. They need to still describe the same things + // below that they describe right here. + Regfree(&src) + + // base, len, cap = growslice(type, base, len, cap, newlen) + var arg Node + arg.Op = OINDREG + arg.Reg = int16(Thearch.REGSP) + arg.Addable = true + arg.Xoffset = 0 + if HasLinkRegister() { + arg.Xoffset = int64(Ctxt.Arch.Ptrsize) + } + arg.Type = Ptrto(Types[TUINT8]) + Cgen(typename(res.Type), &arg) + arg.Xoffset += int64(Widthptr) + + arg.Type = Types[Tptr] + Cgen(base, &arg) + arg.Xoffset += int64(Widthptr) + + arg.Type = Types[TUINT] + Cgen(len, &arg) + arg.Xoffset += int64(Widthptr) + + arg.Type = Types[TUINT] + Cgen(cap, &arg) + arg.Xoffset += int64(Widthptr) + + arg.Type = Types[TUINT] + Cgen(&rlen, &arg) + arg.Xoffset += int64(Widthptr) + Regfree(&rlen) + + fn := syslook("growslice", 1) + substArgTypes(fn, res.Type.Type, res.Type.Type) + Ginscall(fn, 0) + + if Widthptr == 4 && Widthreg == 8 { + arg.Xoffset += 4 + } + + arg.Type = Types[Tptr] + Cgen(&arg, base) + arg.Xoffset += int64(Widthptr) + + arg.Type = Types[TUINT] + Cgen(&arg, len) + arg.Xoffset += int64(Widthptr) + + arg.Type = Types[TUINT] + Cgen(&arg, cap) + + // Update res with base, len+argc, cap. + if needFullUpdate { + if Debug_append > 0 { + Warn("append: full update") + } + Patch(p, Pc) + } + if res.Op == ONAME { + Gvardef(res) + } + var dst, r1 Node + Igen(res, &dst, nil) + dst.Type = Types[TUINT] + dst.Xoffset += int64(Widthptr) + Regalloc(&r1, Types[TUINT], nil) + Thearch.Gmove(len, &r1) + Thearch.Ginscon(Thearch.Optoas(OADD, Types[TUINT]), int64(count(n.List)-1), &r1) + Thearch.Gmove(&r1, &dst) + Regfree(&r1) + dst.Xoffset += int64(Widthptr) + Thearch.Gmove(cap, &dst) + dst.Type = Types[Tptr] + dst.Xoffset -= 2 * int64(Widthptr) + cgen_wb(base, &dst, needwritebarrier(&dst, base)) + Regfree(&dst) + + if !needFullUpdate { + if Debug_append > 0 { + Warn("append: len-only update") + } + // goto L2; + // L1: + // update len only + // L2: + q := Gbranch(obj.AJMP, nil, 0) + Patch(p, Pc) + // At the goto above, src refers to cap and rlen holds the new len + if src.Op == OREGISTER || src.Op == OINDREG { + Regrealloc(&src) + } + Regrealloc(&rlen) + src.Xoffset -= int64(Widthptr) + Thearch.Gmove(&rlen, &src) + Regfree(&src) + Regfree(&rlen) + Patch(q, Pc) + } + + // Copy data into place. + // Could do write barrier check around entire copy instead of each element. + // Could avoid reloading registers on each iteration if we know the cgen_wb + // is not going to use a write barrier. + i := 0 + var r2 Node + for l := n.List.Next; l != nil; l = l.Next { + Regalloc(&r1, Types[Tptr], nil) + Thearch.Gmove(base, &r1) + Regalloc(&r2, Types[TUINT], nil) + Thearch.Gmove(len, &r2) + if i > 0 { + Thearch.Gins(Thearch.Optoas(OADD, Types[TUINT]), Nodintconst(int64(i)), &r2) + } + w := res.Type.Type.Width + if Thearch.AddIndex != nil && Thearch.AddIndex(&r2, w, &r1) { + // r1 updated by back end + } else if w == 1 { + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1) + } else { + Thearch.Ginscon(Thearch.Optoas(OMUL, Types[TUINT]), int64(w), &r2) + Thearch.Gins(Thearch.Optoas(OADD, Types[Tptr]), &r2, &r1) + } + Regfree(&r2) + + r1.Op = OINDREG + r1.Type = res.Type.Type + cgen_wb(l.N, &r1, needwritebarrier(&r1, l.N)) + Regfree(&r1) + i++ + } +} diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/internal/gc/lex.go index 4bbda957a5..be95138b6a 100644 --- a/src/cmd/internal/gc/lex.go +++ b/src/cmd/internal/gc/lex.go @@ -35,7 +35,10 @@ var goarch string var goroot string -var Debug_wb int +var ( + Debug_wb int + Debug_append int +) // Debug arguments. // These can be specified with the -d flag, as in "-d nil" @@ -49,6 +52,7 @@ var debugtab = []struct { {"typeassert", &Debug_typeassert}, // print information about type assertion inlining {"disablenil", &Disable_checknil}, // disable nil checks {"wb", &Debug_wb}, // print information about write barriers + {"append", &Debug_append}, // print information about append compilation } // Our own isdigit, isspace, isalpha, isalnum that take care diff --git a/src/cmd/internal/gc/order.go b/src/cmd/internal/gc/order.go index 82876f81bc..5de2aa391c 100644 --- a/src/cmd/internal/gc/order.go +++ b/src/cmd/internal/gc/order.go @@ -1055,8 +1055,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) { n.Right.Ninit = concat(l, n.Right.Ninit) orderexprinplace(&n.Right, order) - case OAPPEND, - OCALLFUNC, + case OCALLFUNC, OCALLINTER, OCALLMETH, OCAP, @@ -1075,6 +1074,12 @@ func orderexpr(np **Node, order *Order, lhs *Node) { n = ordercopyexpr(n, n.Type, order, 0) } + case OAPPEND: + ordercallargs(&n.List, order) + if lhs == nil || flag_race != 0 || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.N) { + n = ordercopyexpr(n, n.Type, order, 0) + } + case OCLOSURE: if n.Noescape && n.Func.Cvars != nil { n.Alloc = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type @@ -1119,7 +1124,6 @@ func orderexpr(np **Node, order *Order, lhs *Node) { // for complex comparisons, we need both args to be // addressable so we can pass them to the runtime. orderaddrtemp(&n.Left, order) - orderaddrtemp(&n.Right, order) } } diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go index c8a5c7e2f3..bef08ae252 100644 --- a/src/cmd/internal/gc/walk.go +++ b/src/cmd/internal/gc/walk.go @@ -711,6 +711,23 @@ func walkexpr(np **Node, init **NodeList) { n = mkcall1(chanfn("chanrecv1", 2, r.Type), nil, init, typename(r.Type), r, n1) walkexpr(&n, init) goto ret + + case OAPPEND: + // x = append(...) + r := n.Right + if r.Isddd { + r = appendslice(r, init) // also works for append(slice, string). + } else { + r = walkappend(r, init, n) + } + n.Right = r + if r.Op == OAPPEND { + // Left in place for back end. + // Do not add a new write barrier. + goto ret + } + // Otherwise, lowered for race detector. + // Treat as ordinary assignment. } if n.Left != nil && n.Right != nil { @@ -1400,12 +1417,8 @@ func walkexpr(np **Node, init **NodeList) { goto ret case OAPPEND: - if n.Isddd { - n = appendslice(n, init) // also works for append(slice, string). - } else { - n = walkappend(n, init) - } - goto ret + // order should make sure we only see OAS(node, OAPPEND), which we handle above. + Fatal("append outside assignment") case OCOPY: n = copyany(n, init, flag_race) @@ -2108,9 +2121,8 @@ func isstack(n *Node) bool { } switch n.Op { - // OINDREG only ends up in walk if it's indirect of SP. case OINDREG: - return true + return n.Reg == int16(Thearch.REGSP) case ONAME: switch n.Class { @@ -3006,7 +3018,13 @@ func appendslice(n *Node, init **NodeList) *Node { return s } -// expand append(src, a [, b]* ) to +// Rewrite append(src, x, y, z) so that any side effects in +// x, y, z (including runtime panics) are evaluated in +// initialization statements before the append. +// For normal code generation, stop there and leave the +// rest to cgen_append. +// +// For race detector, expand append(src, a [, b]* ) to // // init { // s := src @@ -3021,13 +3039,21 @@ func appendslice(n *Node, init **NodeList) *Node { // ... // } // s -func walkappend(n *Node, init **NodeList) *Node { - walkexprlistsafe(n.List, init) +func walkappend(n *Node, init **NodeList, dst *Node) *Node { + if !samesafeexpr(dst, n.List.N) { + l := n.List + l.N = safeexpr(l.N, init) + walkexpr(&l.N, init) + } + walkexprlistsafe(n.List.Next, init) // walkexprlistsafe will leave OINDEX (s[n]) alone if both s // and n are name or literal, but those may index the slice we're // modifying here. Fix explicitly. - for l := n.List; l != nil; l = l.Next { + // Using cheapexpr also makes sure that the evaluation + // of all arguments (and especially any panics) happen + // before we begin to modify the slice in a visible way. + for l := n.List.Next; l != nil; l = l.Next { l.N = cheapexpr(l.N, init) } @@ -3042,6 +3068,12 @@ func walkappend(n *Node, init **NodeList) *Node { return nsrc } + // General case, with no function calls left as arguments. + // Leave for gen, except that race detector requires old form + if flag_race == 0 { + return n + } + var l *NodeList ns := temp(nsrc.Type) diff --git a/test/sliceopt.go b/test/sliceopt.go new file mode 100644 index 0000000000..dc30717ebf --- /dev/null +++ b/test/sliceopt.go @@ -0,0 +1,22 @@ +// errorcheck -0 -d=append + +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Check optimization results for append. + +package main + +func a1(x []int, y int) []int { + x = append(x, y) // ERROR "append: len-only update" + return x +} + +func a2(x []int, y int) []int { + return append(x, y) // ERROR "append: full update" +} + +func a3(x *[]int, y int) { + *x = append(*x, y) // ERROR "append: len-only update" +} diff --git a/test/writebarrier.go b/test/writebarrier.go index 1f25d91ea4..b24af9a14d 100644 --- a/test/writebarrier.go +++ b/test/writebarrier.go @@ -28,7 +28,7 @@ func f1a(x *[]byte, y *[]byte) { *x = *y // ERROR "write barrier" z := *y // no barrier - *x = z // ERROR "write barrier" + *x = z // ERROR "write barrier" } func f2(x *interface{}, y interface{}) { @@ -56,7 +56,7 @@ func f3a(x *string, y *string) { *x = *y // ERROR "write barrier" z := *y // no barrier - *x = z // ERROR "write barrier" + *x = z // ERROR "write barrier" } func f4(x *[2]string, y [2]string) { @@ -70,7 +70,7 @@ func f4a(x *[2]string, y *[2]string) { *x = *y // ERROR "write barrier" z := *y // no barrier - *x = z // ERROR "write barrier" + *x = z // ERROR "write barrier" } type T struct { @@ -108,3 +108,23 @@ func f10(x *byte, f func(interface{})) { func f11(x *unsafe.Pointer, y unsafe.Pointer) { *x = unsafe.Pointer(uintptr(y) + 1) // ERROR "write barrier" } + +func f12(x []*int, y *int) []*int { + // write barrier for storing y in x's underlying array + x = append(x, y) // ERROR "write barrier" + return x +} + +func f12a(x []int, y int) []int { + // y not a pointer, so no write barriers in this function + x = append(x, y) + return x +} + +func f13(x []int, y *[]int) { + *y = append(x, 1) // ERROR "write barrier" +} + +func f14(y *[]int) { + *y = append(*y, 1) // ERROR "write barrier" +} From 9b379d7e04750bbf6615cdfc1783db53c3d9bdc9 Mon Sep 17 00:00:00 2001 From: Andrew Williams Date: Sun, 25 Jan 2015 12:53:34 -0600 Subject: [PATCH 054/232] syscall: relocate linux death signal code Fix bug on Linux SysProcAttr handling: setting both Pdeathsig and Credential caused Pdeathsig to be ignored. This is because the kernel clears the deathsignal field when performing a setuid/setgid system call. Avoid this by moving Pdeathsig handling after Credential handling. Fixes #9686 Change-Id: Id01896ad4e979b8c448e0061f00aa8762ca0ac94 Reviewed-on: https://go-review.googlesource.com/3290 Reviewed-by: Ian Lance Taylor Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/syscall/exec_linux.go | 40 ++++----- src/syscall/syscall_linux_test.go | 140 ++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 20 deletions(-) create mode 100644 src/syscall/syscall_linux_test.go diff --git a/src/syscall/exec_linux.go b/src/syscall/exec_linux.go index ced2ca862d..3aa30c7364 100644 --- a/src/syscall/exec_linux.go +++ b/src/syscall/exec_linux.go @@ -132,26 +132,6 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } - // Parent death signal - if sys.Pdeathsig != 0 { - _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0) - if err1 != 0 { - goto childerror - } - - // Signal self if parent is already dead. This might cause a - // duplicate signal in rare cases, but it won't matter when - // using SIGKILL. - r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0) - if r1 != ppid { - pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) - _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0) - if err1 != 0 { - goto childerror - } - } - } - // Enable tracing if requested. if sys.Ptrace { _, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0) @@ -232,6 +212,26 @@ func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr } } + // Parent death signal + if sys.Pdeathsig != 0 { + _, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0) + if err1 != 0 { + goto childerror + } + + // Signal self if parent is already dead. This might cause a + // duplicate signal in rare cases, but it won't matter when + // using SIGKILL. + r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0) + if r1 != ppid { + pid, _, _ := RawSyscall(SYS_GETPID, 0, 0, 0) + _, _, err1 := RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0) + if err1 != 0 { + goto childerror + } + } + } + // Pass 1: look for fd[i] < i and move those up above len(fd) // so that pass 2 won't stomp on an fd it needs later. if pipe < nextfd { diff --git a/src/syscall/syscall_linux_test.go b/src/syscall/syscall_linux_test.go new file mode 100644 index 0000000000..40fce6d68c --- /dev/null +++ b/src/syscall/syscall_linux_test.go @@ -0,0 +1,140 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package syscall_test + +import ( + "bufio" + "fmt" + "io" + "io/ioutil" + "os" + "os/exec" + "os/signal" + "path/filepath" + "syscall" + "testing" + "time" +) + +func TestMain(m *testing.M) { + if os.Getenv("GO_DEATHSIG_PARENT") == "1" { + deathSignalParent() + } else if os.Getenv("GO_DEATHSIG_CHILD") == "1" { + deathSignalChild() + } + + os.Exit(m.Run()) +} + +func TestLinuxDeathSignal(t *testing.T) { + if os.Getuid() != 0 { + t.Skip("skipping root only test") + } + + // Copy the test binary to a location that a non-root user can read/execute + // after we drop privileges + tempDir, err := ioutil.TempDir("", "TestDeathSignal") + if err != nil { + t.Fatalf("cannot create temporary directory: %v", err) + } + defer os.RemoveAll(tempDir) + os.Chmod(tempDir, 0755) + + tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0])) + + src, err := os.Open(os.Args[0]) + if err != nil { + t.Fatalf("cannot open binary %q, %v", os.Args[0], err) + } + defer src.Close() + + dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) + if err != nil { + t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err) + } + if _, err := io.Copy(dst, src); err != nil { + t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err) + } + err = dst.Close() + if err != nil { + t.Fatalf("failed to close test binary %q, %v", tmpBinary, err) + } + + cmd := exec.Command(tmpBinary) + cmd.Env = []string{"GO_DEATHSIG_PARENT=1"} + chldStdin, err := cmd.StdinPipe() + if err != nil { + t.Fatal("failed to create new stdin pipe: %v", err) + } + chldStdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatal("failed to create new stdout pipe: %v", err) + } + cmd.Stderr = os.Stderr + + err = cmd.Start() + defer cmd.Wait() + if err != nil { + t.Fatalf("failed to start first child process: %v", err) + } + + chldPipe := bufio.NewReader(chldStdout) + + if got, err := chldPipe.ReadString('\n'); got == "start\n" { + syscall.Kill(cmd.Process.Pid, syscall.SIGTERM) + + go func() { + time.Sleep(5 * time.Second) + chldStdin.Close() + }() + + want := "ok\n" + if got, err = chldPipe.ReadString('\n'); got != want { + t.Fatalf("expected %q, received %q, %v", want, got, err) + } + } else { + t.Fatalf("did not receive start from child, received %q, %v", got, err) + } +} + +func deathSignalParent() { + cmd := exec.Command(os.Args[0]) + cmd.Env = []string{"GO_DEATHSIG_CHILD=1"} + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + attrs := syscall.SysProcAttr{ + Pdeathsig: syscall.SIGUSR1, + // UID/GID 99 is the user/group "nobody" on RHEL/Fedora and is + // unused on Ubuntu + Credential: &syscall.Credential{Uid: 99, Gid: 99}, + } + cmd.SysProcAttr = &attrs + + err := cmd.Start() + if err != nil { + fmt.Fprintf(os.Stderr, "death signal parent error: %v\n") + os.Exit(1) + } + cmd.Wait() + os.Exit(0) +} + +func deathSignalChild() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGUSR1) + go func() { + <-c + fmt.Println("ok") + os.Exit(0) + }() + fmt.Println("start") + + buf := make([]byte, 32) + os.Stdin.Read(buf) + + // We expected to be signaled before stdin closed + fmt.Println("not ok") + os.Exit(1) +} From 5ed4bb6db111d31a2c8625fe122de0ed7f1a9cbd Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 12 May 2015 15:51:22 -0400 Subject: [PATCH 055/232] cmd/5g: fix build The line in cgen.go was lost during the ginscmp CL. The ggen.go change is not strictly necessary, but it makes the 5g -S output for x[0] match what it said before the ginscmp CL. Change-Id: I5890a9ec1ac69a38509416eda5aea13b8b12b94a Reviewed-on: https://go-review.googlesource.com/9929 Reviewed-by: Russ Cox --- src/cmd/5g/ggen.go | 22 +++++++++++++++------- src/cmd/internal/gc/cgen.go | 1 + 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/cmd/5g/ggen.go b/src/cmd/5g/ggen.go index ade4bd6096..c2bd6dda0a 100644 --- a/src/cmd/5g/ggen.go +++ b/src/cmd/5g/ggen.go @@ -480,20 +480,28 @@ func ginscon(as int, c int64, n *gc.Node) { } func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { + if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && gc.Mpgetfix(n1.Val.U.Xval) == 0 && n2.Op != gc.OLITERAL { + op = gc.Brrev(op) + n1, n2 = n2, n1 + } var r1, r2, g1, g2 gc.Node gc.Regalloc(&r1, t, n1) gc.Regalloc(&g1, n1.Type, &r1) gc.Cgen(n1, &g1) gmove(&g1, &r1) - gc.Regalloc(&r2, t, n2) - gc.Regalloc(&g2, n1.Type, &r2) - gc.Cgen(n2, &g2) - gmove(&g2, &r2) - gins(optoas(gc.OCMP, t), &r1, &r2) + if gc.Isint[t.Etype] && n2.Op == gc.OLITERAL && gc.Mpgetfix(n2.Val.U.Xval) == 0 { + gins(arm.ACMP, &r1, n2) + } else { + gc.Regalloc(&r2, t, n2) + gc.Regalloc(&g2, n1.Type, &r2) + gc.Cgen(n2, &g2) + gmove(&g2, &r2) + gins(optoas(gc.OCMP, t), &r1, &r2) + gc.Regfree(&g2) + gc.Regfree(&r2) + } gc.Regfree(&g1) gc.Regfree(&r1) - gc.Regfree(&g2) - gc.Regfree(&r2) return gc.Gbranch(optoas(op, t), nil, likely) } diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go index 0c847c291c..3763a367b0 100644 --- a/src/cmd/internal/gc/cgen.go +++ b/src/cmd/internal/gc/cgen.go @@ -1045,6 +1045,7 @@ func Agenr(n *Node, a *Node, res *Node) { n1.Op = OINDREG n1.Type = Types[Tptr] n1.Xoffset = int64(Array_nel) + Nodconst(&n2, Types[TUINT32], int64(v)) p1 := Thearch.Ginscmp(OGT, Types[TUINT32], &n1, &n2, +1) Ginscall(Panicindex, -1) Patch(p1, Pc) From 350fd548b3313cfe47cf3c02cd01cfccb931211d Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Mon, 11 May 2015 18:53:49 -0400 Subject: [PATCH 056/232] runtime: don't run runq tests on the system stack Running these tests on the system stack is problematic because they allocate Ps, which are large enough to overflow the system stack if they are stack-allocated. It used to be necessary to run these tests on the system stack because they were written in C, but since this is no longer the case, we can fix this problem by simply not running the tests on the system stack. This also means we no longer need the hack in one of these tests that forces the allocated Ps to escape to the heap, so eliminate that as well. Change-Id: I9064f5f8fd7f7b446ff39a22a70b172cfcb2dc57 Reviewed-on: https://go-review.googlesource.com/9923 Reviewed-by: Rick Hudson Run-TryBot: Austin Clements --- src/runtime/export_test.go | 4 ++-- src/runtime/proc1.go | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go index 817622abd0..2f8df78e13 100644 --- a/src/runtime/export_test.go +++ b/src/runtime/export_test.go @@ -83,10 +83,10 @@ func GCMask(x interface{}) (ret []byte) { } func RunSchedLocalQueueTest() { - systemstack(testSchedLocalQueue) + testSchedLocalQueue() } func RunSchedLocalQueueStealTest() { - systemstack(testSchedLocalQueueSteal) + testSchedLocalQueueSteal() } var StringHash = stringHash diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go index 01c46a85ec..2fe1551952 100644 --- a/src/runtime/proc1.go +++ b/src/runtime/proc1.go @@ -3539,13 +3539,9 @@ func testSchedLocalQueue() { } } -var pSink *p - func testSchedLocalQueueSteal() { p1 := new(p) p2 := new(p) - pSink = p1 // Force to heap, too large to allocate on system stack ("G0 stack") - pSink = p2 // Force to heap, too large to allocate on system stack ("G0 stack") gs := make([]g, len(p1.runq)) for i := 0; i < len(p1.runq); i++ { for j := 0; j < i; j++ { From 1828d03ad56e2e57ae1595728ec96c28b98dfd50 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 12 May 2015 14:24:26 -0700 Subject: [PATCH 057/232] syscall: mksysnum_linux.pl: run syscall numbers through GCC This will skip system call numbers that are ifdef'ed out in unistd.h, as occurs on PPC. Change-Id: I88e640e4621c7a8cc266433f34a7b4be71543ec9 Reviewed-on: https://go-review.googlesource.com/9966 Reviewed-by: Minux Ma --- src/syscall/mkall.sh | 2 +- src/syscall/mksysnum_linux.pl | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/syscall/mkall.sh b/src/syscall/mkall.sh index 739663ed9c..85fab4ff3e 100755 --- a/src/syscall/mkall.sh +++ b/src/syscall/mkall.sh @@ -179,7 +179,7 @@ linux_amd64) linux_arm) mkerrors="$mkerrors" mksyscall="./mksyscall.pl -l32 -arm" - mksysnum="curl -s 'http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/arch/arm/include/uapi/asm/unistd.h' | ./mksysnum_linux.pl" + mksysnum="curl -s 'http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/arch/arm/include/uapi/asm/unistd.h' | ./mksysnum_linux.pl -" mktypes="GOARCH=$GOARCH go tool cgo -godefs" ;; linux_arm64) diff --git a/src/syscall/mksysnum_linux.pl b/src/syscall/mksysnum_linux.pl index 7a8add8bab..b6fbcb599b 100755 --- a/src/syscall/mksysnum_linux.pl +++ b/src/syscall/mksysnum_linux.pl @@ -28,7 +28,8 @@ sub fmt { } my $prev; -while(<>){ +open(GCC, "gcc -E -dD $ARGV[0] |") || die "can't run gcc"; +while(){ if(/^#define __NR_syscalls\s+/) { # ignore redefinitions of __NR_syscalls } From c06b8565558d9060aad39442270e9fd48f2448b6 Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Tue, 12 May 2015 19:19:29 -0400 Subject: [PATCH 058/232] testing: fix typo Fixes #10794. Change-Id: Id91485394ddbadc28c800e1d0c3ec281ba6cd098 Reviewed-on: https://go-review.googlesource.com/9990 Reviewed-by: Brad Fitzpatrick --- src/testing/testing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/testing/testing.go b/src/testing/testing.go index 35ab82d421..2a1c45f768 100644 --- a/src/testing/testing.go +++ b/src/testing/testing.go @@ -44,7 +44,7 @@ // } // // The benchmark function must run the target code b.N times. -// During benchark execution, b.N is adjusted until the benchmark function lasts +// During benchmark execution, b.N is adjusted until the benchmark function lasts // long enough to be timed reliably. The output // BenchmarkHello 10000000 282 ns/op // means that the loop ran 10000000 times at a speed of 282 ns per loop. From a4f4a46c28fdf6a2b0724bd779c9cf1bad32b066 Mon Sep 17 00:00:00 2001 From: "Hyang-Ah (Hana) Kim" Date: Tue, 12 May 2015 16:47:40 -0400 Subject: [PATCH 059/232] misc/cgo/testcshared: fix test for android. On android the generated header files are located in pkg/$(go env GOOS)_$(go env GOARCH)_testcshared. The test was broken since https://go-review.googlesource.com/9798. The installation path differs based on codegenArgs (around src/cmd/go/build.go line 389), and the codegenArgs is platform dependent. Change-Id: I01ae9cb957fb7676e399f3b8c067f24c5bd20b9d Reviewed-on: https://go-review.googlesource.com/9980 Reviewed-by: Ian Lance Taylor --- misc/cgo/testcshared/test.bash | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/misc/cgo/testcshared/test.bash b/misc/cgo/testcshared/test.bash index 9862a37993..ed437577c7 100755 --- a/misc/cgo/testcshared/test.bash +++ b/misc/cgo/testcshared/test.bash @@ -15,6 +15,14 @@ if [ ! -f src/libgo/libgo.go ]; then fi goos=$(go env GOOS) +goarch=$(go env GOARCH) + +# Directory where cgo headers and outputs will be installed. +# The installation directory format varies depending on the platform. +installdir=pkg/${goos}_${goarch}_testcshared_shared +if [ "${goos}/${goarch}" == "android/arm" ]; then + installdir=pkg/${goos}_${goarch}_testcshared +fi # Temporary directory on the android device. androidpath=/data/local/tmp/testcshared-$$ @@ -22,9 +30,9 @@ androidpath=/data/local/tmp/testcshared-$$ function cleanup() { rm -rf libgo.so libgo2.so libgo.h testp testp2 testp3 pkg - rm -rf $(go env GOROOT)/pkg/$(go env GOOS)_$(go env GOARCH)_testcshared_shared + rm -rf $(go env GOROOT)/${installdir} - if [ "$(go env GOOS)" == "android" ]; then + if [ "$goos" == "android" ]; then adb shell rm -rf $androidpath fi } @@ -38,11 +46,8 @@ function run() { case "$goos" in "android") local args=$@ - for ((i=0; i < ${#args}; i++)); do - args[$i]=${args[$i]//.\//${androidpath}\/} - args[$i]=${args[$i]//=./=${androidpath}} - done - output=$(adb shell ${args} | tr -d '\r') + output=$(adb shell "cd ${androidpath}; env $@") + output=$(echo $output|tr -d '\r') case $output in *PASS) echo "PASS";; *) echo "$output";; @@ -73,8 +78,9 @@ binpush libgo.so # test0: exported symbols in shared lib are accessible. # TODO(iant): using _shared here shouldn't really be necessary. -$(go env CC) $(go env GOGCCFLAGS) -I pkg/$(go env GOOS)_$(go env GOARCH)_testcshared_shared -o testp main0.c libgo.so +$(go env CC) $(go env GOGCCFLAGS) -I ${installdir} -o testp main0.c libgo.so binpush testp + output=$(run LD_LIBRARY_PATH=. ./testp) if [ "$output" != "PASS" ]; then echo "FAIL test0 got ${output}" From 08ba7dbdfdf0a2824ee122a6214e0263431a6ff0 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 11 May 2015 20:49:32 -0700 Subject: [PATCH 060/232] syscall: mkerrors.sh: don't define _FILE_OFFSET_BITS if __LP64__ If __LP64__ is defined then the type "long" is 64-bits, and there is no need to explicitly request _FILE_OFFSET_BITS == 64. This changes the definitions of F_GETLK, F_SETLK, and F_SETLKW on PPC to the values that the kernel requires. The values used in C when _FILE_OFFSET_BITS == 64 are corrected by the glibc fcntl function before making the system call. With this change, regenerate ppc64le files on Ubuntu trusty. Change-Id: I8dddbd8a6bae877efff818f5c5dd06291ade3238 Reviewed-on: https://go-review.googlesource.com/9962 Reviewed-by: Minux Ma --- src/syscall/mkerrors.sh | 2 ++ src/syscall/zerrors_linux_ppc64le.go | 4 ++-- src/syscall/ztypes_linux_ppc64le.go | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/syscall/mkerrors.sh b/src/syscall/mkerrors.sh index d25527bbf9..438de6e5d8 100755 --- a/src/syscall/mkerrors.sh +++ b/src/syscall/mkerrors.sh @@ -87,7 +87,9 @@ includes_FreeBSD=' includes_Linux=' #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE +#ifndef __LP64__ #define _FILE_OFFSET_BITS 64 +#endif #define _GNU_SOURCE #include diff --git a/src/syscall/zerrors_linux_ppc64le.go b/src/syscall/zerrors_linux_ppc64le.go index fdecdf24dd..17c4c4cf3a 100644 --- a/src/syscall/zerrors_linux_ppc64le.go +++ b/src/syscall/zerrors_linux_ppc64le.go @@ -366,9 +366,9 @@ const ( F_SETFD = 0x2 F_SETFL = 0x4 F_SETLEASE = 0x400 - F_SETLK = 0xd + F_SETLK = 0x6 F_SETLK64 = 0xd - F_SETLKW = 0xe + F_SETLKW = 0x7 F_SETLKW64 = 0xe F_SETOWN = 0x8 F_SETOWN_EX = 0xf diff --git a/src/syscall/ztypes_linux_ppc64le.go b/src/syscall/ztypes_linux_ppc64le.go index c6b6f1615d..0de1770f7f 100644 --- a/src/syscall/ztypes_linux_ppc64le.go +++ b/src/syscall/ztypes_linux_ppc64le.go @@ -549,7 +549,7 @@ type Sysinfo_t struct { Totalhigh uint64 Freehigh uint64 Unit uint32 - X_f [0]byte + X_f [0]uint8 Pad_cgo_1 [4]byte } From 6f7961da28232c609f7c51b3bed7f15db7dd33e1 Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Thu, 23 Apr 2015 23:57:00 +0900 Subject: [PATCH 061/232] net, internal/syscall/unix: add SocketConn, SocketPacketConn FileConn and FilePacketConn APIs accept user-configured socket descriptors to make them work together with runtime-integrated network poller, but there's a limitation. The APIs reject protocol sockets that are not supported by standard library. It's very hard for the net, syscall packages to look after all platform, feature-specific sockets. This change allows various platform, feature-specific socket descriptors to use runtime-integrated network poller by using SocketConn, SocketPacketConn APIs that bridge between the net, syscall packages and platforms. New exposed APIs: pkg net, func SocketConn(*os.File, SocketAddr) (Conn, error) pkg net, func SocketPacketConn(*os.File, SocketAddr) (PacketConn, error) pkg net, type SocketAddr interface { Addr, Raw } pkg net, type SocketAddr interface, Addr([]uint8) Addr pkg net, type SocketAddr interface, Raw(Addr) []uint8 Fixes #10565. Change-Id: Iec57499b3d84bb5cb0bcf3f664330c535eec11e3 Reviewed-on: https://go-review.googlesource.com/9275 Reviewed-by: Ian Lance Taylor --- src/go/build/deps_test.go | 2 +- src/internal/syscall/unix/socket.go | 39 ++++ src/internal/syscall/unix/socket_linux_386.go | 67 +++++++ src/internal/syscall/unix/socket_linux_386.s | 11 ++ src/internal/syscall/unix/socket_stub.go | 25 +++ src/internal/syscall/unix/socket_unix.go | 59 ++++++ src/net/fd_unix.go | 51 ++++++ src/net/file.go | 44 +++++ src/net/file_bsd_test.go | 94 ++++++++++ src/net/file_linux_test.go | 97 ++++++++++ src/net/file_plan9.go | 8 + src/net/file_stub.go | 8 +- src/net/file_unix.go | 169 ++++++++++++------ src/net/file_windows.go | 10 ++ 14 files changed, 624 insertions(+), 60 deletions(-) create mode 100644 src/internal/syscall/unix/socket.go create mode 100644 src/internal/syscall/unix/socket_linux_386.go create mode 100644 src/internal/syscall/unix/socket_linux_386.s create mode 100644 src/internal/syscall/unix/socket_stub.go create mode 100644 src/internal/syscall/unix/socket_unix.go create mode 100644 src/net/file_bsd_test.go create mode 100644 src/net/file_linux_test.go diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go index 5a28c34adf..8e985aa05b 100644 --- a/src/go/build/deps_test.go +++ b/src/go/build/deps_test.go @@ -244,7 +244,7 @@ var pkgDeps = map[string][]string{ // Basic networking. // Because net must be used by any package that wants to // do networking portably, it must have a small dependency set: just L1+basic os. - "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/windows", "internal/singleflight"}, + "net": {"L1", "CGO", "os", "syscall", "time", "internal/syscall/unix", "internal/syscall/windows", "internal/singleflight"}, // NET enables use of basic network-related packages. "NET": { diff --git a/src/internal/syscall/unix/socket.go b/src/internal/syscall/unix/socket.go new file mode 100644 index 0000000000..d7a9b9cb1d --- /dev/null +++ b/src/internal/syscall/unix/socket.go @@ -0,0 +1,39 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris + +package unix + +// Getsockname copies the binary encoding of the current address for s +// into addr. +func Getsockname(s int, addr []byte) error { + return getsockname(s, addr) +} + +// Getpeername copies the binary encoding of the peer address for s +// into addr. +func Getpeername(s int, addr []byte) error { + return getpeername(s, addr) +} + +var emptyPayload uintptr + +// Recvfrom receives a message from s, copying the message into b. +// The socket address addr must be large enough for storing the source +// address of the message. +// Flags must be operation control flags or 0. +// It retunrs the number of bytes copied into b. +func Recvfrom(s int, b []byte, flags int, addr []byte) (int, error) { + return recvfrom(s, b, flags, addr) +} + +// Sendto sends a message to the socket address addr, copying the +// message from b. +// The socket address addr must be suitable for s. +// Flags must be operation control flags or 0. +// It retunrs the number of bytes copied from b. +func Sendto(s int, b []byte, flags int, addr []byte) (int, error) { + return sendto(s, b, flags, addr) +} diff --git a/src/internal/syscall/unix/socket_linux_386.go b/src/internal/syscall/unix/socket_linux_386.go new file mode 100644 index 0000000000..47105e0b1d --- /dev/null +++ b/src/internal/syscall/unix/socket_linux_386.go @@ -0,0 +1,67 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package unix + +import ( + "syscall" + "unsafe" +) + +const ( + sysGETSOCKNAME = 0x6 + sysGETPEERNAME = 0x7 + sysSENDTO = 0xb + sysRECVFROM = 0xc +) + +func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno) +func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (int, syscall.Errno) + +func getsockname(s int, addr []byte) error { + l := uint32(len(addr)) + _, errno := rawsocketcall(sysGETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0) + if errno != 0 { + return error(errno) + } + return nil +} + +func getpeername(s int, addr []byte) error { + l := uint32(len(addr)) + _, errno := rawsocketcall(sysGETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l)), 0, 0, 0) + if errno != 0 { + return error(errno) + } + return nil +} + +func recvfrom(s int, b []byte, flags int, from []byte) (int, error) { + var p unsafe.Pointer + if len(b) > 0 { + p = unsafe.Pointer(&b[0]) + } else { + p = unsafe.Pointer(&emptyPayload) + } + l := uint32(len(from)) + n, errno := socketcall(sysRECVFROM, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&from[0])), uintptr(unsafe.Pointer(&l))) + if errno != 0 { + return int(n), error(errno) + } + return int(n), nil +} + +func sendto(s int, b []byte, flags int, to []byte) (int, error) { + var p unsafe.Pointer + if len(b) > 0 { + p = unsafe.Pointer(&b[0]) + } else { + p = unsafe.Pointer(&emptyPayload) + } + n, errno := socketcall(sysSENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to))) + if errno != 0 { + return int(n), error(errno) + } + return int(n), nil +} diff --git a/src/internal/syscall/unix/socket_linux_386.s b/src/internal/syscall/unix/socket_linux_386.s new file mode 100644 index 0000000000..48e2094db5 --- /dev/null +++ b/src/internal/syscall/unix/socket_linux_386.s @@ -0,0 +1,11 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "textflag.h" + +TEXT ·socketcall(SB),NOSPLIT,$0-36 + JMP syscall·socketcall(SB) + +TEXT ·rawsocketcall(SB),NOSPLIT,$0-36 + JMP syscall·socketcall(SB) diff --git a/src/internal/syscall/unix/socket_stub.go b/src/internal/syscall/unix/socket_stub.go new file mode 100644 index 0000000000..1c89ed1820 --- /dev/null +++ b/src/internal/syscall/unix/socket_stub.go @@ -0,0 +1,25 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build nacl solaris + +package unix + +import "syscall" + +func getsockname(s int, addr []byte) error { + return syscall.EOPNOTSUPP +} + +func getpeername(s int, addr []byte) error { + return syscall.EOPNOTSUPP +} + +func recvfrom(s int, b []byte, flags int, from []byte) (int, error) { + return 0, syscall.EOPNOTSUPP +} + +func sendto(s int, b []byte, flags int, to []byte) (int, error) { + return 0, syscall.EOPNOTSUPP +} diff --git a/src/internal/syscall/unix/socket_unix.go b/src/internal/syscall/unix/socket_unix.go new file mode 100644 index 0000000000..a769bb38b6 --- /dev/null +++ b/src/internal/syscall/unix/socket_unix.go @@ -0,0 +1,59 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd linux,!386 netbsd openbsd + +package unix + +import ( + "syscall" + "unsafe" +) + +func getsockname(s int, addr []byte) error { + l := uint32(len(addr)) + _, _, errno := syscall.RawSyscall(syscall.SYS_GETSOCKNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l))) + if errno != 0 { + return error(errno) + } + return nil +} + +func getpeername(s int, addr []byte) error { + l := uint32(len(addr)) + _, _, errno := syscall.RawSyscall(syscall.SYS_GETPEERNAME, uintptr(s), uintptr(unsafe.Pointer(&addr[0])), uintptr(unsafe.Pointer(&l))) + if errno != 0 { + return error(errno) + } + return nil +} + +func recvfrom(s int, b []byte, flags int, from []byte) (int, error) { + var p unsafe.Pointer + if len(b) > 0 { + p = unsafe.Pointer(&b[0]) + } else { + p = unsafe.Pointer(&emptyPayload) + } + l := uint32(len(from)) + n, _, errno := syscall.Syscall6(syscall.SYS_RECVFROM, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&from[0])), uintptr(unsafe.Pointer(&l))) + if errno != 0 { + return int(n), error(errno) + } + return int(n), nil +} + +func sendto(s int, b []byte, flags int, to []byte) (int, error) { + var p unsafe.Pointer + if len(b) > 0 { + p = unsafe.Pointer(&b[0]) + } else { + p = unsafe.Pointer(&emptyPayload) + } + n, _, errno := syscall.Syscall6(syscall.SYS_SENDTO, uintptr(s), uintptr(p), uintptr(len(b)), uintptr(flags), uintptr(unsafe.Pointer(&to[0])), uintptr(len(to))) + if errno != 0 { + return int(n), error(errno) + } + return int(n), nil +} diff --git a/src/net/fd_unix.go b/src/net/fd_unix.go index f2d7b348bf..827045b13d 100644 --- a/src/net/fd_unix.go +++ b/src/net/fd_unix.go @@ -7,6 +7,7 @@ package net import ( + "internal/syscall/unix" "io" "os" "runtime" @@ -270,6 +271,33 @@ func (fd *netFD) readFrom(p []byte) (n int, sa syscall.Sockaddr, err error) { return } +func (fd *netFD) recvFrom(b []byte, flags int, from []byte) (n int, err error) { + if err := fd.readLock(); err != nil { + return 0, err + } + defer fd.readUnlock() + if err := fd.pd.PrepareRead(); err != nil { + return 0, err + } + for { + n, err = unix.Recvfrom(fd.sysfd, b, flags, from) + if err != nil { + n = 0 + if err == syscall.EAGAIN { + if err = fd.pd.WaitRead(); err == nil { + continue + } + } + } + err = fd.eofError(n, err) + break + } + if _, ok := err.(syscall.Errno); ok { + err = os.NewSyscallError("recvfrom", err) + } + return +} + func (fd *netFD) readMsg(p []byte, oob []byte) (n, oobn, flags int, sa syscall.Sockaddr, err error) { if err := fd.readLock(); err != nil { return 0, 0, 0, nil, err @@ -359,6 +387,29 @@ func (fd *netFD) writeTo(p []byte, sa syscall.Sockaddr) (n int, err error) { return } +func (fd *netFD) sendTo(b []byte, flags int, to []byte) (n int, err error) { + if err := fd.writeLock(); err != nil { + return 0, err + } + defer fd.writeUnlock() + if err := fd.pd.PrepareWrite(); err != nil { + return 0, err + } + for { + n, err = unix.Sendto(fd.sysfd, b, flags, to) + if err == syscall.EAGAIN { + if err = fd.pd.WaitWrite(); err == nil { + continue + } + } + break + } + if _, ok := err.(syscall.Errno); ok { + err = os.NewSyscallError("sendto", err) + } + return +} + func (fd *netFD) writeMsg(p []byte, oob []byte, sa syscall.Sockaddr) (n int, oobn int, err error) { if err := fd.writeLock(); err != nil { return 0, 0, err diff --git a/src/net/file.go b/src/net/file.go index 1aad477400..1d0686c9fc 100644 --- a/src/net/file.go +++ b/src/net/file.go @@ -46,3 +46,47 @@ func FilePacketConn(f *os.File) (c PacketConn, err error) { } return } + +// A SocketAddr is used with SocketConn or SocketPacketConn to +// implement a user-configured socket address. +// The net package does not provide any implementations of SocketAddr; +// the caller of SocketConn or SocketPacketConn is expected to provide +// one. +type SocketAddr interface { + // Addr takes a platform-specific socket address and returns + // a net.Addr. The result may be nil when the syscall package, + // system call or underlying protocol does not support the + // socket address. + Addr([]byte) Addr + + // Raw takes a net.Addr and returns a platform-specific socket + // address. The result may be nil when the syscall package, + // system call or underlying protocol does not support the + // socket address. + Raw(Addr) []byte +} + +// SocketConn returns a copy of the network connection corresponding +// to the open file f and user-defined socket address sa. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func SocketConn(f *os.File, sa SocketAddr) (c Conn, err error) { + c, err = socketConn(f, sa) + if err != nil { + err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err} + } + return +} + +// SocketPacketConn returns a copy of the packet network connection +// corresponding to the open file f and user-defined socket address +// sa. +// It is the caller's responsibility to close f when finished. +// Closing c does not affect f, and closing f does not affect c. +func SocketPacketConn(f *os.File, sa SocketAddr) (c PacketConn, err error) { + c, err = socketPacketConn(f, sa) + if err != nil { + err = &OpError{Op: "file", Net: "file+net", Source: nil, Addr: fileAddr(f.Name()), Err: err} + } + return +} diff --git a/src/net/file_bsd_test.go b/src/net/file_bsd_test.go new file mode 100644 index 0000000000..6e6cf126ad --- /dev/null +++ b/src/net/file_bsd_test.go @@ -0,0 +1,94 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build darwin dragonfly freebsd netbsd openbsd + +package net + +import ( + "os" + "runtime" + "strings" + "sync" + "syscall" + "testing" + "unsafe" +) + +type routeAddr struct{} + +func (a *routeAddr) Network() string { return "route" } +func (a *routeAddr) String() string { return "" } + +func (a *routeAddr) Addr(rsa []byte) Addr { return &routeAddr{} } +func (a *routeAddr) Raw(addr Addr) []byte { return nil } + +func TestSocketConn(t *testing.T) { + var freebsd32o64 bool + if runtime.GOOS == "freebsd" && runtime.GOARCH == "386" { + archs, _ := syscall.Sysctl("kern.supported_archs") + for _, s := range strings.Split(archs, " ") { + if strings.TrimSpace(s) == "amd64" { + freebsd32o64 = true + break + } + } + } + + s, err := syscall.Socket(syscall.AF_ROUTE, syscall.SOCK_RAW, syscall.AF_UNSPEC) + if err != nil { + t.Fatal(err) + } + f := os.NewFile(uintptr(s), "route") + c, err := SocketConn(f, &routeAddr{}) + f.Close() + if err != nil { + t.Fatal(err) + } + defer c.Close() + + const N = 3 + for i := 0; i < N; i++ { + go func(i int) { + l := syscall.SizeofRtMsghdr + syscall.SizeofSockaddrInet4 + if freebsd32o64 { + l += syscall.SizeofRtMetrics // see syscall/route_freebsd_32bit.go + } + b := make([]byte, l) + h := (*syscall.RtMsghdr)(unsafe.Pointer(&b[0])) + h.Msglen = uint16(len(b)) + h.Version = syscall.RTM_VERSION + h.Type = syscall.RTM_GET + h.Addrs = syscall.RTA_DST + h.Pid = int32(os.Getpid()) + h.Seq = int32(i) + p := (*syscall.RawSockaddrInet4)(unsafe.Pointer(&b[syscall.SizeofRtMsghdr])) + p.Len = syscall.SizeofSockaddrInet4 + p.Family = syscall.AF_INET + p.Addr = [4]byte{127, 0, 0, 1} + if _, err := c.Write(b); err != nil { + t.Error(err) + return + } + }(i + 1) + } + var wg sync.WaitGroup + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + defer wg.Done() + b := make([]byte, os.Getpagesize()) + n, err := c.Read(b) + if err != nil { + t.Error(err) + return + } + if _, err := syscall.ParseRoutingMessage(b[:n]); err != nil { + t.Error(err) + return + } + }() + } + wg.Wait() +} diff --git a/src/net/file_linux_test.go b/src/net/file_linux_test.go new file mode 100644 index 0000000000..58f74d2cc5 --- /dev/null +++ b/src/net/file_linux_test.go @@ -0,0 +1,97 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package net + +import ( + "fmt" + "os" + "sync" + "syscall" + "testing" + "unsafe" +) + +type netlinkAddr struct { + PID uint32 + Groups uint32 +} + +func (a *netlinkAddr) Network() string { return "netlink" } +func (a *netlinkAddr) String() string { return fmt.Sprintf("%x:%x", a.PID, a.Groups) } + +func (a *netlinkAddr) Addr(rsa []byte) Addr { + if len(rsa) < syscall.SizeofSockaddrNetlink { + return nil + } + var addr netlinkAddr + b := (*[unsafe.Sizeof(addr)]byte)(unsafe.Pointer(&addr)) + copy(b[0:4], rsa[4:8]) + copy(b[4:8], rsa[8:12]) + return &addr +} + +func (a *netlinkAddr) Raw(addr Addr) []byte { + if addr, ok := addr.(*netlinkAddr); ok { + rsa := &syscall.RawSockaddrNetlink{Family: syscall.AF_NETLINK, Pid: addr.PID, Groups: addr.Groups} + return (*[unsafe.Sizeof(*rsa)]byte)(unsafe.Pointer(rsa))[:] + } + return nil +} + +func TestSocketPacketConn(t *testing.T) { + s, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, syscall.NETLINK_ROUTE) + if err != nil { + t.Fatal(err) + } + lsa := syscall.SockaddrNetlink{Family: syscall.AF_NETLINK} + if err := syscall.Bind(s, &lsa); err != nil { + syscall.Close(s) + t.Fatal(err) + } + f := os.NewFile(uintptr(s), "netlink") + c, err := SocketPacketConn(f, &netlinkAddr{}) + f.Close() + if err != nil { + t.Fatal(err) + } + defer c.Close() + + const N = 3 + dst := &netlinkAddr{PID: 0} + for i := 0; i < N; i++ { + go func() { + l := syscall.NLMSG_HDRLEN + syscall.SizeofRtGenmsg + b := make([]byte, l) + *(*uint32)(unsafe.Pointer(&b[0:4][0])) = uint32(l) + *(*uint16)(unsafe.Pointer(&b[4:6][0])) = uint16(syscall.RTM_GETLINK) + *(*uint16)(unsafe.Pointer(&b[6:8][0])) = uint16(syscall.NLM_F_DUMP | syscall.NLM_F_REQUEST) + *(*uint32)(unsafe.Pointer(&b[8:12][0])) = uint32(1) + *(*uint32)(unsafe.Pointer(&b[12:16][0])) = uint32(0) + b[16] = byte(syscall.AF_UNSPEC) + if _, err := c.WriteTo(b, dst); err != nil { + t.Error(err) + return + } + }() + } + var wg sync.WaitGroup + wg.Add(N) + for i := 0; i < N; i++ { + go func() { + defer wg.Done() + b := make([]byte, os.Getpagesize()) + n, _, err := c.ReadFrom(b) + if err != nil { + t.Error(err) + return + } + if _, err := syscall.ParseNetlinkMessage(b[:n]); err != nil { + t.Error(err) + return + } + }() + } + wg.Wait() +} diff --git a/src/net/file_plan9.go b/src/net/file_plan9.go index 892775a024..efe416f690 100644 --- a/src/net/file_plan9.go +++ b/src/net/file_plan9.go @@ -135,3 +135,11 @@ func fileListener(f *os.File) (Listener, error) { func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.EPLAN9 } + +func socketConn(f *os.File, sa SocketAddr) (Conn, error) { + return nil, syscall.EPLAN9 +} + +func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) { + return nil, syscall.EPLAN9 +} diff --git a/src/net/file_stub.go b/src/net/file_stub.go index 0f7460c757..41ca78b437 100644 --- a/src/net/file_stub.go +++ b/src/net/file_stub.go @@ -11,6 +11,8 @@ import ( "syscall" ) -func fileConn(f *os.File) (Conn, error) { return nil, syscall.ENOPROTOOPT } -func fileListener(f *os.File) (Listener, error) { return nil, syscall.ENOPROTOOPT } -func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.ENOPROTOOPT } +func fileConn(f *os.File) (Conn, error) { return nil, syscall.ENOPROTOOPT } +func fileListener(f *os.File) (Listener, error) { return nil, syscall.ENOPROTOOPT } +func filePacketConn(f *os.File) (PacketConn, error) { return nil, syscall.ENOPROTOOPT } +func socketConn(f *os.File, sa SocketAddr) (Conn, error) { return nil, syscall.ENOPROTOOPT } +func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) { return nil, syscall.ENOPROTOOPT } diff --git a/src/net/file_unix.go b/src/net/file_unix.go index 147ca1ed95..df884d1603 100644 --- a/src/net/file_unix.go +++ b/src/net/file_unix.go @@ -7,76 +7,81 @@ package net import ( + "internal/syscall/unix" "os" "syscall" ) -func newFileFD(f *os.File) (*netFD, error) { - fd, err := dupCloseOnExec(int(f.Fd())) +func dupSocket(f *os.File) (int, error) { + s, err := dupCloseOnExec(int(f.Fd())) + if err != nil { + return -1, err + } + if err := syscall.SetNonblock(s, true); err != nil { + closeFunc(s) + return -1, os.NewSyscallError("setnonblock", err) + } + return s, nil +} + +func newFileFD(f *os.File, sa SocketAddr) (*netFD, error) { + s, err := dupSocket(f) if err != nil { return nil, err } - - if err = syscall.SetNonblock(fd, true); err != nil { - closeFunc(fd) - return nil, os.NewSyscallError("setnonblock", err) + var laddr, raddr Addr + var fd *netFD + if sa != nil { + lsa := make([]byte, syscall.SizeofSockaddrAny) + if err := unix.Getsockname(s, lsa); err != nil { + lsa = nil + } + rsa := make([]byte, syscall.SizeofSockaddrAny) + if err := unix.Getpeername(s, rsa); err != nil { + rsa = nil + } + laddr = sa.Addr(lsa) + raddr = sa.Addr(rsa) + fd, err = newFD(s, -1, -1, laddr.Network()) + } else { + family := syscall.AF_UNSPEC + sotype, err := syscall.GetsockoptInt(s, syscall.SOL_SOCKET, syscall.SO_TYPE) + if err != nil { + closeFunc(s) + return nil, os.NewSyscallError("getsockopt", err) + } + lsa, _ := syscall.Getsockname(s) + rsa, _ := syscall.Getpeername(s) + switch lsa.(type) { + case *syscall.SockaddrInet4: + family = syscall.AF_INET + case *syscall.SockaddrInet6: + family = syscall.AF_INET6 + case *syscall.SockaddrUnix: + family = syscall.AF_UNIX + default: + closeFunc(s) + return nil, syscall.EPROTONOSUPPORT + } + fd, err = newFD(s, family, sotype, "") + laddr = fd.addrFunc()(lsa) + raddr = fd.addrFunc()(rsa) + fd.net = laddr.Network() } - - sotype, err := syscall.GetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TYPE) if err != nil { - closeFunc(fd) - return nil, os.NewSyscallError("getsockopt", err) - } - - family := syscall.AF_UNSPEC - toAddr := sockaddrToTCP - lsa, _ := syscall.Getsockname(fd) - switch lsa.(type) { - case *syscall.SockaddrInet4: - family = syscall.AF_INET - if sotype == syscall.SOCK_DGRAM { - toAddr = sockaddrToUDP - } else if sotype == syscall.SOCK_RAW { - toAddr = sockaddrToIP - } - case *syscall.SockaddrInet6: - family = syscall.AF_INET6 - if sotype == syscall.SOCK_DGRAM { - toAddr = sockaddrToUDP - } else if sotype == syscall.SOCK_RAW { - toAddr = sockaddrToIP - } - case *syscall.SockaddrUnix: - family = syscall.AF_UNIX - toAddr = sockaddrToUnix - if sotype == syscall.SOCK_DGRAM { - toAddr = sockaddrToUnixgram - } else if sotype == syscall.SOCK_SEQPACKET { - toAddr = sockaddrToUnixpacket - } - default: - closeFunc(fd) - return nil, syscall.EPROTONOSUPPORT - } - laddr := toAddr(lsa) - rsa, _ := syscall.Getpeername(fd) - raddr := toAddr(rsa) - - netfd, err := newFD(fd, family, sotype, laddr.Network()) - if err != nil { - closeFunc(fd) + closeFunc(s) return nil, err } - if err := netfd.init(); err != nil { - netfd.Close() + if err := fd.init(); err != nil { + fd.Close() return nil, err } - netfd.setAddr(laddr, raddr) - return netfd, nil + fd.setAddr(laddr, raddr) + return fd, nil } func fileConn(f *os.File) (Conn, error) { - fd, err := newFileFD(f) + fd, err := newFileFD(f, nil) if err != nil { return nil, err } @@ -95,7 +100,7 @@ func fileConn(f *os.File) (Conn, error) { } func fileListener(f *os.File) (Listener, error) { - fd, err := newFileFD(f) + fd, err := newFileFD(f, nil) if err != nil { return nil, err } @@ -110,7 +115,7 @@ func fileListener(f *os.File) (Listener, error) { } func filePacketConn(f *os.File) (PacketConn, error) { - fd, err := newFileFD(f) + fd, err := newFileFD(f, nil) if err != nil { return nil, err } @@ -125,3 +130,55 @@ func filePacketConn(f *os.File) (PacketConn, error) { fd.Close() return nil, syscall.EINVAL } + +func socketConn(f *os.File, sa SocketAddr) (Conn, error) { + fd, err := newFileFD(f, sa) + if err != nil { + return nil, err + } + return &socketFile{conn: conn{fd}, SocketAddr: sa}, nil +} + +func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) { + fd, err := newFileFD(f, sa) + if err != nil { + return nil, err + } + return &socketFile{conn: conn{fd}, SocketAddr: sa}, nil +} + +var ( + _ Conn = &socketFile{} + _ PacketConn = &socketFile{} +) + +// A socketFile is a placeholder that holds a user-specified socket +// descriptor and a profile of socket address encoding. +// It implements both Conn and PacketConn interfaces. +type socketFile struct { + conn + SocketAddr +} + +func (c *socketFile) ReadFrom(b []byte) (int, Addr, error) { + if !c.ok() { + return 0, nil, syscall.EINVAL + } + from := make([]byte, syscall.SizeofSockaddrAny) + n, err := c.fd.recvFrom(b, 0, from) + if err != nil { + return n, nil, &OpError{Op: "read", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} + } + return n, c.SocketAddr.Addr(from), nil +} + +func (c *socketFile) WriteTo(b []byte, addr Addr) (int, error) { + if !c.ok() { + return 0, syscall.EINVAL + } + n, err := c.fd.sendTo(b, 0, c.SocketAddr.Raw(addr)) + if err != nil { + return n, &OpError{Op: "write", Net: c.fd.net, Source: c.fd.laddr, Addr: c.fd.raddr, Err: err} + } + return n, nil +} diff --git a/src/net/file_windows.go b/src/net/file_windows.go index 241fa17617..1ed72d5bd4 100644 --- a/src/net/file_windows.go +++ b/src/net/file_windows.go @@ -23,3 +23,13 @@ func filePacketConn(f *os.File) (PacketConn, error) { // TODO: Implement this return nil, syscall.EWINDOWS } + +func socketConn(f *os.File, sa SocketAddr) (Conn, error) { + // TODO: Implement this + return nil, syscall.EWINDOWS +} + +func socketPacketConn(f *os.File, sa SocketAddr) (PacketConn, error) { + // TODO: Implement this + return nil, syscall.EWINDOWS +} From 3b38626f7d5c9b85e3acb2601e34324879438fae Mon Sep 17 00:00:00 2001 From: Mikio Hara Date: Tue, 12 May 2015 23:11:28 +0900 Subject: [PATCH 062/232] net: don't run IP stack required tests on IP stack unimplemented kernels Fixes #10787. Change-Id: I35c96808a713dafb1f0fea301fa3f3528fe6a5bf Reviewed-on: https://go-review.googlesource.com/9948 Reviewed-by: Alex Brainman --- src/net/main_test.go | 4 ++-- src/net/platform_test.go | 13 ++++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/net/main_test.go b/src/net/main_test.go index a56b9cd5f9..4288e2add2 100644 --- a/src/net/main_test.go +++ b/src/net/main_test.go @@ -97,7 +97,7 @@ func printLeakedGoroutines() { if len(gss) == 0 { return } - fmt.Fprintf(os.Stderr, "Leaked goroutines:\n") + fmt.Fprintf(os.Stderr, "Running goroutines:\n") for _, gs := range gss { fmt.Fprintf(os.Stderr, "%v\n", gs) } @@ -130,7 +130,7 @@ func printLeakedSockets() { if len(sos) == 0 { return } - fmt.Fprintf(os.Stderr, "Leaked sockets:\n") + fmt.Fprintf(os.Stderr, "Inflight sockets:\n") for s, so := range sos { fmt.Fprintf(os.Stderr, "%v: %v\n", s, so) } diff --git a/src/net/platform_test.go b/src/net/platform_test.go index b700091dc5..d6248520f3 100644 --- a/src/net/platform_test.go +++ b/src/net/platform_test.go @@ -14,7 +14,8 @@ import ( // testableNetwork reports whether network is testable on the current // platform configuration. func testableNetwork(network string) bool { - switch ss := strings.Split(network, ":"); ss[0] { + ss := strings.Split(network, ":") + switch ss[0] { case "ip+nopriv": switch runtime.GOOS { case "nacl": @@ -46,6 +47,16 @@ func testableNetwork(network string) bool { return false } } + switch ss[0] { + case "tcp4", "udp4", "ip4": + if !supportsIPv4 { + return false + } + case "tcp6", "udp6", "ip6": + if !supportsIPv6 { + return false + } + } return true } From c4fe503119ee517e202e81913f08b2e9940e1a72 Mon Sep 17 00:00:00 2001 From: Rick Hudson Date: Thu, 7 May 2015 17:19:30 -0400 Subject: [PATCH 063/232] runtime: reduce thrashing of gs between ps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One important use case is a pipeline computation that pass values from one Goroutine to the next and then exits or is placed in a wait state. If GOMAXPROCS > 1 a Goroutine running on P1 will enable another Goroutine and then immediately make P1 available to execute it. We need to prevent other Ps from stealing the G that P1 is about to execute. Otherwise the Gs can thrash between Ps causing unneeded synchronization and slowing down throughput. Fix this by changing the stealing logic so that when a P attempts to steal the only G on some other P's run queue, it will pause momentarily to allow the victim P to schedule the G. As part of optimizing stealing we also use a per P victim queue move stolen gs. This eliminates the zeroing of a stack local victim queue which turned out to be expensive. This CL is a necessary but not sufficient prerequisite to changing the default value of GOMAXPROCS to something > 1 which is another CL/discussion. For highly serialized programs, such as GoroutineRing below this can make a large difference. For larger and more parallel programs such as the x/benchmarks there is no noticeable detriment. ~/work/code/src/rsc.io/benchstat/benchstat old.txt new.txt name old mean new mean delta GoroutineRing 30.2µs × (0.98,1.01) 30.1µs × (0.97,1.04) ~ (p=0.941) GoroutineRing-2 113µs × (0.91,1.07) 30µs × (0.98,1.03) -73.17% (p=0.004) GoroutineRing-4 144µs × (0.98,1.02) 32µs × (0.98,1.01) -77.69% (p=0.000) GoroutineRingBuf 32.7µs × (0.97,1.03) 32.5µs × (0.97,1.02) ~ (p=0.795) GoroutineRingBuf-2 120µs × (0.92,1.08) 33µs × (1.00,1.00) -72.48% (p=0.004) GoroutineRingBuf-4 138µs × (0.92,1.06) 33µs × (1.00,1.00) -76.21% (p=0.003) The bench benchmarks show little impact. old new garbage 7032879 7011696 httpold 25509 25301 splayold 1022073 1019499 jsonold 28230624 28081433 Change-Id: I228c48fed8d85c9bbef16a7edc53ab7898506f50 Reviewed-on: https://go-review.googlesource.com/9872 Reviewed-by: Austin Clements --- src/runtime/proc1.go | 44 +++++++++++++++++++++++++---------------- src/runtime/runtime2.go | 7 ++++--- 2 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go index 2fe1551952..8aeacee747 100644 --- a/src/runtime/proc1.go +++ b/src/runtime/proc1.go @@ -1420,7 +1420,7 @@ top: xadd(&sched.nmspinning, 1) } // random steal from other P's - for i := 0; i < int(2*gomaxprocs); i++ { + for i := 0; i < int(4*gomaxprocs); i++ { if sched.gcwaiting != 0 { goto top } @@ -1429,15 +1429,17 @@ top: if _p_ == _g_.m.p.ptr() { gp, _ = runqget(_p_) } else { - gp = runqsteal(_g_.m.p.ptr(), _p_) + stealRunNextG := i > 2*int(gomaxprocs) // first look for ready queues with more than 1 g + gp = runqsteal(_g_.m.p.ptr(), _p_, stealRunNextG) } if gp != nil { return gp, false } } + stop: - // We have nothing to do. If we're in the GC mark phaseand can + // We have nothing to do. If we're in the GC mark phase and can // safely scan and blacken objects, run idle-time marking // rather than give up the P. if _p_ := _g_.m.p.ptr(); gcBlackenEnabled != 0 && _p_.gcBgMarkWorker != nil { @@ -3461,20 +3463,30 @@ func runqget(_p_ *p) (gp *g, inheritTime bool) { // Grabs a batch of goroutines from local runnable queue. // batch array must be of size len(p->runq)/2. Returns number of grabbed goroutines. // Can be executed by any P. -func runqgrab(_p_ *p, batch []*g) uint32 { +func runqgrab(_p_ *p, batch []*g, stealRunNextG bool) uint32 { for { h := atomicload(&_p_.runqhead) // load-acquire, synchronize with other consumers t := atomicload(&_p_.runqtail) // load-acquire, synchronize with the producer n := t - h n = n - n/2 if n == 0 { - // Try to steal from _p_.runnext. - if next := _p_.runnext; next != 0 { - if !_p_.runnext.cas(next, 0) { - continue + if stealRunNextG { + // Try to steal from _p_.runnext. + if next := _p_.runnext; next != 0 { + // Sleep to ensure that _p_ isn't about to run the g we + // are about to steal. + // The important use case here is when the g running on _p_ + // ready()s another g and then almost immediately blocks. + // Instead of stealing runnext in this window, back off + // to give _p_ a chance to schedule runnext. This will avoid + // thrashing gs between different Ps. + usleep(100) + if !_p_.runnext.cas(next, 0) { + continue + } + batch[0] = next.ptr() + return 1 } - batch[0] = next.ptr() - return 1 } return 0 } @@ -3493,15 +3505,13 @@ func runqgrab(_p_ *p, batch []*g) uint32 { // Steal half of elements from local runnable queue of p2 // and put onto local runnable queue of p. // Returns one of the stolen elements (or nil if failed). -func runqsteal(_p_, p2 *p) *g { - var batch [len(_p_.runq) / 2]*g - - n := runqgrab(p2, batch[:]) +func runqsteal(_p_, p2 *p, stealRunNextG bool) *g { + n := runqgrab(p2, _p_.runqvictims[:], stealRunNextG) if n == 0 { return nil } n-- - gp := batch[n] + gp := _p_.runqvictims[n] if n == 0 { return gp } @@ -3511,7 +3521,7 @@ func runqsteal(_p_, p2 *p) *g { throw("runqsteal: runq overflow") } for i := uint32(0); i < n; i++ { - _p_.runq[(t+i)%uint32(len(_p_.runq))] = batch[i] + _p_.runq[(t+i)%uint32(len(_p_.runq))] = _p_.runqvictims[i] } atomicstore(&_p_.runqtail, t+n) // store-release, makes the item available for consumption return gp @@ -3548,7 +3558,7 @@ func testSchedLocalQueueSteal() { gs[j].sig = 0 runqput(p1, &gs[j], false) } - gp := runqsteal(p2, p1) + gp := runqsteal(p2, p1, true) s := 0 if gp != nil { s++ diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 8dfece5845..ae93bb8dcb 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -353,9 +353,10 @@ type p struct { goidcacheend uint64 // Queue of runnable goroutines. Accessed without lock. - runqhead uint32 - runqtail uint32 - runq [256]*g + runqhead uint32 + runqtail uint32 + runq [256]*g + runqvictims [128]*g // Used to stage victims from another p's runq // runnext, if non-nil, is a runnable G that was ready'd by // the current G and should be run next instead of what's in // runq if there's time remaining in the running G's time From 99475dfb5923b775a9ab587ad99cb64de5d1f51c Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 21 Apr 2015 10:39:21 -0700 Subject: [PATCH 064/232] cmd/internal/gc: avoid spurious div-zero errors Set overflowing integer constants to 1 rather than 0 to avoid spurious div-zero errors in subsequent constant expressions. Also: Exclude new test case from go/types test since it's running too long (go/types doesn't have an upper constant size limit at the moment). Fixes #7746. Change-Id: I3768488ad9909a3cf995247b81ee78a8eb5a1e41 Reviewed-on: https://go-review.googlesource.com/9165 Run-TryBot: Robert Griesemer TryBot-Result: Gobot Gobot Reviewed-by: Russ Cox --- src/cmd/internal/gc/const.go | 4 +- src/cmd/internal/gc/mparith2.go | 2 +- src/go/types/stdlib_test.go | 1 + test/fixedbugs/issue7746.go | 133 ++++++++++++++++++++++++++++++++ 4 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 test/fixedbugs/issue7746.go diff --git a/src/cmd/internal/gc/const.go b/src/cmd/internal/gc/const.go index 5ec54bdffb..84b133769c 100644 --- a/src/cmd/internal/gc/const.go +++ b/src/cmd/internal/gc/const.go @@ -774,7 +774,7 @@ func evconst(n *Node) { ODIV<<16 | CTRUNE: if mpcmpfixc(rv.U.Xval, 0) == 0 { Yyerror("division by zero") - Mpmovecfix(v.U.Xval, 1) + mpsetovf(v.U.Xval) break } @@ -784,7 +784,7 @@ func evconst(n *Node) { OMOD<<16 | CTRUNE: if mpcmpfixc(rv.U.Xval, 0) == 0 { Yyerror("division by zero") - Mpmovecfix(v.U.Xval, 1) + mpsetovf(v.U.Xval) break } diff --git a/src/cmd/internal/gc/mparith2.go b/src/cmd/internal/gc/mparith2.go index de96e97809..2456dbf60a 100644 --- a/src/cmd/internal/gc/mparith2.go +++ b/src/cmd/internal/gc/mparith2.go @@ -13,7 +13,7 @@ import ( /// implements fix arithmetic func mpsetovf(a *Mpint) { - a.Val.SetUint64(0) + a.Val.SetUint64(1) // avoid spurious div-zero errors a.Ovf = true } diff --git a/src/go/types/stdlib_test.go b/src/go/types/stdlib_test.go index d04dd71e4f..28a66cebe3 100644 --- a/src/go/types/stdlib_test.go +++ b/src/go/types/stdlib_test.go @@ -146,6 +146,7 @@ func TestStdFixed(t *testing.T) { "bug459.go", // possibly incorrect test - see issue 6703 (pending spec clarification) "issue3924.go", // possibly incorrect test - see issue 6671 (pending spec clarification) "issue6889.go", // gc-specific test + "issue7746.go", // large constants - consumes too much memory ) } diff --git a/test/fixedbugs/issue7746.go b/test/fixedbugs/issue7746.go new file mode 100644 index 0000000000..0dc119d2e6 --- /dev/null +++ b/test/fixedbugs/issue7746.go @@ -0,0 +1,133 @@ +// errorcheck + +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +const ( + c0 = 1 << 100 + c1 = c0 * c0 + c2 = c1 * c1 + c3 = c2 * c2 // ERROR "overflow" + c4 = c3 * c3 + c5 = c4 * c4 + c6 = c5 * c5 + c7 = c6 * c6 + c8 = c7 * c7 + c9 = c8 * c8 + c10 = c9 * c9 + c11 = c10 * c10 + c12 = c11 * c11 + c13 = c12 * c12 + c14 = c13 * c13 + c15 = c14 * c14 + c16 = c15 * c15 + c17 = c16 * c16 + c18 = c17 * c17 + c19 = c18 * c18 + c20 = c19 * c19 + c21 = c20 * c20 + c22 = c21 * c21 + c23 = c22 * c22 + c24 = c23 * c23 + c25 = c24 * c24 + c26 = c25 * c25 + c27 = c26 * c26 + c28 = c27 * c27 + c29 = c28 * c28 + c30 = c29 * c29 + c31 = c30 * c30 + c32 = c31 * c31 + c33 = c32 * c32 + c34 = c33 * c33 + c35 = c34 * c34 + c36 = c35 * c35 + c37 = c36 * c36 + c38 = c37 * c37 + c39 = c38 * c38 + c40 = c39 * c39 + c41 = c40 * c40 + c42 = c41 * c41 + c43 = c42 * c42 + c44 = c43 * c43 + c45 = c44 * c44 + c46 = c45 * c45 + c47 = c46 * c46 + c48 = c47 * c47 + c49 = c48 * c48 + c50 = c49 * c49 + c51 = c50 * c50 + c52 = c51 * c51 + c53 = c52 * c52 + c54 = c53 * c53 + c55 = c54 * c54 + c56 = c55 * c55 + c57 = c56 * c56 + c58 = c57 * c57 + c59 = c58 * c58 + c60 = c59 * c59 + c61 = c60 * c60 + c62 = c61 * c61 + c63 = c62 * c62 + c64 = c63 * c63 + c65 = c64 * c64 + c66 = c65 * c65 + c67 = c66 * c66 + c68 = c67 * c67 + c69 = c68 * c68 + c70 = c69 * c69 + c71 = c70 * c70 + c72 = c71 * c71 + c73 = c72 * c72 + c74 = c73 * c73 + c75 = c74 * c74 + c76 = c75 * c75 + c77 = c76 * c76 + c78 = c77 * c77 + c79 = c78 * c78 + c80 = c79 * c79 + c81 = c80 * c80 + c82 = c81 * c81 + c83 = c82 * c82 + c84 = c83 * c83 + c85 = c84 * c84 + c86 = c85 * c85 + c87 = c86 * c86 + c88 = c87 * c87 + c89 = c88 * c88 + c90 = c89 * c89 + c91 = c90 * c90 + c92 = c91 * c91 + c93 = c92 * c92 + c94 = c93 * c93 + c95 = c94 * c94 + c96 = c95 * c95 + c97 = c96 * c96 + c98 = c97 * c97 + c99 = c98 * c98 + c100 = c99 * c99 +) + +func main() { + println(c1 / c1) + println(c2 / c2) + println(c3 / c3) + println(c4 / c4) + println(c5 / c5) + println(c6 / c6) + println(c7 / c7) + println(c8 / c8) + println(c9 / c9) + println(c10 / c10) + println(c20 / c20) + println(c30 / c30) + println(c40 / c40) + println(c50 / c50) + println(c60 / c60) + println(c70 / c70) + println(c80 / c80) + println(c90 / c90) + println(c100 / c100) +} From abb818bc03fc19324c9271e8d4dbe5840eea18d2 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Mon, 20 Apr 2015 17:17:24 -0700 Subject: [PATCH 065/232] spec: fix binary expression grammar rule The spec explains later in the "Operator precedence" section that * has a higher precedence than +, but the current production rule requires that "1 + 2 * 3" be parsed as "(1 + 2) * 3", instead of the intended "1 + (2 * 3)". The new production rule better matches cmd/internal/gc/go.y's grammar: expr: uexpr | expr LOROR expr | expr LANDAND expr | ... Fixes #10151. Change-Id: I13c9635d6ddf1263cafe7cc63e68f3e5779e24ba Reviewed-on: https://go-review.googlesource.com/9163 Reviewed-by: Robert Griesemer --- doc/go_spec.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index d02697bd0a..4e2f911388 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -1,6 +1,6 @@ @@ -3305,7 +3305,7 @@ Operators combine operands into expressions.

-Expression = UnaryExpr | Expression binary_op UnaryExpr .
+Expression = UnaryExpr | Expression binary_op Expression .
 UnaryExpr  = PrimaryExpr | unary_op UnaryExpr .
 
 binary_op  = "||" | "&&" | rel_op | add_op | mul_op .

From d4472799277102e461968fa059f49bc8b9b6e433 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Wed, 6 May 2015 12:35:53 -0400
Subject: [PATCH 066/232] cmd/internal/gc: optimize slice + write barrier
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The code generated for a slice x[i:j] or x[i:j:k] computes the entire
new slice (base, len, cap) and then uses it as the evaluation of the
slice expression.

If the slice is part of an update x = x[i:j] or x = x[i:j:k], there are
opportunities to avoid computing some of these fields.

For x = x[0:i], we know that only the len is changing;
base can be ignored completely, and cap can be left unmodified.

For x = x[0:i:j], we know that only len and cap are changing;
base can be ignored completely.

For x = x[i:i], we know that the resulting cap is zero, and we don't
adjust the base during a slice producing a zero-cap result,
so again base can be ignored completely.

No write to base, no write barrier.

The old slice code was trying to work at a Go syntax level, mainly
because that was how you wrote code just once instead of once
per architecture. Now the compiler is factored a bit better and we
can implement slice during code generation but still have one copy
of the code. So the new code is working at that lower level.
(It must, to update only parts of the result.)

This CL by itself:
name                   old mean              new mean              delta
BinaryTree17            5.81s × (0.98,1.03)   5.71s × (0.96,1.05)     ~    (p=0.101)
Fannkuch11              4.35s × (1.00,1.00)   4.39s × (1.00,1.00)   +0.79% (p=0.000)
FmtFprintfEmpty        86.0ns × (0.94,1.11)  82.6ns × (0.98,1.04)   -3.86% (p=0.048)
FmtFprintfString        276ns × (0.98,1.04)   273ns × (0.98,1.02)     ~    (p=0.235)
FmtFprintfInt           274ns × (0.98,1.06)   270ns × (0.99,1.01)     ~    (p=0.119)
FmtFprintfIntInt        506ns × (0.99,1.01)   475ns × (0.99,1.01)   -6.02% (p=0.000)
FmtFprintfPrefixedInt   391ns × (0.99,1.01)   393ns × (1.00,1.01)     ~    (p=0.139)
FmtFprintfFloat         566ns × (0.99,1.01)   574ns × (1.00,1.01)   +1.33% (p=0.001)
FmtManyArgs            1.91µs × (0.99,1.01)  1.87µs × (0.99,1.02)   -1.83% (p=0.000)
GobDecode              15.3ms × (0.99,1.02)  15.0ms × (0.98,1.05)   -1.84% (p=0.042)
GobEncode              11.5ms × (0.97,1.03)  11.4ms × (0.99,1.03)     ~    (p=0.152)
Gzip                    645ms × (0.99,1.01)   647ms × (0.99,1.01)     ~    (p=0.265)
Gunzip                  142ms × (1.00,1.00)   143ms × (1.00,1.01)   +0.90% (p=0.000)
HTTPClientServer       90.5µs × (0.97,1.04)  88.5µs × (0.99,1.03)   -2.27% (p=0.014)
JSONEncode             32.0ms × (0.98,1.03)  29.6ms × (0.98,1.01)   -7.51% (p=0.000)
JSONDecode              114ms × (0.99,1.01)   104ms × (1.00,1.01)   -8.60% (p=0.000)
Mandelbrot200          6.04ms × (1.00,1.01)  6.02ms × (1.00,1.00)     ~    (p=0.057)
GoParse                6.47ms × (0.97,1.05)  6.37ms × (0.97,1.04)     ~    (p=0.105)
RegexpMatchEasy0_32     171ns × (0.93,1.07)   152ns × (0.99,1.01)  -11.09% (p=0.000)
RegexpMatchEasy0_1K     550ns × (0.98,1.01)   530ns × (1.00,1.00)   -3.78% (p=0.000)
RegexpMatchEasy1_32     135ns × (0.99,1.02)   134ns × (0.99,1.01)   -1.33% (p=0.002)
RegexpMatchEasy1_1K     879ns × (1.00,1.01)   865ns × (1.00,1.00)   -1.58% (p=0.000)
RegexpMatchMedium_32    243ns × (1.00,1.00)   233ns × (1.00,1.00)   -4.30% (p=0.000)
RegexpMatchMedium_1K   70.3µs × (1.00,1.00)  69.5µs × (1.00,1.00)   -1.13% (p=0.000)
RegexpMatchHard_32     3.82µs × (1.00,1.01)  3.74µs × (1.00,1.00)   -1.95% (p=0.000)
RegexpMatchHard_1K      117µs × (1.00,1.00)   115µs × (1.00,1.00)   -1.69% (p=0.000)
Revcomp                 917ms × (0.97,1.04)   920ms × (0.97,1.04)     ~    (p=0.786)
Template                114ms × (0.99,1.01)   117ms × (0.99,1.01)   +2.58% (p=0.000)
TimeParse               622ns × (0.99,1.01)   615ns × (0.99,1.00)   -1.06% (p=0.000)
TimeFormat              665ns × (0.99,1.01)   654ns × (0.99,1.00)   -1.70% (p=0.000)

This CL and previous CL (append) combined:
name                   old mean              new mean              delta
BinaryTree17            5.68s × (0.97,1.04)   5.71s × (0.96,1.05)     ~    (p=0.638)
Fannkuch11              4.41s × (0.98,1.03)   4.39s × (1.00,1.00)     ~    (p=0.474)
FmtFprintfEmpty        92.7ns × (0.91,1.16)  82.6ns × (0.98,1.04)  -10.89% (p=0.004)
FmtFprintfString        281ns × (0.96,1.08)   273ns × (0.98,1.02)     ~    (p=0.078)
FmtFprintfInt           288ns × (0.97,1.06)   270ns × (0.99,1.01)   -6.37% (p=0.000)
FmtFprintfIntInt        493ns × (0.97,1.04)   475ns × (0.99,1.01)   -3.53% (p=0.002)
FmtFprintfPrefixedInt   423ns × (0.97,1.04)   393ns × (1.00,1.01)   -7.07% (p=0.000)
FmtFprintfFloat         598ns × (0.99,1.01)   574ns × (1.00,1.01)   -4.02% (p=0.000)
FmtManyArgs            1.89µs × (0.98,1.05)  1.87µs × (0.99,1.02)     ~    (p=0.305)
GobDecode              14.8ms × (0.98,1.03)  15.0ms × (0.98,1.05)     ~    (p=0.237)
GobEncode              12.3ms × (0.98,1.01)  11.4ms × (0.99,1.03)   -6.95% (p=0.000)
Gzip                    656ms × (0.99,1.05)   647ms × (0.99,1.01)     ~    (p=0.101)
Gunzip                  142ms × (1.00,1.00)   143ms × (1.00,1.01)   +0.58% (p=0.001)
HTTPClientServer       91.2µs × (0.97,1.04)  88.5µs × (0.99,1.03)   -3.02% (p=0.003)
JSONEncode             32.6ms × (0.97,1.08)  29.6ms × (0.98,1.01)   -9.10% (p=0.000)
JSONDecode              114ms × (0.97,1.05)   104ms × (1.00,1.01)   -8.74% (p=0.000)
Mandelbrot200          6.11ms × (0.98,1.04)  6.02ms × (1.00,1.00)     ~    (p=0.090)
GoParse                6.66ms × (0.97,1.04)  6.37ms × (0.97,1.04)   -4.41% (p=0.000)
RegexpMatchEasy0_32     159ns × (0.99,1.00)   152ns × (0.99,1.01)   -4.69% (p=0.000)
RegexpMatchEasy0_1K     538ns × (1.00,1.01)   530ns × (1.00,1.00)   -1.57% (p=0.000)
RegexpMatchEasy1_32     138ns × (1.00,1.00)   134ns × (0.99,1.01)   -2.91% (p=0.000)
RegexpMatchEasy1_1K     869ns × (0.99,1.01)   865ns × (1.00,1.00)   -0.51% (p=0.012)
RegexpMatchMedium_32    252ns × (0.99,1.01)   233ns × (1.00,1.00)   -7.85% (p=0.000)
RegexpMatchMedium_1K   72.7µs × (1.00,1.00)  69.5µs × (1.00,1.00)   -4.43% (p=0.000)
RegexpMatchHard_32     3.85µs × (1.00,1.00)  3.74µs × (1.00,1.00)   -2.74% (p=0.000)
RegexpMatchHard_1K      118µs × (1.00,1.00)   115µs × (1.00,1.00)   -2.24% (p=0.000)
Revcomp                 920ms × (0.97,1.07)   920ms × (0.97,1.04)     ~    (p=0.998)
Template                129ms × (0.98,1.03)   117ms × (0.99,1.01)   -9.79% (p=0.000)
TimeParse               619ns × (0.99,1.01)   615ns × (0.99,1.00)   -0.57% (p=0.011)
TimeFormat              661ns × (0.98,1.04)   654ns × (0.99,1.00)     ~    (p=0.223)

Change-Id: If054d81ab2c71d8d62cf54b5b1fac2af66b387fc
Reviewed-on: https://go-review.googlesource.com/9813
Reviewed-by: David Chase 
Run-TryBot: Russ Cox 
TryBot-Result: Gobot Gobot 
---
 src/cmd/8g/cgen.go              |   2 +-
 src/cmd/internal/gc/cgen.go     | 574 +++++++++++++++++++++++++++++++-
 src/cmd/internal/gc/gen.go      | 116 -------
 src/cmd/internal/gc/lex.go      |   2 +
 src/cmd/internal/gc/order.go    |  38 ++-
 src/cmd/internal/gc/racewalk.go |   3 +-
 src/cmd/internal/gc/subr.go     |   2 +-
 src/cmd/internal/gc/walk.go     | 290 +++-------------
 test/sliceopt.go                |  41 ++-
 9 files changed, 688 insertions(+), 380 deletions(-)

diff --git a/src/cmd/8g/cgen.go b/src/cmd/8g/cgen.go
index dfbdafefe3..48d9e9867a 100644
--- a/src/cmd/8g/cgen.go
+++ b/src/cmd/8g/cgen.go
@@ -17,7 +17,7 @@ import (
  */
 func igenindex(n *gc.Node, res *gc.Node, bounded bool) *obj.Prog {
 	if !gc.Is64(n.Type) {
-		if n.Addable {
+		if n.Addable && (gc.Simtype[n.Etype] == gc.TUINT32 || gc.Simtype[n.Etype] == gc.TINT32) {
 			// nothing to do.
 			*res = *n
 		} else {
diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go
index 3763a367b0..a4b4f0b61b 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/internal/gc/cgen.go
@@ -43,14 +43,7 @@ func cgen_wb(n, res *Node, wb bool) {
 
 	switch n.Op {
 	case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
-		if res.Op != ONAME || !res.Addable || wb {
-			var n1 Node
-			Tempname(&n1, n.Type)
-			Cgen_slice(n, &n1)
-			cgen_wb(&n1, res, wb)
-		} else {
-			Cgen_slice(n, res)
-		}
+		cgen_slice(n, res, wb)
 		return
 
 	case OEFACE:
@@ -2970,3 +2963,568 @@ func cgen_append(n, res *Node) {
 		i++
 	}
 }
+
+// Generate res = n, where n is x[i:j] or x[i:j:k].
+// If wb is true, need write barrier updating res's base pointer.
+// On systems with 32-bit ints, i, j, k are guaranteed to be 32-bit values.
+func cgen_slice(n, res *Node, wb bool) {
+	needFullUpdate := !samesafeexpr(n.Left, res)
+
+	// orderexpr has made sure that x is safe (but possibly expensive)
+	// and i, j, k are cheap. On a system with registers (anything but 386)
+	// we can evaluate x first and then know we have enough registers
+	// for i, j, k as well.
+	var x, xbase, xlen, xcap, i, j, k Node
+	if n.Op != OSLICEARR && n.Op != OSLICE3ARR {
+		Igen(n.Left, &x, nil)
+	}
+
+	indexRegType := Types[TUINT]
+	if Widthreg > Widthptr { // amd64p32
+		indexRegType = Types[TUINT64]
+	}
+
+	// On most systems, we use registers.
+	// The 386 has basically no registers, so substitute functions
+	// that can work with temporaries instead.
+	regalloc := Regalloc
+	ginscon := Thearch.Ginscon
+	gins := Thearch.Gins
+	if Thearch.Thechar == '8' {
+		regalloc = func(n *Node, t *Type, reuse *Node) {
+			Tempname(n, t)
+		}
+		ginscon = func(as int, c int64, n *Node) {
+			var n1 Node
+			Regalloc(&n1, n.Type, n)
+			Thearch.Gmove(n, &n1)
+			Thearch.Ginscon(as, c, &n1)
+			Thearch.Gmove(&n1, n)
+			Regfree(&n1)
+		}
+		gins = func(as int, f, t *Node) *obj.Prog {
+			var n1 Node
+			Regalloc(&n1, t.Type, t)
+			Thearch.Gmove(t, &n1)
+			Thearch.Gins(as, f, &n1)
+			Thearch.Gmove(&n1, t)
+			Regfree(&n1)
+			return nil
+		}
+	}
+
+	panics := make([]*obj.Prog, 0, 6) // 3 loads + 3 checks
+
+	loadlen := func() {
+		if xlen.Op != 0 {
+			return
+		}
+		if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
+			Nodconst(&xlen, indexRegType, n.Left.Type.Type.Bound)
+			return
+		}
+		if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
+			Nodconst(&xlen, indexRegType, int64(len(n.Left.Val.U.Sval)))
+			return
+		}
+		regalloc(&xlen, indexRegType, nil)
+		x.Xoffset += int64(Widthptr)
+		x.Type = Types[TUINT]
+		Thearch.Gmove(&x, &xlen)
+		x.Xoffset -= int64(Widthptr)
+	}
+
+	loadcap := func() {
+		if xcap.Op != 0 {
+			return
+		}
+		if n.Op == OSLICEARR || n.Op == OSLICE3ARR || n.Op == OSLICESTR {
+			loadlen()
+			xcap = xlen
+			if xcap.Op == OREGISTER {
+				Regrealloc(&xcap)
+			}
+			return
+		}
+		regalloc(&xcap, indexRegType, nil)
+		x.Xoffset += 2 * int64(Widthptr)
+		x.Type = Types[TUINT]
+		Thearch.Gmove(&x, &xcap)
+		x.Xoffset -= 2 * int64(Widthptr)
+	}
+
+	var x1, x2, x3 *Node // unevaluated index arguments
+	x1 = n.Right.Left
+	switch n.Op {
+	default:
+		x2 = n.Right.Right
+	case OSLICE3, OSLICE3ARR:
+		x2 = n.Right.Right.Left
+		x3 = n.Right.Right.Right
+	}
+
+	// load computes src into targ, but if src refers to the len or cap of n.Left,
+	// load copies those from xlen, xcap, loading xlen if needed.
+	// If targ.Op == OREGISTER on return, it must be Regfreed,
+	// but it should not be modified without first checking whether it is
+	// xlen or xcap's register.
+	load := func(src, targ *Node) {
+		if src == nil {
+			return
+		}
+		switch src.Op {
+		case OLITERAL:
+			*targ = *src
+			return
+		case OLEN:
+			// NOTE(rsc): This doesn't actually trigger, because order.go
+			// has pulled all the len and cap calls into separate assignments
+			// to temporaries. There are tests in test/sliceopt.go that could
+			// be enabled if this is fixed.
+			if samesafeexpr(n.Left, src.Left) {
+				if Debug_slice > 0 {
+					Warn("slice: reuse len")
+				}
+				loadlen()
+				*targ = xlen
+				if targ.Op == OREGISTER {
+					Regrealloc(targ)
+				}
+				return
+			}
+		case OCAP:
+			// NOTE(rsc): This doesn't actually trigger; see note in case OLEN above.
+			if samesafeexpr(n.Left, src.Left) {
+				if Debug_slice > 0 {
+					Warn("slice: reuse cap")
+				}
+				loadcap()
+				*targ = xcap
+				if targ.Op == OREGISTER {
+					Regrealloc(targ)
+				}
+				return
+			}
+		}
+		if i.Op != 0 && samesafeexpr(x1, src) {
+			if Debug_slice > 0 {
+				Warn("slice: reuse 1st index")
+			}
+			*targ = i
+			if targ.Op == OREGISTER {
+				Regrealloc(targ)
+			}
+			return
+		}
+		if j.Op != 0 && samesafeexpr(x2, src) {
+			if Debug_slice > 0 {
+				Warn("slice: reuse 2nd index")
+			}
+			*targ = j
+			if targ.Op == OREGISTER {
+				Regrealloc(targ)
+			}
+			return
+		}
+		if Thearch.Cgenindex != nil {
+			regalloc(targ, indexRegType, nil)
+			p := Thearch.Cgenindex(src, targ, false)
+			if p != nil {
+				panics = append(panics, p)
+			}
+		} else if Thearch.Igenindex != nil {
+			p := Thearch.Igenindex(src, targ, false)
+			if p != nil {
+				panics = append(panics, p)
+			}
+		} else {
+			regalloc(targ, indexRegType, nil)
+			var tmp Node
+			Cgenr(src, &tmp, targ)
+			Thearch.Gmove(&tmp, targ)
+			Regfree(&tmp)
+		}
+	}
+
+	load(x1, &i)
+	load(x2, &j)
+	load(x3, &k)
+
+	// i defaults to 0.
+	if i.Op == 0 {
+		Nodconst(&i, indexRegType, 0)
+	}
+
+	// j defaults to len(x)
+	if j.Op == 0 {
+		loadlen()
+		j = xlen
+		if j.Op == OREGISTER {
+			Regrealloc(&j)
+		}
+	}
+
+	// k defaults to cap(x)
+	// Only need to load it if we're recalculating cap or doing a full update.
+	if k.Op == 0 && n.Op != OSLICESTR && (!iszero(&i) || needFullUpdate) {
+		loadcap()
+		k = xcap
+		if k.Op == OREGISTER {
+			Regrealloc(&k)
+		}
+	}
+
+	// Check constant indexes for negative values, and against constant length if known.
+	// The func obvious below checks for out-of-order constant indexes.
+	var bound int64 = -1
+	if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
+		bound = n.Left.Type.Type.Bound
+	} else if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
+		bound = int64(len(n.Left.Val.U.Sval))
+	}
+	if Isconst(&i, CTINT) {
+		if mpcmpfixc(i.Val.U.Xval, 0) < 0 || bound >= 0 && mpcmpfixc(i.Val.U.Xval, bound) > 0 {
+			Yyerror("slice index out of bounds")
+		}
+	}
+	if Isconst(&j, CTINT) {
+		if mpcmpfixc(j.Val.U.Xval, 0) < 0 || bound >= 0 && mpcmpfixc(j.Val.U.Xval, bound) > 0 {
+			Yyerror("slice index out of bounds")
+		}
+	}
+	if Isconst(&k, CTINT) {
+		if mpcmpfixc(k.Val.U.Xval, 0) < 0 || bound >= 0 && mpcmpfixc(k.Val.U.Xval, bound) > 0 {
+			Yyerror("slice index out of bounds")
+		}
+	}
+
+	// same reports whether n1 and n2 are the same register or constant.
+	same := func(n1, n2 *Node) bool {
+		return n1.Op == OREGISTER && n2.Op == OREGISTER && n1.Reg == n2.Reg ||
+			n1.Op == ONAME && n2.Op == ONAME && n1.Orig == n2.Orig && n1.Type == n2.Type && n1.Xoffset == n2.Xoffset ||
+			n1.Op == OLITERAL && n2.Op == OLITERAL && Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval) == 0
+	}
+
+	// obvious reports whether n1 <= n2 is obviously true,
+	// and it calls Yyerror if n1 <= n2 is obviously false.
+	obvious := func(n1, n2 *Node) bool {
+		if Debug['B'] != 0 { // -B disables bounds checks
+			return true
+		}
+		if same(n1, n2) {
+			return true // n1 == n2
+		}
+		if iszero(n1) {
+			return true // using unsigned compare, so 0 <= n2 always true
+		}
+		if xlen.Op != 0 && same(n1, &xlen) && xcap.Op != 0 && same(n2, &xcap) {
+			return true // len(x) <= cap(x) always true
+		}
+		if Isconst(n1, CTINT) && Isconst(n2, CTINT) {
+			if Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval) <= 0 {
+				return true // n1, n2 constants such that n1 <= n2
+			}
+			Yyerror("slice index out of bounds")
+			return true
+		}
+		return false
+	}
+
+	compare := func(n1, n2 *Node) {
+		p := Thearch.Ginscmp(OGT, indexRegType, n1, n2, -1)
+		panics = append(panics, p)
+	}
+
+	loadcap()
+	max := &xcap
+	if k.Op != 0 && (n.Op == OSLICE3 || n.Op == OSLICE3ARR) {
+		if obvious(&k, max) {
+			if Debug_slice > 0 {
+				Warn("slice: omit check for 3rd index")
+			}
+		} else {
+			compare(&k, max)
+		}
+		max = &k
+	}
+	if j.Op != 0 {
+		if obvious(&j, max) {
+			if Debug_slice > 0 {
+				Warn("slice: omit check for 2nd index")
+			}
+		} else {
+			compare(&j, max)
+		}
+		max = &j
+	}
+	if i.Op != 0 {
+		if obvious(&i, max) {
+			if Debug_slice > 0 {
+				Warn("slice: omit check for 1st index")
+			}
+		} else {
+			compare(&i, max)
+		}
+		max = &i
+	}
+	if k.Op != 0 && i.Op != 0 {
+		obvious(&i, &k) // emit compile-time error for x[3:n:2]
+	}
+
+	if len(panics) > 0 {
+		p := Gbranch(obj.AJMP, nil, 0)
+		for _, q := range panics {
+			Patch(q, Pc)
+		}
+		Ginscall(panicslice, -1)
+		Patch(p, Pc)
+	}
+
+	// Checks are done.
+	// Compute new len as j-i, cap as k-i.
+	// If i and j are same register, len is constant 0.
+	// If i and k are same register, cap is constant 0.
+	// If j and k are same register, len and cap are same.
+
+	// Done with xlen and xcap.
+	// Now safe to modify j and k even if they alias xlen, xcap.
+	if xlen.Op == OREGISTER {
+		Regfree(&xlen)
+	}
+	if xcap.Op == OREGISTER {
+		Regfree(&xcap)
+	}
+
+	// are j and k the same value?
+	sameJK := same(&j, &k)
+
+	if i.Op != 0 {
+		// j -= i
+		if same(&i, &j) {
+			if Debug_slice > 0 {
+				Warn("slice: result len == 0")
+			}
+			if j.Op == OREGISTER {
+				Regfree(&j)
+			}
+			Nodconst(&j, indexRegType, 0)
+		} else {
+			switch j.Op {
+			case OLITERAL:
+				if Isconst(&i, CTINT) {
+					Nodconst(&j, indexRegType, Mpgetfix(j.Val.U.Xval)-Mpgetfix(i.Val.U.Xval))
+					if Debug_slice > 0 {
+						Warn("slice: result len == %d", Mpgetfix(j.Val.U.Xval))
+					}
+					break
+				}
+				fallthrough
+			case ONAME:
+				if !istemp(&j) {
+					var r Node
+					regalloc(&r, indexRegType, nil)
+					Thearch.Gmove(&j, &r)
+					j = r
+				}
+				fallthrough
+			case OREGISTER:
+				if i.Op == OLITERAL {
+					v := Mpgetfix(i.Val.U.Xval)
+					if v != 0 {
+						ginscon(Thearch.Optoas(OSUB, indexRegType), v, &j)
+					}
+				} else {
+					gins(Thearch.Optoas(OSUB, indexRegType), &i, &j)
+				}
+			}
+		}
+
+		// k -= i if k different from j and cap is needed.j
+		// (The modifications to j above cannot affect i: if j and i were aliased,
+		// we replace j with a constant 0 instead of doing a subtraction,
+		// leaving i unmodified.)
+		if k.Op == 0 {
+			if Debug_slice > 0 && n.Op != OSLICESTR {
+				Warn("slice: result cap not computed")
+			}
+			// no need
+		} else if same(&i, &k) {
+			if k.Op == OREGISTER {
+				Regfree(&k)
+			}
+			Nodconst(&k, indexRegType, 0)
+			if Debug_slice > 0 {
+				Warn("slice: result cap == 0")
+			}
+		} else if sameJK {
+			if Debug_slice > 0 {
+				Warn("slice: result cap == result len")
+			}
+			// k and j were the same value; make k-i the same as j-i.
+			if k.Op == OREGISTER {
+				Regfree(&k)
+			}
+			k = j
+			if k.Op == OREGISTER {
+				Regrealloc(&k)
+			}
+		} else {
+			switch k.Op {
+			case OLITERAL:
+				if Isconst(&i, CTINT) {
+					Nodconst(&k, indexRegType, Mpgetfix(k.Val.U.Xval)-Mpgetfix(i.Val.U.Xval))
+					if Debug_slice > 0 {
+						Warn("slice: result cap == %d", Mpgetfix(k.Val.U.Xval))
+					}
+					break
+				}
+				fallthrough
+			case ONAME:
+				if !istemp(&k) {
+					var r Node
+					regalloc(&r, indexRegType, nil)
+					Thearch.Gmove(&k, &r)
+					k = r
+				}
+				fallthrough
+			case OREGISTER:
+				if same(&i, &k) {
+					Regfree(&k)
+					Nodconst(&k, indexRegType, 0)
+					if Debug_slice > 0 {
+						Warn("slice: result cap == 0")
+					}
+				} else if i.Op == OLITERAL {
+					v := Mpgetfix(i.Val.U.Xval)
+					if v != 0 {
+						ginscon(Thearch.Optoas(OSUB, indexRegType), v, &k)
+					}
+				} else {
+					gins(Thearch.Optoas(OSUB, indexRegType), &i, &k)
+				}
+			}
+		}
+	}
+
+	adjustBase := true
+	if i.Op == 0 || iszero(&i) {
+		if Debug_slice > 0 {
+			Warn("slice: skip base adjustment for 1st index 0")
+		}
+		adjustBase = false
+	} else if k.Op != 0 && iszero(&k) || k.Op == 0 && iszero(&j) {
+		if Debug_slice > 0 {
+			if n.Op == OSLICESTR {
+				Warn("slice: skip base adjustment for string len == 0")
+			} else {
+				Warn("slice: skip base adjustment for cap == 0")
+			}
+		}
+		adjustBase = false
+	}
+
+	if !adjustBase && !needFullUpdate {
+		if Debug_slice > 0 {
+			if k.Op != 0 {
+				Warn("slice: len/cap-only update")
+			} else {
+				Warn("slice: len-only update")
+			}
+		}
+		if i.Op == OREGISTER {
+			Regfree(&i)
+		}
+		// Write len (and cap if needed) back to x.
+		x.Xoffset += int64(Widthptr)
+		x.Type = Types[TUINT]
+		Thearch.Gmove(&j, &x)
+		x.Xoffset -= int64(Widthptr)
+		if k.Op != 0 {
+			x.Xoffset += 2 * int64(Widthptr)
+			x.Type = Types[TUINT]
+			Thearch.Gmove(&k, &x)
+			x.Xoffset -= 2 * int64(Widthptr)
+		}
+		Regfree(&x)
+	} else {
+		// Compute new base. May smash i.
+		if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
+			Cgenr(n.Left, &xbase, nil)
+			Cgen_checknil(&xbase)
+		} else {
+			regalloc(&xbase, Ptrto(res.Type.Type), nil)
+			x.Type = xbase.Type
+			Thearch.Gmove(&x, &xbase)
+			Regfree(&x)
+		}
+		if i.Op != 0 && adjustBase {
+			// Branch around the base adjustment if the resulting cap will be 0.
+			var p *obj.Prog
+			size := &k
+			if k.Op == 0 {
+				size = &j
+			}
+			if Isconst(size, CTINT) {
+				// zero was checked above, must be non-zero.
+			} else {
+				var tmp Node
+				Nodconst(&tmp, indexRegType, 0)
+				p = Thearch.Ginscmp(OEQ, indexRegType, size, &tmp, -1)
+			}
+			var w int64
+			if n.Op == OSLICESTR {
+				w = 1 // res is string, elem size is 1 (byte)
+			} else {
+				w = res.Type.Type.Width // res is []T, elem size is T.width
+			}
+			if Isconst(&i, CTINT) {
+				ginscon(Thearch.Optoas(OADD, xbase.Type), Mpgetfix(i.Val.U.Xval)*w, &xbase)
+			} else if Thearch.AddIndex != nil && Thearch.AddIndex(&i, w, &xbase) {
+				// done by back end
+			} else if w == 1 {
+				gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase)
+			} else {
+				if i.Op == ONAME && !istemp(&i) {
+					var tmp Node
+					Tempname(&tmp, i.Type)
+					Thearch.Gmove(&i, &tmp)
+					i = tmp
+				}
+				ginscon(Thearch.Optoas(OMUL, i.Type), w, &i)
+				gins(Thearch.Optoas(OADD, xbase.Type), &i, &xbase)
+			}
+			if p != nil {
+				Patch(p, Pc)
+			}
+		}
+		if i.Op == OREGISTER {
+			Regfree(&i)
+		}
+
+		// Write len, cap, base to result.
+		if res.Op == ONAME {
+			Gvardef(res)
+		}
+		Igen(res, &x, nil)
+		x.Xoffset += int64(Widthptr)
+		x.Type = Types[TUINT]
+		Thearch.Gmove(&j, &x)
+		x.Xoffset -= int64(Widthptr)
+		if k.Op != 0 {
+			x.Xoffset += 2 * int64(Widthptr)
+			Thearch.Gmove(&k, &x)
+			x.Xoffset -= 2 * int64(Widthptr)
+		}
+		x.Type = xbase.Type
+		cgen_wb(&xbase, &x, wb)
+		Regfree(&xbase)
+		Regfree(&x)
+	}
+
+	if j.Op == OREGISTER {
+		Regfree(&j)
+	}
+	if k.Op == OREGISTER {
+		Regfree(&k)
+	}
+}
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go
index 76e9a82392..a9c348da2d 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/internal/gc/gen.go
@@ -551,122 +551,6 @@ func Cgen_As2dottype(n, res, resok *Node) {
 	Patch(q, Pc)
 }
 
-/*
- * generate:
- *	res = s[lo, hi];
- * n->left is s
- * n->list is (cap(s)-lo(TUINT), hi-lo(TUINT)[, lo*width(TUINTPTR)])
- * caller (cgen) guarantees res is an addable ONAME.
- *
- * called for OSLICE, OSLICE3, OSLICEARR, OSLICE3ARR, OSLICESTR.
- */
-func Cgen_slice(n *Node, res *Node) {
-	cap := n.List.N
-	len := n.List.Next.N
-	var offs *Node
-	if n.List.Next.Next != nil {
-		offs = n.List.Next.Next.N
-	}
-
-	// evaluate base pointer first, because it is the only
-	// possibly complex expression. once that is evaluated
-	// and stored, updating the len and cap can be done
-	// without making any calls, so without doing anything that
-	// might cause preemption or garbage collection.
-	// this makes the whole slice update atomic as far as the
-	// garbage collector can see.
-	base := temp(Types[TUINTPTR])
-
-	tmplen := temp(Types[TINT])
-	var tmpcap *Node
-	if n.Op != OSLICESTR {
-		tmpcap = temp(Types[TINT])
-	} else {
-		tmpcap = tmplen
-	}
-
-	var src Node
-	if isnil(n.Left) {
-		Tempname(&src, n.Left.Type)
-		Cgen(n.Left, &src)
-	} else {
-		src = *n.Left
-	}
-	if n.Op == OSLICE || n.Op == OSLICE3 || n.Op == OSLICESTR {
-		src.Xoffset += int64(Array_array)
-	}
-
-	if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
-		if !Isptr[n.Left.Type.Etype] {
-			Fatal("slicearr is supposed to work on pointer: %v\n", Nconv(n, obj.FmtSign))
-		}
-		Cgen(&src, base)
-		Cgen_checknil(base)
-	} else {
-		src.Type = Types[Tptr]
-		Cgen(&src, base)
-	}
-
-	// committed to the update
-	Gvardef(res)
-
-	// compute len and cap.
-	// len = n-i, cap = m-i, and offs = i*width.
-	// computing offs last lets the multiply overwrite i.
-	Cgen((*Node)(len), tmplen)
-
-	if n.Op != OSLICESTR {
-		Cgen(cap, tmpcap)
-	}
-
-	// if new cap != 0 { base += add }
-	// This avoids advancing base past the end of the underlying array/string,
-	// so that it cannot point at the next object in memory.
-	// If cap == 0, the base doesn't matter except insofar as it is 0 or non-zero.
-	// In essence we are replacing x[i:j:k] where i == j == k
-	// or x[i:j] where i == j == cap(x) with x[0:0:0].
-	if offs != nil {
-		p1 := gjmp(nil)
-		p2 := gjmp(nil)
-		Patch(p1, Pc)
-
-		var con Node
-		Nodconst(&con, tmpcap.Type, 0)
-		cmp := Nod(OEQ, tmpcap, &con)
-		typecheck(&cmp, Erv)
-		Bgen(cmp, true, -1, p2)
-
-		add := Nod(OADD, base, offs)
-		typecheck(&add, Erv)
-		Cgen(add, base)
-
-		Patch(p2, Pc)
-	}
-
-	// dst.array = src.array  [ + lo *width ]
-	dst := *res
-
-	dst.Xoffset += int64(Array_array)
-	dst.Type = Types[Tptr]
-	Cgen(base, &dst)
-
-	// dst.len = hi [ - lo ]
-	dst = *res
-
-	dst.Xoffset += int64(Array_nel)
-	dst.Type = Types[Simtype[TUINT]]
-	Cgen(tmplen, &dst)
-
-	if n.Op != OSLICESTR {
-		// dst.cap = cap [ - lo ]
-		dst = *res
-
-		dst.Xoffset += int64(Array_cap)
-		dst.Type = Types[Simtype[TUINT]]
-		Cgen(tmpcap, &dst)
-	}
-}
-
 /*
  * gather series of offsets
  * >=0 is direct addressed field
diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/internal/gc/lex.go
index be95138b6a..96a1a01aaa 100644
--- a/src/cmd/internal/gc/lex.go
+++ b/src/cmd/internal/gc/lex.go
@@ -38,6 +38,7 @@ var goroot string
 var (
 	Debug_wb     int
 	Debug_append int
+	Debug_slice  int
 )
 
 // Debug arguments.
@@ -53,6 +54,7 @@ var debugtab = []struct {
 	{"disablenil", &Disable_checknil}, // disable nil checks
 	{"wb", &Debug_wb},                 // print information about write barriers
 	{"append", &Debug_append},         // print information about append compilation
+	{"slice", &Debug_slice},           // print information about slice compilation
 }
 
 // Our own isdigit, isspace, isalpha, isalnum that take care
diff --git a/src/cmd/internal/gc/order.go b/src/cmd/internal/gc/order.go
index 5de2aa391c..06a1490be4 100644
--- a/src/cmd/internal/gc/order.go
+++ b/src/cmd/internal/gc/order.go
@@ -104,9 +104,23 @@ func ordercopyexpr(n *Node, t *Type, order *Order, clear int) *Node {
 // If not, ordercheapexpr allocates a new tmp, emits tmp = n,
 // and then returns tmp.
 func ordercheapexpr(n *Node, order *Order) *Node {
+	if n == nil {
+		return nil
+	}
 	switch n.Op {
 	case ONAME, OLITERAL:
 		return n
+	case OLEN, OCAP:
+		l := ordercheapexpr(n.Left, order)
+		if l == n.Left {
+			return n
+		}
+		a := Nod(OXXX, nil, nil)
+		*a = *n
+		a.Orig = a
+		a.Left = l
+		typecheck(&a, Erv)
+		return a
 	}
 
 	return ordercopyexpr(n, n.Type, order, 0)
@@ -124,7 +138,7 @@ func ordersafeexpr(n *Node, order *Order) *Node {
 	case ONAME, OLITERAL:
 		return n
 
-	case ODOT:
+	case ODOT, OLEN, OCAP:
 		l := ordersafeexpr(n.Left, order)
 		if l == n.Left {
 			return n
@@ -1080,6 +1094,28 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
 			n = ordercopyexpr(n, n.Type, order, 0)
 		}
 
+	case OSLICE, OSLICEARR, OSLICESTR:
+		orderexpr(&n.Left, order, nil)
+		orderexpr(&n.Right.Left, order, nil)
+		n.Right.Left = ordercheapexpr(n.Right.Left, order)
+		orderexpr(&n.Right.Right, order, nil)
+		n.Right.Right = ordercheapexpr(n.Right.Right, order)
+		if lhs == nil || flag_race != 0 || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
+			n = ordercopyexpr(n, n.Type, order, 0)
+		}
+
+	case OSLICE3, OSLICE3ARR:
+		orderexpr(&n.Left, order, nil)
+		orderexpr(&n.Right.Left, order, nil)
+		n.Right.Left = ordercheapexpr(n.Right.Left, order)
+		orderexpr(&n.Right.Right.Left, order, nil)
+		n.Right.Right.Left = ordercheapexpr(n.Right.Right.Left, order)
+		orderexpr(&n.Right.Right.Right, order, nil)
+		n.Right.Right.Right = ordercheapexpr(n.Right.Right.Right, order)
+		if lhs == nil || flag_race != 0 || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) {
+			n = ordercopyexpr(n, n.Type, order, 0)
+		}
+
 	case OCLOSURE:
 		if n.Noescape && n.Func.Cvars != nil {
 			n.Alloc = ordertemp(Types[TUINT8], order, false) // walk will fill in correct type
diff --git a/src/cmd/internal/gc/racewalk.go b/src/cmd/internal/gc/racewalk.go
index e7f35006dc..446ec038c8 100644
--- a/src/cmd/internal/gc/racewalk.go
+++ b/src/cmd/internal/gc/racewalk.go
@@ -324,9 +324,8 @@ func racewalknode(np **Node, init **NodeList, wr int, skip int) {
 		}
 		goto ret
 
-		// Seems to only lead to double instrumentation.
-	//racewalknode(&n->left, init, 0, 0);
 	case OSLICE, OSLICEARR, OSLICE3, OSLICE3ARR:
+		racewalknode(&n.Left, init, 0, 0)
 		goto ret
 
 	case OADDR:
diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/internal/gc/subr.go
index 06ceff5844..dd84214dc8 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/internal/gc/subr.go
@@ -1898,7 +1898,7 @@ func safeexpr(n *Node, init **NodeList) *Node {
 	case ONAME, OLITERAL:
 		return n
 
-	case ODOT:
+	case ODOT, OLEN, OCAP:
 		l := safeexpr(n.Left, init)
 		if l == n.Left {
 			return n
diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go
index bef08ae252..495acb1861 100644
--- a/src/cmd/internal/gc/walk.go
+++ b/src/cmd/internal/gc/walk.go
@@ -1280,56 +1280,39 @@ func walkexpr(np **Node, init **NodeList) {
 	case ORECV:
 		Fatal("walkexpr ORECV") // should see inside OAS only
 
-	case OSLICE:
-		if n.Right != nil && n.Right.Left == nil && n.Right.Right == nil { // noop
-			walkexpr(&n.Left, init)
-			n = n.Left
-			goto ret
-		}
-		fallthrough
-
-	case OSLICEARR, OSLICESTR:
-		if n.Right == nil { // already processed
-			goto ret
-		}
-
+	case OSLICE, OSLICEARR, OSLICESTR:
 		walkexpr(&n.Left, init)
-
-		// cgen_slice can't handle string literals as source
-		// TODO the OINDEX case is a bug elsewhere that needs to be traced.  it causes a crash on ([2][]int{ ... })[1][lo:hi]
-		if (n.Op == OSLICESTR && n.Left.Op == OLITERAL) || (n.Left.Op == OINDEX) {
-			n.Left = copyexpr(n.Left, n.Left.Type, init)
-		} else {
-			n.Left = safeexpr(n.Left, init)
-		}
 		walkexpr(&n.Right.Left, init)
-		n.Right.Left = safeexpr(n.Right.Left, init)
+		if n.Right.Left != nil && iszero(n.Right.Left) {
+			// Reduce x[0:j] to x[:j].
+			n.Right.Left = nil
+		}
 		walkexpr(&n.Right.Right, init)
-		n.Right.Right = safeexpr(n.Right.Right, init)
-		n = sliceany(n, init) // chops n.Right, sets n.List
+		n = reduceSlice(n)
 		goto ret
 
 	case OSLICE3, OSLICE3ARR:
-		if n.Right == nil { // already processed
+		walkexpr(&n.Left, init)
+		walkexpr(&n.Right.Left, init)
+		if n.Right.Left != nil && iszero(n.Right.Left) {
+			// Reduce x[0:j:k] to x[:j:k].
+			n.Right.Left = nil
+		}
+		walkexpr(&n.Right.Right.Left, init)
+		walkexpr(&n.Right.Right.Right, init)
+
+		r := n.Right.Right.Right
+		if r != nil && r.Op == OCAP && samesafeexpr(n.Left, r.Left) {
+			// Reduce x[i:j:cap(x)] to x[i:j].
+			n.Right.Right = n.Right.Right.Left
+			if n.Op == OSLICE3 {
+				n.Op = OSLICE
+			} else {
+				n.Op = OSLICEARR
+			}
+			n = reduceSlice(n)
 			goto ret
 		}
-
-		walkexpr(&n.Left, init)
-
-		// TODO the OINDEX case is a bug elsewhere that needs to be traced.  it causes a crash on ([2][]int{ ... })[1][lo:hi]
-		// TODO the comment on the previous line was copied from case OSLICE. it might not even be true.
-		if n.Left.Op == OINDEX {
-			n.Left = copyexpr(n.Left, n.Left.Type, init)
-		} else {
-			n.Left = safeexpr(n.Left, init)
-		}
-		walkexpr(&n.Right.Left, init)
-		n.Right.Left = safeexpr(n.Right.Left, init)
-		walkexpr(&n.Right.Right.Left, init)
-		n.Right.Right.Left = safeexpr(n.Right.Right.Left, init)
-		walkexpr(&n.Right.Right.Right, init)
-		n.Right.Right.Right = safeexpr(n.Right.Right.Right, init)
-		n = sliceany(n, init) // chops n.Right, sets n.List
 		goto ret
 
 	case OADDR:
@@ -1660,6 +1643,22 @@ ret:
 	*np = n
 }
 
+func reduceSlice(n *Node) *Node {
+	r := n.Right.Right
+	if r != nil && r.Op == OLEN && samesafeexpr(n.Left, r.Left) {
+		// Reduce x[i:len(x)] to x[i:].
+		n.Right.Right = nil
+	}
+	if (n.Op == OSLICE || n.Op == OSLICESTR) && n.Right.Left == nil && n.Right.Right == nil {
+		// Reduce x[:] to x.
+		if Debug_slice > 0 {
+			Warn("slice: omit slice operation")
+		}
+		return n.Left
+	}
+	return n
+}
+
 func ascompatee1(op int, l *Node, r *Node, init **NodeList) *Node {
 	// convas will turn map assigns into function calls,
 	// making it impossible for reorder3 to work.
@@ -3178,213 +3177,6 @@ func copyany(n *Node, init **NodeList, runtimecall int) *Node {
 	return nlen
 }
 
-// Generate frontend part for OSLICE[3][ARR|STR]
-//
-func sliceany(n *Node, init **NodeList) *Node {
-	var hb *Node
-	var cb *Node
-
-	//	print("before sliceany: %+N\n", n);
-
-	src := n.Left
-
-	lb := n.Right.Left
-	slice3 := n.Op == OSLICE3 || n.Op == OSLICE3ARR
-	if slice3 {
-		hb = n.Right.Right.Left
-		cb = n.Right.Right.Right
-	} else {
-		hb = n.Right.Right
-		cb = nil
-	}
-
-	bounded := int(n.Etype)
-
-	var bound *Node
-	if n.Op == OSLICESTR {
-		bound = Nod(OLEN, src, nil)
-	} else {
-		bound = Nod(OCAP, src, nil)
-	}
-
-	typecheck(&bound, Erv)
-	walkexpr(&bound, init) // if src is an array, bound will be a const now.
-
-	// static checks if possible
-	bv := int64(1 << 50)
-
-	if Isconst(bound, CTINT) {
-		if !Smallintconst(bound) {
-			Yyerror("array len too large")
-		} else {
-			bv = Mpgetfix(bound.Val.U.Xval)
-		}
-	}
-
-	if Isconst(cb, CTINT) {
-		cbv := Mpgetfix(cb.Val.U.Xval)
-		if cbv < 0 || cbv > bv {
-			Yyerror("slice index out of bounds")
-		}
-	}
-
-	if Isconst(hb, CTINT) {
-		hbv := Mpgetfix(hb.Val.U.Xval)
-		if hbv < 0 || hbv > bv {
-			Yyerror("slice index out of bounds")
-		}
-	}
-
-	if Isconst(lb, CTINT) {
-		lbv := Mpgetfix(lb.Val.U.Xval)
-		if lbv < 0 || lbv > bv {
-			Yyerror("slice index out of bounds")
-			lbv = -1
-		}
-
-		if lbv == 0 {
-			lb = nil
-		}
-	}
-
-	// Checking src[lb:hb:cb] or src[lb:hb].
-	// if chk0 || chk1 || chk2 { panicslice() }
-
-	// All comparisons are unsigned to avoid testing < 0.
-	bt := Types[Simtype[TUINT]]
-
-	if cb != nil && cb.Type.Width > 4 {
-		bt = Types[TUINT64]
-	}
-	if hb != nil && hb.Type.Width > 4 {
-		bt = Types[TUINT64]
-	}
-	if lb != nil && lb.Type.Width > 4 {
-		bt = Types[TUINT64]
-	}
-
-	bound = cheapexpr(conv(bound, bt), init)
-
-	var chk0 *Node // cap(src) < cb
-	if cb != nil {
-		cb = cheapexpr(conv(cb, bt), init)
-		if bounded == 0 {
-			chk0 = Nod(OLT, bound, cb)
-		}
-	} else if slice3 {
-		// When we figure out what this means, implement it.
-		Fatal("slice3 with cb == N") // rejected by parser
-	}
-
-	var chk1 *Node // cb < hb for src[lb:hb:cb]; cap(src) < hb for src[lb:hb]
-	if hb != nil {
-		hb = cheapexpr(conv(hb, bt), init)
-		if bounded == 0 {
-			if cb != nil {
-				chk1 = Nod(OLT, cb, hb)
-			} else {
-				chk1 = Nod(OLT, bound, hb)
-			}
-		}
-	} else if slice3 {
-		// When we figure out what this means, implement it.
-		Fatal("slice3 with hb == N") // rejected by parser
-	} else if n.Op == OSLICEARR {
-		hb = bound
-	} else {
-		hb = Nod(OLEN, src, nil)
-		typecheck(&hb, Erv)
-		walkexpr(&hb, init)
-		hb = cheapexpr(conv(hb, bt), init)
-	}
-
-	var chk2 *Node // hb < lb
-	if lb != nil {
-		lb = cheapexpr(conv(lb, bt), init)
-		if bounded == 0 {
-			chk2 = Nod(OLT, hb, lb)
-		}
-	}
-
-	if chk0 != nil || chk1 != nil || chk2 != nil {
-		chk := Nod(OIF, nil, nil)
-		chk.Nbody = list1(mkcall("panicslice", nil, init))
-		chk.Likely = -1
-		if chk0 != nil {
-			chk.Ntest = chk0
-		}
-		if chk1 != nil {
-			if chk.Ntest == nil {
-				chk.Ntest = chk1
-			} else {
-				chk.Ntest = Nod(OOROR, chk.Ntest, chk1)
-			}
-		}
-
-		if chk2 != nil {
-			if chk.Ntest == nil {
-				chk.Ntest = chk2
-			} else {
-				chk.Ntest = Nod(OOROR, chk.Ntest, chk2)
-			}
-		}
-
-		typecheck(&chk, Etop)
-		walkstmt(&chk)
-		*init = concat(*init, chk.Ninit)
-		chk.Ninit = nil
-		*init = list(*init, chk)
-	}
-
-	// prepare new cap, len and offs for backend cgen_slice
-	// cap = bound [ - lo ]
-	n.Right = nil
-
-	n.List = nil
-	if !slice3 {
-		cb = bound
-	}
-	if lb == nil {
-		bound = conv(cb, Types[Simtype[TUINT]])
-	} else {
-		bound = Nod(OSUB, conv(cb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]]))
-	}
-	typecheck(&bound, Erv)
-	walkexpr(&bound, init)
-	n.List = list(n.List, bound)
-
-	// len = hi [ - lo]
-	if lb == nil {
-		hb = conv(hb, Types[Simtype[TUINT]])
-	} else {
-		hb = Nod(OSUB, conv(hb, Types[Simtype[TUINT]]), conv(lb, Types[Simtype[TUINT]]))
-	}
-	typecheck(&hb, Erv)
-	walkexpr(&hb, init)
-	n.List = list(n.List, hb)
-
-	// offs = [width *] lo, but omit if zero
-	if lb != nil {
-		var w int64
-		if n.Op == OSLICESTR {
-			w = 1
-		} else {
-			w = n.Type.Type.Width
-		}
-		lb = conv(lb, Types[TUINTPTR])
-		if w > 1 {
-			lb = Nod(OMUL, Nodintconst(w), lb)
-		}
-		typecheck(&lb, Erv)
-		walkexpr(&lb, init)
-		n.List = list(n.List, lb)
-	}
-
-	//	print("after sliceany: %+N\n", n);
-
-	return n
-}
-
 func eqfor(t *Type, needsize *int) *Node {
 	// Should only arrive here with large memory or
 	// a struct/array containing a non-memory field/element.
diff --git a/test/sliceopt.go b/test/sliceopt.go
index dc30717ebf..c9d089f7d2 100644
--- a/test/sliceopt.go
+++ b/test/sliceopt.go
@@ -1,10 +1,10 @@
-// errorcheck -0 -d=append
+// errorcheck -0 -d=append,slice
 
 // Copyright 2015 The Go Authors.  All rights reserved.
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Check optimization results for append.
+// Check optimization results for append and slicing.
 
 package main
 
@@ -20,3 +20,40 @@ func a2(x []int, y int) []int {
 func a3(x *[]int, y int) {
 	*x = append(*x, y) // ERROR "append: len-only update"
 }
+
+func s1(x **[]int, xs **string, i, j int) {
+	var z []int
+	z = (**x)[2:]         // ERROR "slice: omit check for 2nd index"
+	z = (**x)[2:len(**x)] // not yet: "slice: reuse len" "slice: omit check for 2nd index"
+	z = (**x)[2:cap(**x)] // not yet: "slice: reuse cap" "slice: omit check for 2nd index"
+	z = (**x)[i:i]        // ERROR "slice: reuse 1st index" "slice: omit check for 1st index" "slice: result len == 0"
+	z = (**x)[1:i:i]      // ERROR "slice: reuse 2nd index" "slice: omit check for 2nd index" "slice: result cap == result len"
+	z = (**x)[i:j:0]      // ERROR "slice: omit check for 3rd index"
+	z = (**x)[i:0:j]      // ERROR "slice: omit check for 2nd index"
+	z = (**x)[0:i:j]      // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
+	z = (**x)[0:]         // ERROR "slice: omit slice operation"
+	z = (**x)[2:8]        // ERROR "slice: omit check for 1st index" "slice: result len == 6"
+	z = (**x)[2:2]        // ERROR "slice: omit check for 1st index" "slice: result len == 0"
+	z = (**x)[0:i]        // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
+	z = (**x)[2:i:8]      // ERROR "slice: result cap == 6"
+	z = (**x)[i:2:i]      // ERROR "slice: reuse 1st index" "slice: result cap == 0" "slice: skip base adjustment for cap == 0"
+
+	z = z[0:i]       // ERROR "slice: omit check for 1st index" "slice: result cap not computed" "slice: skip base adjustment for 1st index 0" "slice: len-only update"
+	z = z[0:i : i+1] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0" "slice: len/cap-only update"
+	z = z[i : i+1]
+
+	println(z)
+
+	var zs string
+	zs = (**xs)[2:]          // ERROR "slice: omit check for 2nd index"
+	zs = (**xs)[2:len(**xs)] // not yet: "slice: reuse len" "slice: omit check for 2nd index"
+	zs = (**xs)[i:i]         // ERROR "slice: reuse 1st index" "slice: omit check for 1st index" "slice: result len == 0" "slice: skip base adjustment for string len == 0"
+	zs = (**xs)[0:]          // ERROR "slice: omit slice operation"
+	zs = (**xs)[2:8]         // ERROR "slice: omit check for 1st index" "slice: result len == 6"
+	zs = (**xs)[2:2]         // ERROR "slice: omit check for 1st index" "slice: result len == 0" "slice: skip base adjustment for string len == 0"
+	zs = (**xs)[0:i]         // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0"
+
+	zs = zs[0:i] // ERROR "slice: omit check for 1st index" "slice: skip base adjustment for 1st index 0" "slice: len-only update"
+	zs = zs[i : i+1]
+	println(zs)
+}

From 6f2c0f1585e50bf2d8bcc11058373e38f5321227 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Tue, 12 May 2015 10:01:37 -0700
Subject: [PATCH 067/232] runtime: add check for malloc in a signal handler

Change-Id: Ic8ebbe81eb788626c01bfab238d54236e6e5ef2b
Reviewed-on: https://go-review.googlesource.com/9964
Run-TryBot: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
Reviewed-by: Russ Cox 
---
 src/runtime/malloc.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/runtime/malloc.go b/src/runtime/malloc.go
index a0cd8bb433..2d7e55643f 100644
--- a/src/runtime/malloc.go
+++ b/src/runtime/malloc.go
@@ -509,6 +509,9 @@ func mallocgc(size uintptr, typ *_type, flags uint32) unsafe.Pointer {
 	if mp.mallocing != 0 {
 		throw("malloc deadlock")
 	}
+	if mp.gsignal == getg() {
+		throw("malloc during signal")
+	}
 	mp.mallocing = 1
 
 	shouldhelpgc := false

From 1e26df40fa2ee41da971338ab25299e27a221704 Mon Sep 17 00:00:00 2001
From: Rob Pike 
Date: Mon, 11 May 2015 13:31:05 -0700
Subject: [PATCH 068/232] cmd/doc: print BUGs after package docs

Was otherwise absent unless bound to an exported symbol,
as in the BUG with strings.Title.

Fixes #10781.

Change-Id: I1543137073a9dee9e546bc9d648ca54fc9632dde
Reviewed-on: https://go-review.googlesource.com/9899
Reviewed-by: Russ Cox 
---
 src/cmd/doc/pkg.go | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 3a0aa7ff89..835313e902 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -190,6 +190,7 @@ func (pkg *Package) packageDoc() {
 	pkg.valueSummary(pkg.doc.Vars)
 	pkg.funcSummary(pkg.doc.Funcs)
 	pkg.typeSummary()
+	pkg.bugs()
 }
 
 // packageClause prints the package clause.
@@ -253,6 +254,18 @@ func (pkg *Package) typeSummary() {
 	}
 }
 
+// bugs prints the BUGS information for the package.
+// TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)?
+func (pkg *Package) bugs() {
+	if pkg.doc.Notes["BUG"] == nil {
+		return
+	}
+	pkg.Printf("\n")
+	for _, note := range pkg.doc.Notes["BUG"] {
+		pkg.Printf("%s: %v\n", "BUG", note.Body)
+	}
+}
+
 // findValues finds the doc.Values that describe the symbol.
 func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) {
 	for _, value := range docValues {

From 647026a16ba7468855b9f83ab796080708879516 Mon Sep 17 00:00:00 2001
From: "Hyang-Ah (Hana) Kim" 
Date: Wed, 13 May 2015 17:26:19 -0400
Subject: [PATCH 069/232] misc/cgo/testcshared: remove use of 'env'.

'env' command is not available on some android devices.

Change-Id: I68b1152ef7ea248c8e80c7f71e97da76e3ec6394
Reviewed-on: https://go-review.googlesource.com/9999
Reviewed-by: Minux Ma 
---
 misc/cgo/testcshared/test.bash | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/misc/cgo/testcshared/test.bash b/misc/cgo/testcshared/test.bash
index ed437577c7..492d25e134 100755
--- a/misc/cgo/testcshared/test.bash
+++ b/misc/cgo/testcshared/test.bash
@@ -46,7 +46,7 @@ function run() {
 	case "$goos" in
 	"android")
 		local args=$@
-		output=$(adb shell "cd ${androidpath}; env $@")
+		output=$(adb shell "cd ${androidpath}; $@")
 		output=$(echo $output|tr -d '\r')
 		case $output in
 			*PASS) echo "PASS";; 

From 645e77ef10ef2367ab07669dc4e8be5b54d36fe7 Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Wed, 13 May 2015 14:06:43 +0900
Subject: [PATCH 070/232] net/internal/socktest: fix data race

Fixes #10796.

Change-Id: Ifcd2e771c64114e210fbfc5efaaceb53c534f745
Reviewed-on: https://go-review.googlesource.com/10007
Reviewed-by: Brad Fitzpatrick 
---
 src/net/internal/socktest/main_test.go      | 16 ++++++++++++++++
 src/net/internal/socktest/switch.go         | 14 +++++++-------
 src/net/internal/socktest/switch_unix.go    |  2 +-
 src/net/internal/socktest/switch_windows.go |  2 +-
 src/net/internal/socktest/sys_unix.go       |  2 ++
 src/net/internal/socktest/sys_windows.go    |  2 ++
 6 files changed, 29 insertions(+), 9 deletions(-)

diff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go
index 3ae1c6be3c..60e581f463 100644
--- a/src/net/internal/socktest/main_test.go
+++ b/src/net/internal/socktest/main_test.go
@@ -9,6 +9,7 @@ package socktest_test
 import (
 	"net/internal/socktest"
 	"os"
+	"sync"
 	"syscall"
 	"testing"
 )
@@ -27,6 +28,21 @@ func TestMain(m *testing.M) {
 	os.Exit(st)
 }
 
+func TestSwitch(t *testing.T) {
+	const N = 10
+	var wg sync.WaitGroup
+	wg.Add(N)
+	for i := 0; i < N; i++ {
+		go func() {
+			defer wg.Done()
+			for _, family := range []int{syscall.AF_INET, syscall.AF_INET6} {
+				socketFunc(family, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
+			}
+		}()
+	}
+	wg.Wait()
+}
+
 func TestSocket(t *testing.T) {
 	for _, f := range []socktest.Filter{
 		func(st *socktest.Status) (socktest.AfterFilter, error) { return nil, nil },
diff --git a/src/net/internal/socktest/switch.go b/src/net/internal/socktest/switch.go
index 5e558a2de3..4e38c7a85f 100644
--- a/src/net/internal/socktest/switch.go
+++ b/src/net/internal/socktest/switch.go
@@ -10,12 +10,6 @@ import (
 	"sync"
 )
 
-func switchInit(sw *Switch) {
-	sw.fltab = make(map[FilterType]Filter)
-	sw.sotab = make(Sockets)
-	sw.stats = make(stats)
-}
-
 // A Switch represents a callpath point switch for socket system
 // calls.
 type Switch struct {
@@ -29,6 +23,12 @@ type Switch struct {
 	stats stats
 }
 
+func (sw *Switch) init() {
+	sw.fltab = make(map[FilterType]Filter)
+	sw.sotab = make(Sockets)
+	sw.stats = make(stats)
+}
+
 // Stats returns a list of per-cookie socket statistics.
 func (sw *Switch) Stats() []Stat {
 	var st []Stat
@@ -162,7 +162,7 @@ func (f AfterFilter) apply(st *Status) error {
 
 // Set deploys the socket system call filter f for the filter type t.
 func (sw *Switch) Set(t FilterType, f Filter) {
-	sw.once.Do(func() { switchInit(sw) })
+	sw.once.Do(sw.init)
 	sw.fmu.Lock()
 	sw.fltab[t] = f
 	sw.fmu.Unlock()
diff --git a/src/net/internal/socktest/switch_unix.go b/src/net/internal/socktest/switch_unix.go
index 2b89276fa1..14c0c228a2 100644
--- a/src/net/internal/socktest/switch_unix.go
+++ b/src/net/internal/socktest/switch_unix.go
@@ -22,7 +22,7 @@ func (sw *Switch) sockso(s int) *Status {
 // addLocked returns a new Status without locking.
 // sw.smu must be held before call.
 func (sw *Switch) addLocked(s, family, sotype, proto int) *Status {
-	sw.once.Do(func() { switchInit(sw) })
+	sw.once.Do(sw.init)
 	so := Status{Cookie: cookie(family, sotype, proto)}
 	sw.sotab[s] = so
 	return &so
diff --git a/src/net/internal/socktest/switch_windows.go b/src/net/internal/socktest/switch_windows.go
index 3cee49ba0b..4f1d597a27 100644
--- a/src/net/internal/socktest/switch_windows.go
+++ b/src/net/internal/socktest/switch_windows.go
@@ -22,7 +22,7 @@ func (sw *Switch) sockso(s syscall.Handle) *Status {
 // addLocked returns a new Status without locking.
 // sw.smu must be held before call.
 func (sw *Switch) addLocked(s syscall.Handle, family, sotype, proto int) *Status {
-	sw.once.Do(func() { switchInit(sw) })
+	sw.once.Do(sw.init)
 	so := Status{Cookie: cookie(family, sotype, proto)}
 	sw.sotab[s] = so
 	return &so
diff --git a/src/net/internal/socktest/sys_unix.go b/src/net/internal/socktest/sys_unix.go
index 4089f8cea2..f983e266f1 100644
--- a/src/net/internal/socktest/sys_unix.go
+++ b/src/net/internal/socktest/sys_unix.go
@@ -10,6 +10,8 @@ import "syscall"
 
 // Socket wraps syscall.Socket.
 func (sw *Switch) Socket(family, sotype, proto int) (s int, err error) {
+	sw.once.Do(sw.init)
+
 	so := &Status{Cookie: cookie(family, sotype, proto)}
 	sw.fmu.RLock()
 	f, _ := sw.fltab[FilterSocket]
diff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go
index 907e01b5a2..07af0e7046 100644
--- a/src/net/internal/socktest/sys_windows.go
+++ b/src/net/internal/socktest/sys_windows.go
@@ -8,6 +8,8 @@ import "syscall"
 
 // Socket wraps syscall.Socket.
 func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {
+	sw.once.Do(sw.init)
+
 	so := &Status{Cookie: cookie(family, sotype, proto)}
 	sw.fmu.RLock()
 	f, _ := sw.fltab[FilterSocket]

From f6d1009431c4d4677a292be5a78f57c239929d4b Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Wed, 13 May 2015 10:06:20 +0900
Subject: [PATCH 071/232] doc: mention net.SocketConn, net.SocketPacketConn in
 go1.5.txt

Change-Id: I6bda19877ae5148ad349cfb8929f1103740422bb
Reviewed-on: https://go-review.googlesource.com/10005
Reviewed-by: Ian Lance Taylor 
---
 doc/go1.5.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/doc/go1.5.txt b/doc/go1.5.txt
index b0602f9b77..571a9f17d0 100644
--- a/doc/go1.5.txt
+++ b/doc/go1.5.txt
@@ -53,6 +53,7 @@ mime: add ExtensionByType (https://golang.org/cl/7444)
 mime/quotedprintable: new package (https://golang.org/cl/5940 + others)
 net: add Source field to OpError (https://go-review.googlesource.com/9231)
 net: fix inconsistent errors (https://golang.org/cl/9236)
+net: add SocketConn, SocketPacketConn (https://golang.org/cl/9275)
 net/http: support for setting trailers from a server Handler (https://golang.org/cl/2157)
 net/http: ignore the Unix epoch time in ServeContent (https://golang.org/cl/7915)
 net/http/cgi: fix REMOTE_ADDR, REMOTE_HOST, add REMOTE_PORT (https://golang.org/cl/4933)

From f8fbcefa6cdd9901d5d9183bf6ad3fed73f1b455 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Wed, 13 May 2015 15:12:59 -0700
Subject: [PATCH 072/232] math/rand: shorten Float32 test for GOARM=5

Fixes #10749

Change-Id: I9d5f6f179fd117b0c358d7c8042daf5985b645c0
Reviewed-on: https://go-review.googlesource.com/10022
Reviewed-by: Dave Cheney 
---
 src/math/rand/rand_test.go | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/src/math/rand/rand_test.go b/src/math/rand/rand_test.go
index ab0dc49b41..c61494f8eb 100644
--- a/src/math/rand/rand_test.go
+++ b/src/math/rand/rand_test.go
@@ -8,6 +8,8 @@ import (
 	"errors"
 	"fmt"
 	"math"
+	"os"
+	"runtime"
 	"testing"
 )
 
@@ -322,10 +324,17 @@ func TestExpTables(t *testing.T) {
 	}
 }
 
-// For issue 6721, the problem came after 7533753 calls, so check 10e6.
 func TestFloat32(t *testing.T) {
+	// For issue 6721, the problem came after 7533753 calls, so check 10e6.
+	num := int(10e6)
+	// But ARM5 floating point emulation is slow (Issue 10749), so
+	// do less for that builder:
+	if testing.Short() && runtime.GOARCH == "arm" && os.Getenv("GOARM") == "5" {
+		num /= 100 // 1.72 seconds instead of 172 seconds
+	}
+
 	r := New(NewSource(1))
-	for ct := 0; ct < 10e6; ct++ {
+	for ct := 0; ct < num; ct++ {
 		f := r.Float32()
 		if f >= 1 {
 			t.Fatal("Float32() should be in range [0,1). ct:", ct, "f:", f)

From fd5b8aa7999e6710e14f4798dcb9e9387247511d Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Wed, 13 May 2015 16:07:33 -0700
Subject: [PATCH 073/232] text/scanner: avoid further reads after EOF

Fixes #10735.

Change-Id: I5c6e424653657c89da176136ac56597c7565abe5
Reviewed-on: https://go-review.googlesource.com/10039
Reviewed-by: Rob Pike 
---
 src/text/scanner/scanner.go      |  6 +++++-
 src/text/scanner/scanner_test.go | 36 +++++++++++++++++++++++++-------
 2 files changed, 33 insertions(+), 9 deletions(-)

diff --git a/src/text/scanner/scanner.go b/src/text/scanner/scanner.go
index d3eadfd7e1..eacc0a2245 100644
--- a/src/text/scanner/scanner.go
+++ b/src/text/scanner/scanner.go
@@ -314,7 +314,9 @@ func (s *Scanner) Next() rune {
 	s.tokPos = -1 // don't collect token text
 	s.Line = 0    // invalidate token position
 	ch := s.Peek()
-	s.ch = s.next()
+	if ch != EOF {
+		s.ch = s.next()
+	}
 	return ch
 }
 
@@ -597,6 +599,8 @@ redo:
 		}
 	default:
 		switch ch {
+		case EOF:
+			break
 		case '"':
 			if s.Mode&ScanStrings != 0 {
 				s.scanString('"')
diff --git a/src/text/scanner/scanner_test.go b/src/text/scanner/scanner_test.go
index aca17b1b27..798bed7e92 100644
--- a/src/text/scanner/scanner_test.go
+++ b/src/text/scanner/scanner_test.go
@@ -619,13 +619,12 @@ func TestPos(t *testing.T) {
 
 type countReader int
 
-func (c *countReader) Read([]byte) (int, error) {
-	*c++
-
+func (r *countReader) Read([]byte) (int, error) {
+	*r++
 	return 0, io.EOF
 }
 
-func TestPeekEOFHandling(t *testing.T) {
+func TestNextEOFHandling(t *testing.T) {
 	var r countReader
 
 	// corner case: empty source
@@ -633,15 +632,36 @@ func TestPeekEOFHandling(t *testing.T) {
 
 	tok := s.Next()
 	if tok != EOF {
-		t.Errorf("EOF not reported")
+		t.Error("1) EOF not reported")
 	}
 
 	tok = s.Peek()
 	if tok != EOF {
-		t.Errorf("EOF not reported")
+		t.Error("2) EOF not reported")
 	}
 
-	if r != 2 {
-		t.Errorf("scanner called Read %d times, not twice", r)
+	if r != 1 {
+		t.Errorf("scanner called Read %d times, not once", r)
+	}
+}
+
+func TestScanEOFHandling(t *testing.T) {
+	var r countReader
+
+	// corner case: empty source
+	s := new(Scanner).Init(&r)
+
+	tok := s.Scan()
+	if tok != EOF {
+		t.Error("1) EOF not reported")
+	}
+
+	tok = s.Peek()
+	if tok != EOF {
+		t.Error("2) EOF not reported")
+	}
+
+	if r != 1 {
+		t.Errorf("scanner called Read %d times, not once", r)
 	}
 }

From 5c7f94421ef5eca55b37f778b427abd5ea174c26 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Wed, 13 May 2015 15:28:11 -0700
Subject: [PATCH 074/232] cmd/internal/obj: validate GOARM environment
 variable's value before use

I was previously setting GOARM=arm5 (due to confusion with previously
seeing buildall.sh's temporary of "arm5" as a GOARCH and
misremembernig), but GOARM=arm5 was acting like GOARM=5 only on
accident. See https://go-review.googlesource.com/#/c/10023/

Instead, fail if GOARM is not a known value.

Change-Id: I9ba4fd7268df233d40b09f0431f37cd85a049847
Reviewed-on: https://go-review.googlesource.com/10024
Reviewed-by: Minux Ma 
---
 src/cmd/internal/obj/util.go | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index b0b209184f..ac49543fdf 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -213,10 +213,17 @@ func Getgoos() string {
 }
 
 func Getgoarm() string {
-	return envOr("GOARM", defaultGOARM)
+	switch v := envOr("GOARM", defaultGOARM); v {
+	case "5", "6", "7":
+		return v
+	}
+	// Fail here, rather than validate at multiple call sites.
+	log.Fatalf("Invalid GOARM value. Must be 5, 6, or 7.")
+	panic("unreachable")
 }
 
 func Getgo386() string {
+	// Validated by cmd/8g.
 	return envOr("GO386", defaultGO386)
 }
 

From e5febf957f5be9d3325c2d851bff6ec7c55e4662 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Wed, 13 May 2015 12:41:56 -0700
Subject: [PATCH 075/232] net/http: flush request body chunks in Transport

The Transport's writer to the remote server is wrapped in a
bufio.Writer to suppress many small writes while writing headers and
trailers. However, when writing the request body, the buffering may get
in the way if the request body is arriving slowly.

Because the io.Copy from the Request.Body to the writer is already
buffered, the outer bufio.Writer is unnecessary and prevents small
Request.Body.Reads from going to the server right away. (and the
io.Reader contract does say to return when you've got something,
instead of blocking waiting for more). After the body is finished, the
Transport's bufio.Writer is still used for any trailers following.

A previous attempted fix for this made the chunk writer always flush
if the underlying type was a bufio.Writer, but that is not quite
correct. This CL instead makes it opt-in by using a private sentinel
type (wrapping a *bufio.Writer) to the chunk writer that requests
Flushes after each chunk body (the chunk header & chunk body are still
buffered together into one write).

Fixes #6574

Change-Id: Icefcdf17130c9e285c80b69af295bfd3e72c3a70
Reviewed-on: https://go-review.googlesource.com/10021
Reviewed-by: Andrew Gerrand 
Run-TryBot: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
---
 src/net/http/internal/chunked.go | 17 +++++-
 src/net/http/transfer.go         |  5 ++
 src/net/http/transport_test.go   | 99 ++++++++++++++++++++++++++++++++
 3 files changed, 119 insertions(+), 2 deletions(-)

diff --git a/src/net/http/internal/chunked.go b/src/net/http/internal/chunked.go
index 9294deb3e5..6d7c69874d 100644
--- a/src/net/http/internal/chunked.go
+++ b/src/net/http/internal/chunked.go
@@ -173,8 +173,12 @@ func (cw *chunkedWriter) Write(data []byte) (n int, err error) {
 		err = io.ErrShortWrite
 		return
 	}
-	_, err = io.WriteString(cw.Wire, "\r\n")
-
+	if _, err = io.WriteString(cw.Wire, "\r\n"); err != nil {
+		return
+	}
+	if bw, ok := cw.Wire.(*FlushAfterChunkWriter); ok {
+		err = bw.Flush()
+	}
 	return
 }
 
@@ -183,6 +187,15 @@ func (cw *chunkedWriter) Close() error {
 	return err
 }
 
+// FlushAfterChunkWriter signals from the caller of NewChunkedWriter
+// that each chunk should be followed by a flush. It is used by the
+// http.Transport code to keep the buffering behavior for headers and
+// trailers, but flush out chunks aggressively in the middle for
+// request bodies which may be generated slowly. See Issue 6574.
+type FlushAfterChunkWriter struct {
+	*bufio.Writer
+}
+
 func parseHexUint(v []byte) (n uint64, err error) {
 	for _, b := range v {
 		n <<= 4
diff --git a/src/net/http/transfer.go b/src/net/http/transfer.go
index 5640344345..289d53dec0 100644
--- a/src/net/http/transfer.go
+++ b/src/net/http/transfer.go
@@ -43,6 +43,7 @@ type transferWriter struct {
 	Close            bool
 	TransferEncoding []string
 	Trailer          Header
+	IsResponse       bool
 }
 
 func newTransferWriter(r interface{}) (t *transferWriter, err error) {
@@ -89,6 +90,7 @@ func newTransferWriter(r interface{}) (t *transferWriter, err error) {
 			}
 		}
 	case *Response:
+		t.IsResponse = true
 		if rr.Request != nil {
 			t.Method = rr.Request.Method
 		}
@@ -206,6 +208,9 @@ func (t *transferWriter) WriteBody(w io.Writer) error {
 	// Write body
 	if t.Body != nil {
 		if chunked(t.TransferEncoding) {
+			if bw, ok := w.(*bufio.Writer); ok && !t.IsResponse {
+				w = &internal.FlushAfterChunkWriter{bw}
+			}
 			cw := internal.NewChunkedWriter(w)
 			_, err = io.Copy(cw, t.Body)
 			if err == nil {
diff --git a/src/net/http/transport_test.go b/src/net/http/transport_test.go
index ace58896b8..ca1a3ab407 100644
--- a/src/net/http/transport_test.go
+++ b/src/net/http/transport_test.go
@@ -23,6 +23,7 @@ import (
 	"net/http/httptest"
 	"net/url"
 	"os"
+	"reflect"
 	"runtime"
 	"strconv"
 	"strings"
@@ -2447,6 +2448,104 @@ func TestTransportDialCancelRace(t *testing.T) {
 	}
 }
 
+// logWritesConn is a net.Conn that logs each Write call to writes
+// and then proxies to w.
+// It proxies Read calls to a reader it receives from rch.
+type logWritesConn struct {
+	net.Conn // nil. crash on use.
+
+	w io.Writer
+
+	rch <-chan io.Reader
+	r   io.Reader // nil until received by rch
+
+	mu     sync.Mutex
+	writes []string
+}
+
+func (c *logWritesConn) Write(p []byte) (n int, err error) {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	c.writes = append(c.writes, string(p))
+	return c.w.Write(p)
+}
+
+func (c *logWritesConn) Read(p []byte) (n int, err error) {
+	if c.r == nil {
+		c.r = <-c.rch
+	}
+	return c.r.Read(p)
+}
+
+func (c *logWritesConn) Close() error { return nil }
+
+// Issue 6574
+func TestTransportFlushesBodyChunks(t *testing.T) {
+	defer afterTest(t)
+	resBody := make(chan io.Reader, 1)
+	connr, connw := io.Pipe() // connection pipe pair
+	lw := &logWritesConn{
+		rch: resBody,
+		w:   connw,
+	}
+	tr := &Transport{
+		Dial: func(network, addr string) (net.Conn, error) {
+			return lw, nil
+		},
+	}
+	bodyr, bodyw := io.Pipe() // body pipe pair
+	go func() {
+		defer bodyw.Close()
+		for i := 0; i < 3; i++ {
+			fmt.Fprintf(bodyw, "num%d\n", i)
+		}
+	}()
+	resc := make(chan *Response)
+	go func() {
+		req, _ := NewRequest("POST", "http://localhost:8080", bodyr)
+		req.Header.Set("User-Agent", "x") // known value for test
+		res, err := tr.RoundTrip(req)
+		if err != nil {
+			t.Error("RoundTrip: %v", err)
+			close(resc)
+			return
+		}
+		resc <- res
+
+	}()
+	// Fully consume the request before checking the Write log vs. want.
+	req, err := ReadRequest(bufio.NewReader(connr))
+	if err != nil {
+		t.Fatal(err)
+	}
+	io.Copy(ioutil.Discard, req.Body)
+
+	// Unblock the transport's roundTrip goroutine.
+	resBody <- strings.NewReader("HTTP/1.1 204 No Content\r\nConnection: close\r\n\r\n")
+	res, ok := <-resc
+	if !ok {
+		return
+	}
+	defer res.Body.Close()
+
+	want := []string{
+		// Because Request.ContentLength = 0, the body is sniffed for 1 byte to determine whether there's content.
+		// That explains the initial "num0" being split into "n" and "um0".
+		// The first byte is included with the request headers Write. Perhaps in the future
+		// we will want to flush the headers out early if the first byte of the request body is
+		// taking a long time to arrive. But not yet.
+		"POST / HTTP/1.1\r\nHost: localhost:8080\r\nUser-Agent: x\r\nTransfer-Encoding: chunked\r\nAccept-Encoding: gzip\r\n\r\n" +
+			"1\r\nn\r\n",
+		"4\r\num0\n\r\n",
+		"5\r\nnum1\n\r\n",
+		"5\r\nnum2\n\r\n",
+		"0\r\n\r\n",
+	}
+	if !reflect.DeepEqual(lw.writes, want) {
+		t.Errorf("Writes differed.\n Got: %q\nWant: %q\n", lw.writes, want)
+	}
+}
+
 func wantBody(res *http.Response, err error, want string) error {
 	if err != nil {
 		return err

From ef54930ebb65f1d611cb321eb86062fd4accc0ff Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Thu, 14 May 2015 09:25:24 +0900
Subject: [PATCH 076/232] net: simplify sync.Once calls in tests

Change-Id: I0c2e1a4a8261887a696e585dda46e72d691191e0
Reviewed-on: https://go-review.googlesource.com/10070
Reviewed-by: Brad Fitzpatrick 
---
 src/net/dnsclient_unix_test.go |  6 +++---
 src/net/dnsname_test.go        |  2 +-
 src/net/interface_test.go      | 12 ++++++------
 src/net/ip_test.go             |  6 +++---
 src/net/main_test.go           |  2 +-
 src/net/tcp_test.go            |  4 ++--
 6 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index 1b88e7762b..4ea24b6014 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -227,7 +227,7 @@ func TestReloadResolvConfChange(t *testing.T) {
 }
 
 func BenchmarkGoLookupIP(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		goLookupIP("www.example.com")
@@ -235,7 +235,7 @@ func BenchmarkGoLookupIP(b *testing.B) {
 }
 
 func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		goLookupIP("some.nonexistent")
@@ -243,7 +243,7 @@ func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
 }
 
 func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	onceLoadConfig.Do(loadDefaultConfig)
 
diff --git a/src/net/dnsname_test.go b/src/net/dnsname_test.go
index cc660c9d42..be07dc6a16 100644
--- a/src/net/dnsname_test.go
+++ b/src/net/dnsname_test.go
@@ -68,7 +68,7 @@ func TestDNSName(t *testing.T) {
 }
 
 func BenchmarkDNSName(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	benchmarks := append(dnsNameTests, []dnsNameTest{
 		{strings.Repeat("a", 63), true},
diff --git a/src/net/interface_test.go b/src/net/interface_test.go
index 0e5c2e3ddf..567d18de44 100644
--- a/src/net/interface_test.go
+++ b/src/net/interface_test.go
@@ -229,7 +229,7 @@ func testMulticastAddrs(t *testing.T, ifmat []Addr) (nmaf4, nmaf6 int) {
 }
 
 func BenchmarkInterfaces(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		if _, err := Interfaces(); err != nil {
@@ -239,7 +239,7 @@ func BenchmarkInterfaces(b *testing.B) {
 }
 
 func BenchmarkInterfaceByIndex(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	ifi := loopbackInterface()
 	if ifi == nil {
@@ -253,7 +253,7 @@ func BenchmarkInterfaceByIndex(b *testing.B) {
 }
 
 func BenchmarkInterfaceByName(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	ifi := loopbackInterface()
 	if ifi == nil {
@@ -267,7 +267,7 @@ func BenchmarkInterfaceByName(b *testing.B) {
 }
 
 func BenchmarkInterfaceAddrs(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		if _, err := InterfaceAddrs(); err != nil {
@@ -277,7 +277,7 @@ func BenchmarkInterfaceAddrs(b *testing.B) {
 }
 
 func BenchmarkInterfacesAndAddrs(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	ifi := loopbackInterface()
 	if ifi == nil {
@@ -291,7 +291,7 @@ func BenchmarkInterfacesAndAddrs(b *testing.B) {
 }
 
 func BenchmarkInterfacesAndMulticastAddrs(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	ifi := loopbackInterface()
 	if ifi == nil {
diff --git a/src/net/ip_test.go b/src/net/ip_test.go
index 24f67cac97..b1939cd08f 100644
--- a/src/net/ip_test.go
+++ b/src/net/ip_test.go
@@ -53,7 +53,7 @@ func TestParseIP(t *testing.T) {
 }
 
 func BenchmarkParseIP(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		for _, tt := range parseIPTests {
@@ -110,7 +110,7 @@ func TestIPString(t *testing.T) {
 }
 
 func BenchmarkIPString(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		for _, tt := range ipStringTests {
@@ -162,7 +162,7 @@ func TestIPMaskString(t *testing.T) {
 }
 
 func BenchmarkIPMaskString(b *testing.B) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	for i := 0; i < b.N; i++ {
 		for _, tt := range ipMaskStringTests {
diff --git a/src/net/main_test.go b/src/net/main_test.go
index 4288e2add2..5e2f3da0e6 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -43,7 +43,7 @@ func TestMain(m *testing.M) {
 
 	st := m.Run()
 
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 	if !testing.Short() {
 		printLeakedGoroutines()
 		printLeakedSockets()
diff --git a/src/net/tcp_test.go b/src/net/tcp_test.go
index 6229df2869..64117449bd 100644
--- a/src/net/tcp_test.go
+++ b/src/net/tcp_test.go
@@ -58,7 +58,7 @@ func BenchmarkTCP6PersistentTimeout(b *testing.B) {
 }
 
 func benchmarkTCP(b *testing.B, persistent, timeout bool, laddr string) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	const msgLen = 512
 	conns := b.N
@@ -168,7 +168,7 @@ func BenchmarkTCP6ConcurrentReadWrite(b *testing.B) {
 }
 
 func benchmarkTCPConcurrentReadWrite(b *testing.B, laddr string) {
-	testHookUninstaller.Do(func() { uninstallTestHooks() })
+	testHookUninstaller.Do(uninstallTestHooks)
 
 	// The benchmark creates GOMAXPROCS client/server pairs.
 	// Each pair creates 4 goroutines: client reader/writer and server reader/writer.

From dbf533a5460d7fcc7d7be77014fd74a8aff8c412 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Wed, 13 May 2015 19:27:59 -0700
Subject: [PATCH 077/232] encoding/json: make BenchmarkSkipValue more stable

BenchmarkSkipValue was sensitive to the value of
b.N due to its significant startup cost.

Two adjacent runs before this CL:

BenchmarkSkipValue	      50	  21047499 ns/op	  93.37 MB/s
BenchmarkSkipValue	     100	  17260554 ns/op	 118.05 MB/s

After this CL, using benchtime to recreate the
difference in b.N:

BenchmarkSkipValue	      50	  15204797 ns/op	 131.67 MB/s
BenchmarkSkipValue	     100	  15332319 ns/op	 130.58 MB/s

Change-Id: Iac86f86dd774d535302fa5e4c08f89f8da00be9e
Reviewed-on: https://go-review.googlesource.com/10053
Reviewed-by: Andrew Gerrand 
---
 src/encoding/json/scanner_test.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/encoding/json/scanner_test.go b/src/encoding/json/scanner_test.go
index 7880342902..66383ef0ef 100644
--- a/src/encoding/json/scanner_test.go
+++ b/src/encoding/json/scanner_test.go
@@ -209,6 +209,7 @@ var benchScan scanner
 
 func BenchmarkSkipValue(b *testing.B) {
 	initBig()
+	b.ResetTimer()
 	for i := 0; i < b.N; i++ {
 		nextValue(jsonBig, &benchScan)
 	}

From b3fb0fdd3f7c35e020788acf6d12278590b416cb Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Wed, 13 May 2015 19:05:50 -0400
Subject: [PATCH 078/232] cmd/internal/gc: fix vet detected printf problems

Fixes #10805.

Change-Id: Ia77639e606a0c18fc53cba9749d92f325014025f
Reviewed-on: https://go-review.googlesource.com/10040
Reviewed-by: Josh Bleecher Snyder 
---
 src/cmd/internal/gc/dcl.go       |   2 +-
 src/cmd/internal/gc/gen.go       |   2 +-
 src/cmd/internal/gc/go.y         |   5 +-
 src/cmd/internal/gc/typecheck.go |   8 +-
 src/cmd/internal/gc/y.go         | 553 +++++++++---------
 src/cmd/internal/gc/y.output     | 972 +++++++++++++++----------------
 6 files changed, 772 insertions(+), 770 deletions(-)

diff --git a/src/cmd/internal/gc/dcl.go b/src/cmd/internal/gc/dcl.go
index 08d2469094..627556eeef 100644
--- a/src/cmd/internal/gc/dcl.go
+++ b/src/cmd/internal/gc/dcl.go
@@ -183,7 +183,7 @@ func declare(n *Node, ctxt uint8) {
 	}
 
 	if ctxt == PEXTERN && s.Name == "init" {
-		Yyerror("cannot declare init - must be func", s)
+		Yyerror("cannot declare init - must be func")
 	}
 
 	gen := 0
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go
index a9c348da2d..fcb2499d3b 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/internal/gc/gen.go
@@ -965,7 +965,7 @@ func cgen_callmeth(n *Node, proc int) {
 	l := n.Left
 
 	if l.Op != ODOTMETH {
-		Fatal("cgen_callmeth: not dotmethod: %v")
+		Fatal("cgen_callmeth: not dotmethod: %v", l)
 	}
 
 	n2 := *n
diff --git a/src/cmd/internal/gc/go.y b/src/cmd/internal/gc/go.y
index f1904b0085..bbee200889 100644
--- a/src/cmd/internal/gc/go.y
+++ b/src/cmd/internal/gc/go.y
@@ -21,6 +21,7 @@
 package gc
 
 import (
+	"fmt"
 	"strings"
 )
 %}
@@ -1975,9 +1976,9 @@ hidden_import:
 		importlist = list(importlist, $2);
 
 		if Debug['E'] > 0 {
-			print("import [%q] func %lN \n", importpkg.Path, $2);
+			fmt.Printf("import [%q] func %v \n", importpkg.Path, $2)
 			if Debug['m'] > 2 && $2.Func.Inl != nil {
-				print("inl body:%+H\n", $2.Func.Inl);
+				fmt.Printf("inl body:%v\n", $2.Func.Inl)
 			}
 		}
 	}
diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go
index 185cfecc68..3061275da3 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/internal/gc/typecheck.go
@@ -1303,7 +1303,7 @@ OpSwitch:
 		if l.Op == OTYPE {
 			if n.Isddd || l.Type.Bound == -100 {
 				if l.Type.Broke == 0 {
-					Yyerror("invalid use of ... in type conversion", l)
+					Yyerror("invalid use of ... in type conversion to %v", l.Type)
 				}
 				n.Diag = 1
 			}
@@ -1528,7 +1528,7 @@ OpSwitch:
 		var t *Type
 		switch l.Type.Etype {
 		default:
-			Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type, r.Type)
+			Yyerror("invalid operation: %v (arguments have type %v, expected floating-point)", n, l.Type)
 			n.Type = nil
 			return
 
@@ -1645,7 +1645,7 @@ OpSwitch:
 		n.Type = t
 		if !Isslice(t) {
 			if Isconst(args.N, CTNIL) {
-				Yyerror("first argument to append must be typed slice; have untyped nil", t)
+				Yyerror("first argument to append must be typed slice; have untyped nil")
 				n.Type = nil
 				return
 			}
@@ -3526,7 +3526,7 @@ func typecheckfunc(n *Node) {
 func stringtoarraylit(np **Node) {
 	n := *np
 	if n.Left.Op != OLITERAL || n.Left.Val.Ctype != CTSTR {
-		Fatal("stringtoarraylit %N", n)
+		Fatal("stringtoarraylit %v", n)
 	}
 
 	s := n.Left.Val.U.Sval
diff --git a/src/cmd/internal/gc/y.go b/src/cmd/internal/gc/y.go
index f2c8b96982..fd3f2b3176 100644
--- a/src/cmd/internal/gc/y.go
+++ b/src/cmd/internal/gc/y.go
@@ -5,10 +5,11 @@ import __yyfmt__ "fmt"
 
 //line go.y:21
 import (
+	"fmt"
 	"strings"
 )
 
-//line go.y:27
+//line go.y:28
 type yySymType struct {
 	yys  int
 	node *Node
@@ -153,7 +154,7 @@ const yyEofCode = 1
 const yyErrCode = 2
 const yyMaxDepth = 200
 
-//line go.y:2242
+//line go.y:2243
 func fixlbrace(lbr int) {
 	// If the opening brace was an LBODY,
 	// set up for another one now that we're done.
@@ -1187,13 +1188,13 @@ yydefault:
 
 	case 1:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:127
+		//line go.y:128
 		{
 			xtop = concat(xtop, yyDollar[4].list)
 		}
 	case 2:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:133
+		//line go.y:134
 		{
 			prevlineno = lineno
 			Yyerror("package statement must be first")
@@ -1201,13 +1202,13 @@ yydefault:
 		}
 	case 3:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:139
+		//line go.y:140
 		{
 			mkpackage(yyDollar[2].sym.Name)
 		}
 	case 4:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:149
+		//line go.y:150
 		{
 			importpkg = Runtimepkg
 
@@ -1220,13 +1221,13 @@ yydefault:
 		}
 	case 5:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:161
+		//line go.y:162
 		{
 			importpkg = nil
 		}
 	case 11:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:175
+		//line go.y:176
 		{
 			ipkg := importpkg
 			my := importmyname
@@ -1263,7 +1264,7 @@ yydefault:
 		}
 	case 12:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:210
+		//line go.y:211
 		{
 			// When an invalid import path is passed to importfile,
 			// it calls Yyerror and then sets up a fake import with
@@ -1275,7 +1276,7 @@ yydefault:
 		}
 	case 15:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:226
+		//line go.y:227
 		{
 			// import with original name
 			yyVAL.i = parserline()
@@ -1284,7 +1285,7 @@ yydefault:
 		}
 	case 16:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:233
+		//line go.y:234
 		{
 			// import with given name
 			yyVAL.i = parserline()
@@ -1293,7 +1294,7 @@ yydefault:
 		}
 	case 17:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:240
+		//line go.y:241
 		{
 			// import into my name space
 			yyVAL.i = parserline()
@@ -1302,7 +1303,7 @@ yydefault:
 		}
 	case 18:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:249
+		//line go.y:250
 		{
 			if importpkg.Name == "" {
 				importpkg.Name = yyDollar[2].sym.Name
@@ -1319,7 +1320,7 @@ yydefault:
 		}
 	case 20:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:266
+		//line go.y:267
 		{
 			if yyDollar[1].sym.Name == "safe" {
 				curio.importsafe = true
@@ -1327,64 +1328,64 @@ yydefault:
 		}
 	case 21:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:273
+		//line go.y:274
 		{
 			defercheckwidth()
 		}
 	case 22:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:277
+		//line go.y:278
 		{
 			resumecheckwidth()
 			unimportfile()
 		}
 	case 23:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:286
+		//line go.y:287
 		{
 			Yyerror("empty top-level declaration")
 			yyVAL.list = nil
 		}
 	case 25:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:292
+		//line go.y:293
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 26:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:296
+		//line go.y:297
 		{
 			Yyerror("non-declaration statement outside function body")
 			yyVAL.list = nil
 		}
 	case 27:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:301
+		//line go.y:302
 		{
 			yyVAL.list = nil
 		}
 	case 28:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:307
+		//line go.y:308
 		{
 			yyVAL.list = yyDollar[2].list
 		}
 	case 29:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:311
+		//line go.y:312
 		{
 			yyVAL.list = yyDollar[3].list
 		}
 	case 30:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:315
+		//line go.y:316
 		{
 			yyVAL.list = nil
 		}
 	case 31:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:319
+		//line go.y:320
 		{
 			yyVAL.list = yyDollar[2].list
 			iota_ = -100000
@@ -1392,7 +1393,7 @@ yydefault:
 		}
 	case 32:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:325
+		//line go.y:326
 		{
 			yyVAL.list = yyDollar[3].list
 			iota_ = -100000
@@ -1400,7 +1401,7 @@ yydefault:
 		}
 	case 33:
 		yyDollar = yyS[yypt-7 : yypt+1]
-		//line go.y:331
+		//line go.y:332
 		{
 			yyVAL.list = concat(yyDollar[3].list, yyDollar[5].list)
 			iota_ = -100000
@@ -1408,80 +1409,80 @@ yydefault:
 		}
 	case 34:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:337
+		//line go.y:338
 		{
 			yyVAL.list = nil
 			iota_ = -100000
 		}
 	case 35:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:342
+		//line go.y:343
 		{
 			yyVAL.list = list1(yyDollar[2].node)
 		}
 	case 36:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:346
+		//line go.y:347
 		{
 			yyVAL.list = yyDollar[3].list
 		}
 	case 37:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:350
+		//line go.y:351
 		{
 			yyVAL.list = nil
 		}
 	case 38:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:356
+		//line go.y:357
 		{
 			iota_ = 0
 		}
 	case 39:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:362
+		//line go.y:363
 		{
 			yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, nil)
 		}
 	case 40:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:366
+		//line go.y:367
 		{
 			yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list)
 		}
 	case 41:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:370
+		//line go.y:371
 		{
 			yyVAL.list = variter(yyDollar[1].list, nil, yyDollar[3].list)
 		}
 	case 42:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:376
+		//line go.y:377
 		{
 			yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list)
 		}
 	case 43:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:380
+		//line go.y:381
 		{
 			yyVAL.list = constiter(yyDollar[1].list, nil, yyDollar[3].list)
 		}
 	case 45:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:387
+		//line go.y:388
 		{
 			yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, nil)
 		}
 	case 46:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:391
+		//line go.y:392
 		{
 			yyVAL.list = constiter(yyDollar[1].list, nil, nil)
 		}
 	case 47:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:397
+		//line go.y:398
 		{
 			// different from dclname because the name
 			// becomes visible right here, not at the end
@@ -1490,13 +1491,13 @@ yydefault:
 		}
 	case 48:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:406
+		//line go.y:407
 		{
 			yyVAL.node = typedcl1(yyDollar[1].node, yyDollar[2].node, true)
 		}
 	case 49:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:412
+		//line go.y:413
 		{
 			yyVAL.node = yyDollar[1].node
 
@@ -1512,14 +1513,14 @@ yydefault:
 		}
 	case 50:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:426
+		//line go.y:427
 		{
 			yyVAL.node = Nod(OASOP, yyDollar[1].node, yyDollar[3].node)
 			yyVAL.node.Etype = uint8(yyDollar[2].i) // rathole to pass opcode
 		}
 	case 51:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:431
+		//line go.y:432
 		{
 			if yyDollar[1].list.Next == nil && yyDollar[3].list.Next == nil {
 				// simple
@@ -1533,7 +1534,7 @@ yydefault:
 		}
 	case 52:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:443
+		//line go.y:444
 		{
 			if yyDollar[3].list.N.Op == OTYPESW {
 				yyVAL.node = Nod(OTYPESW, nil, yyDollar[3].list.N.Right)
@@ -1553,7 +1554,7 @@ yydefault:
 		}
 	case 53:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:461
+		//line go.y:462
 		{
 			yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1))
 			yyVAL.node.Implicit = true
@@ -1561,7 +1562,7 @@ yydefault:
 		}
 	case 54:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:467
+		//line go.y:468
 		{
 			yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1))
 			yyVAL.node.Implicit = true
@@ -1569,7 +1570,7 @@ yydefault:
 		}
 	case 55:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:475
+		//line go.y:476
 		{
 			var n, nn *Node
 
@@ -1594,7 +1595,7 @@ yydefault:
 		}
 	case 56:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:498
+		//line go.y:499
 		{
 			var n *Node
 
@@ -1614,7 +1615,7 @@ yydefault:
 		}
 	case 57:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:516
+		//line go.y:517
 		{
 			// will be converted to OCASE
 			// right will point to next case
@@ -1625,7 +1626,7 @@ yydefault:
 		}
 	case 58:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:525
+		//line go.y:526
 		{
 			var n, nn *Node
 
@@ -1646,13 +1647,13 @@ yydefault:
 		}
 	case 59:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:546
+		//line go.y:547
 		{
 			markdcl()
 		}
 	case 60:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:550
+		//line go.y:551
 		{
 			if yyDollar[3].list == nil {
 				yyVAL.node = Nod(OEMPTY, nil, nil)
@@ -1663,7 +1664,7 @@ yydefault:
 		}
 	case 61:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:561
+		//line go.y:562
 		{
 			// If the last token read by the lexer was consumed
 			// as part of the case, clear it (parser has cleared yychar).
@@ -1676,7 +1677,7 @@ yydefault:
 		}
 	case 62:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:572
+		//line go.y:573
 		{
 			// This is the only place in the language where a statement
 			// list is not allowed to drop the final semicolon, because
@@ -1696,32 +1697,32 @@ yydefault:
 		}
 	case 63:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:591
+		//line go.y:592
 		{
 			yyVAL.list = nil
 		}
 	case 64:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:595
+		//line go.y:596
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[2].node)
 		}
 	case 65:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:601
+		//line go.y:602
 		{
 			markdcl()
 		}
 	case 66:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:605
+		//line go.y:606
 		{
 			yyVAL.list = yyDollar[3].list
 			popdcl()
 		}
 	case 67:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:612
+		//line go.y:613
 		{
 			yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node)
 			yyVAL.node.List = yyDollar[1].list
@@ -1729,7 +1730,7 @@ yydefault:
 		}
 	case 68:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:618
+		//line go.y:619
 		{
 			yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node)
 			yyVAL.node.List = yyDollar[1].list
@@ -1738,14 +1739,14 @@ yydefault:
 		}
 	case 69:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:625
+		//line go.y:626
 		{
 			yyVAL.node = Nod(ORANGE, nil, yyDollar[2].node)
 			yyVAL.node.Etype = 0 // := flag
 		}
 	case 70:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:632
+		//line go.y:633
 		{
 			// init ; test ; incr
 			if yyDollar[5].node != nil && yyDollar[5].node.Colas {
@@ -1760,7 +1761,7 @@ yydefault:
 		}
 	case 71:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:645
+		//line go.y:646
 		{
 			// normal test
 			yyVAL.node = Nod(OFOR, nil, nil)
@@ -1768,27 +1769,27 @@ yydefault:
 		}
 	case 73:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:654
+		//line go.y:655
 		{
 			yyVAL.node = yyDollar[1].node
 			yyVAL.node.Nbody = concat(yyVAL.node.Nbody, yyDollar[2].list)
 		}
 	case 74:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:661
+		//line go.y:662
 		{
 			markdcl()
 		}
 	case 75:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:665
+		//line go.y:666
 		{
 			yyVAL.node = yyDollar[3].node
 			popdcl()
 		}
 	case 76:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:672
+		//line go.y:673
 		{
 			// test
 			yyVAL.node = Nod(OIF, nil, nil)
@@ -1796,7 +1797,7 @@ yydefault:
 		}
 	case 77:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:678
+		//line go.y:679
 		{
 			// init ; test
 			yyVAL.node = Nod(OIF, nil, nil)
@@ -1807,13 +1808,13 @@ yydefault:
 		}
 	case 78:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:690
+		//line go.y:691
 		{
 			markdcl()
 		}
 	case 79:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:694
+		//line go.y:695
 		{
 			if yyDollar[3].node.Ntest == nil {
 				Yyerror("missing condition in if statement")
@@ -1821,13 +1822,13 @@ yydefault:
 		}
 	case 80:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:700
+		//line go.y:701
 		{
 			yyDollar[3].node.Nbody = yyDollar[5].list
 		}
 	case 81:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:704
+		//line go.y:705
 		{
 			var n *Node
 			var nn *NodeList
@@ -1845,13 +1846,13 @@ yydefault:
 		}
 	case 82:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:722
+		//line go.y:723
 		{
 			markdcl()
 		}
 	case 83:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:726
+		//line go.y:727
 		{
 			if yyDollar[4].node.Ntest == nil {
 				Yyerror("missing condition in if statement")
@@ -1861,25 +1862,25 @@ yydefault:
 		}
 	case 84:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:735
+		//line go.y:736
 		{
 			yyVAL.list = nil
 		}
 	case 85:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:739
+		//line go.y:740
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list)
 		}
 	case 86:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:744
+		//line go.y:745
 		{
 			yyVAL.list = nil
 		}
 	case 87:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:748
+		//line go.y:749
 		{
 			l := &NodeList{N: yyDollar[2].node}
 			l.End = l
@@ -1887,13 +1888,13 @@ yydefault:
 		}
 	case 88:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:756
+		//line go.y:757
 		{
 			markdcl()
 		}
 	case 89:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:760
+		//line go.y:761
 		{
 			var n *Node
 			n = yyDollar[3].node.Ntest
@@ -1904,7 +1905,7 @@ yydefault:
 		}
 	case 90:
 		yyDollar = yyS[yypt-7 : yypt+1]
-		//line go.y:769
+		//line go.y:770
 		{
 			yyVAL.node = yyDollar[3].node
 			yyVAL.node.Op = OSWITCH
@@ -1914,13 +1915,13 @@ yydefault:
 		}
 	case 91:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:779
+		//line go.y:780
 		{
 			typesw = Nod(OXXX, typesw, nil)
 		}
 	case 92:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:783
+		//line go.y:784
 		{
 			yyVAL.node = Nod(OSELECT, nil, nil)
 			yyVAL.node.Lineno = typesw.Lineno
@@ -1929,133 +1930,133 @@ yydefault:
 		}
 	case 94:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:796
+		//line go.y:797
 		{
 			yyVAL.node = Nod(OOROR, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 95:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:800
+		//line go.y:801
 		{
 			yyVAL.node = Nod(OANDAND, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 96:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:804
+		//line go.y:805
 		{
 			yyVAL.node = Nod(OEQ, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 97:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:808
+		//line go.y:809
 		{
 			yyVAL.node = Nod(ONE, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 98:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:812
+		//line go.y:813
 		{
 			yyVAL.node = Nod(OLT, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 99:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:816
+		//line go.y:817
 		{
 			yyVAL.node = Nod(OLE, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 100:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:820
+		//line go.y:821
 		{
 			yyVAL.node = Nod(OGE, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 101:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:824
+		//line go.y:825
 		{
 			yyVAL.node = Nod(OGT, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 102:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:828
+		//line go.y:829
 		{
 			yyVAL.node = Nod(OADD, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 103:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:832
+		//line go.y:833
 		{
 			yyVAL.node = Nod(OSUB, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 104:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:836
+		//line go.y:837
 		{
 			yyVAL.node = Nod(OOR, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 105:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:840
+		//line go.y:841
 		{
 			yyVAL.node = Nod(OXOR, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 106:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:844
+		//line go.y:845
 		{
 			yyVAL.node = Nod(OMUL, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 107:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:848
+		//line go.y:849
 		{
 			yyVAL.node = Nod(ODIV, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 108:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:852
+		//line go.y:853
 		{
 			yyVAL.node = Nod(OMOD, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 109:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:856
+		//line go.y:857
 		{
 			yyVAL.node = Nod(OAND, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 110:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:860
+		//line go.y:861
 		{
 			yyVAL.node = Nod(OANDNOT, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 111:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:864
+		//line go.y:865
 		{
 			yyVAL.node = Nod(OLSH, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 112:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:868
+		//line go.y:869
 		{
 			yyVAL.node = Nod(ORSH, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 113:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:873
+		//line go.y:874
 		{
 			yyVAL.node = Nod(OSEND, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 115:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:880
+		//line go.y:881
 		{
 			yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
 		}
 	case 116:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:884
+		//line go.y:885
 		{
 			if yyDollar[2].node.Op == OCOMPLIT {
 				// Special case for &T{...}: turn into (*T){...}.
@@ -2068,57 +2069,57 @@ yydefault:
 		}
 	case 117:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:895
+		//line go.y:896
 		{
 			yyVAL.node = Nod(OPLUS, yyDollar[2].node, nil)
 		}
 	case 118:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:899
+		//line go.y:900
 		{
 			yyVAL.node = Nod(OMINUS, yyDollar[2].node, nil)
 		}
 	case 119:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:903
+		//line go.y:904
 		{
 			yyVAL.node = Nod(ONOT, yyDollar[2].node, nil)
 		}
 	case 120:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:907
+		//line go.y:908
 		{
 			Yyerror("the bitwise complement operator is ^")
 			yyVAL.node = Nod(OCOM, yyDollar[2].node, nil)
 		}
 	case 121:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:912
+		//line go.y:913
 		{
 			yyVAL.node = Nod(OCOM, yyDollar[2].node, nil)
 		}
 	case 122:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:916
+		//line go.y:917
 		{
 			yyVAL.node = Nod(ORECV, yyDollar[2].node, nil)
 		}
 	case 123:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:926
+		//line go.y:927
 		{
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
 		}
 	case 124:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:930
+		//line go.y:931
 		{
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
 			yyVAL.node.List = yyDollar[3].list
 		}
 	case 125:
 		yyDollar = yyS[yypt-6 : yypt+1]
-		//line go.y:935
+		//line go.y:936
 		{
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
 			yyVAL.node.List = yyDollar[3].list
@@ -2126,13 +2127,13 @@ yydefault:
 		}
 	case 126:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:943
+		//line go.y:944
 		{
 			yyVAL.node = nodlit(yyDollar[1].val)
 		}
 	case 128:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:948
+		//line go.y:949
 		{
 			if yyDollar[1].node.Op == OPACK {
 				var s *Sym
@@ -2145,31 +2146,31 @@ yydefault:
 		}
 	case 129:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:959
+		//line go.y:960
 		{
 			yyVAL.node = Nod(ODOTTYPE, yyDollar[1].node, yyDollar[4].node)
 		}
 	case 130:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:963
+		//line go.y:964
 		{
 			yyVAL.node = Nod(OTYPESW, nil, yyDollar[1].node)
 		}
 	case 131:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:967
+		//line go.y:968
 		{
 			yyVAL.node = Nod(OINDEX, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 132:
 		yyDollar = yyS[yypt-6 : yypt+1]
-		//line go.y:971
+		//line go.y:972
 		{
 			yyVAL.node = Nod(OSLICE, yyDollar[1].node, Nod(OKEY, yyDollar[3].node, yyDollar[5].node))
 		}
 	case 133:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:975
+		//line go.y:976
 		{
 			if yyDollar[5].node == nil {
 				Yyerror("middle index required in 3-index slice")
@@ -2181,7 +2182,7 @@ yydefault:
 		}
 	case 135:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:986
+		//line go.y:987
 		{
 			// conversion
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
@@ -2189,7 +2190,7 @@ yydefault:
 		}
 	case 136:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:992
+		//line go.y:993
 		{
 			yyVAL.node = yyDollar[3].node
 			yyVAL.node.Right = yyDollar[1].node
@@ -2198,7 +2199,7 @@ yydefault:
 		}
 	case 137:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:999
+		//line go.y:1000
 		{
 			yyVAL.node = yyDollar[3].node
 			yyVAL.node.Right = yyDollar[1].node
@@ -2206,7 +2207,7 @@ yydefault:
 		}
 	case 138:
 		yyDollar = yyS[yypt-7 : yypt+1]
-		//line go.y:1005
+		//line go.y:1006
 		{
 			Yyerror("cannot parenthesize type in composite literal")
 			yyVAL.node = yyDollar[5].node
@@ -2215,7 +2216,7 @@ yydefault:
 		}
 	case 140:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1014
+		//line go.y:1015
 		{
 			// composite expression.
 			// make node early so we get the right line number.
@@ -2223,13 +2224,13 @@ yydefault:
 		}
 	case 141:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1022
+		//line go.y:1023
 		{
 			yyVAL.node = Nod(OKEY, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 142:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1028
+		//line go.y:1029
 		{
 			// These nodes do not carry line numbers.
 			// Since a composite literal commonly spans several lines,
@@ -2244,21 +2245,21 @@ yydefault:
 		}
 	case 143:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1041
+		//line go.y:1042
 		{
 			yyVAL.node = yyDollar[2].node
 			yyVAL.node.List = yyDollar[3].list
 		}
 	case 145:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1049
+		//line go.y:1050
 		{
 			yyVAL.node = yyDollar[2].node
 			yyVAL.node.List = yyDollar[3].list
 		}
 	case 147:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1057
+		//line go.y:1058
 		{
 			yyVAL.node = yyDollar[2].node
 
@@ -2272,19 +2273,19 @@ yydefault:
 		}
 	case 151:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1078
+		//line go.y:1079
 		{
 			yyVAL.i = LBODY
 		}
 	case 152:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1082
+		//line go.y:1083
 		{
 			yyVAL.i = '{'
 		}
 	case 153:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1093
+		//line go.y:1094
 		{
 			if yyDollar[1].sym == nil {
 				yyVAL.node = nil
@@ -2294,19 +2295,19 @@ yydefault:
 		}
 	case 154:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1103
+		//line go.y:1104
 		{
 			yyVAL.node = dclname(yyDollar[1].sym)
 		}
 	case 155:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1108
+		//line go.y:1109
 		{
 			yyVAL.node = nil
 		}
 	case 157:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1115
+		//line go.y:1116
 		{
 			yyVAL.sym = yyDollar[1].sym
 			// during imports, unqualified non-exported identifiers are from builtinpkg
@@ -2316,13 +2317,13 @@ yydefault:
 		}
 	case 159:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1124
+		//line go.y:1125
 		{
 			yyVAL.sym = nil
 		}
 	case 160:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1130
+		//line go.y:1131
 		{
 			var p *Pkg
 
@@ -2338,7 +2339,7 @@ yydefault:
 		}
 	case 161:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1144
+		//line go.y:1145
 		{
 			var p *Pkg
 
@@ -2354,7 +2355,7 @@ yydefault:
 		}
 	case 162:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1160
+		//line go.y:1161
 		{
 			yyVAL.node = oldname(yyDollar[1].sym)
 			if yyVAL.node.Pack != nil {
@@ -2363,38 +2364,38 @@ yydefault:
 		}
 	case 164:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1181
+		//line go.y:1182
 		{
 			Yyerror("final argument in variadic function missing type")
 			yyVAL.node = Nod(ODDD, typenod(typ(TINTER)), nil)
 		}
 	case 165:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1186
+		//line go.y:1187
 		{
 			yyVAL.node = Nod(ODDD, yyDollar[2].node, nil)
 		}
 	case 171:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1197
+		//line go.y:1198
 		{
 			yyVAL.node = yyDollar[2].node
 		}
 	case 175:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1206
+		//line go.y:1207
 		{
 			yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
 		}
 	case 180:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1216
+		//line go.y:1217
 		{
 			yyVAL.node = yyDollar[2].node
 		}
 	case 190:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1237
+		//line go.y:1238
 		{
 			if yyDollar[1].node.Op == OPACK {
 				var s *Sym
@@ -2407,53 +2408,53 @@ yydefault:
 		}
 	case 191:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1250
+		//line go.y:1251
 		{
 			yyVAL.node = Nod(OTARRAY, yyDollar[2].node, yyDollar[4].node)
 		}
 	case 192:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1254
+		//line go.y:1255
 		{
 			// array literal of nelem
 			yyVAL.node = Nod(OTARRAY, Nod(ODDD, nil, nil), yyDollar[4].node)
 		}
 	case 193:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1259
+		//line go.y:1260
 		{
 			yyVAL.node = Nod(OTCHAN, yyDollar[2].node, nil)
 			yyVAL.node.Etype = Cboth
 		}
 	case 194:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1264
+		//line go.y:1265
 		{
 			yyVAL.node = Nod(OTCHAN, yyDollar[3].node, nil)
 			yyVAL.node.Etype = Csend
 		}
 	case 195:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1269
+		//line go.y:1270
 		{
 			yyVAL.node = Nod(OTMAP, yyDollar[3].node, yyDollar[5].node)
 		}
 	case 198:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1277
+		//line go.y:1278
 		{
 			yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
 		}
 	case 199:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1283
+		//line go.y:1284
 		{
 			yyVAL.node = Nod(OTCHAN, yyDollar[3].node, nil)
 			yyVAL.node.Etype = Crecv
 		}
 	case 200:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1290
+		//line go.y:1291
 		{
 			yyVAL.node = Nod(OTSTRUCT, nil, nil)
 			yyVAL.node.List = yyDollar[3].list
@@ -2461,14 +2462,14 @@ yydefault:
 		}
 	case 201:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1296
+		//line go.y:1297
 		{
 			yyVAL.node = Nod(OTSTRUCT, nil, nil)
 			fixlbrace(yyDollar[2].i)
 		}
 	case 202:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1303
+		//line go.y:1304
 		{
 			yyVAL.node = Nod(OTINTER, nil, nil)
 			yyVAL.node.List = yyDollar[3].list
@@ -2476,14 +2477,14 @@ yydefault:
 		}
 	case 203:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1309
+		//line go.y:1310
 		{
 			yyVAL.node = Nod(OTINTER, nil, nil)
 			fixlbrace(yyDollar[2].i)
 		}
 	case 204:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1320
+		//line go.y:1321
 		{
 			yyVAL.node = yyDollar[2].node
 			if yyVAL.node == nil {
@@ -2501,7 +2502,7 @@ yydefault:
 		}
 	case 205:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1338
+		//line go.y:1339
 		{
 			var t *Node
 
@@ -2534,7 +2535,7 @@ yydefault:
 		}
 	case 206:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:1369
+		//line go.y:1370
 		{
 			var rcvr, t *Node
 
@@ -2572,7 +2573,7 @@ yydefault:
 		}
 	case 207:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1407
+		//line go.y:1408
 		{
 			var s *Sym
 			var t *Type
@@ -2599,7 +2600,7 @@ yydefault:
 		}
 	case 208:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:1432
+		//line go.y:1433
 		{
 			yyVAL.node = methodname1(newname(yyDollar[4].sym), yyDollar[2].list.N.Right)
 			yyVAL.node.Type = functype(yyDollar[2].list.N, yyDollar[6].list, yyDollar[8].list)
@@ -2617,7 +2618,7 @@ yydefault:
 		}
 	case 209:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1450
+		//line go.y:1451
 		{
 			yyDollar[3].list = checkarglist(yyDollar[3].list, 1)
 			yyVAL.node = Nod(OTFUNC, nil, nil)
@@ -2626,13 +2627,13 @@ yydefault:
 		}
 	case 210:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1458
+		//line go.y:1459
 		{
 			yyVAL.list = nil
 		}
 	case 211:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1462
+		//line go.y:1463
 		{
 			yyVAL.list = yyDollar[2].list
 			if yyVAL.list == nil {
@@ -2641,51 +2642,51 @@ yydefault:
 		}
 	case 212:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1471
+		//line go.y:1472
 		{
 			yyVAL.list = nil
 		}
 	case 213:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1475
+		//line go.y:1476
 		{
 			yyVAL.list = list1(Nod(ODCLFIELD, nil, yyDollar[1].node))
 		}
 	case 214:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1479
+		//line go.y:1480
 		{
 			yyDollar[2].list = checkarglist(yyDollar[2].list, 0)
 			yyVAL.list = yyDollar[2].list
 		}
 	case 215:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1486
+		//line go.y:1487
 		{
 			closurehdr(yyDollar[1].node)
 		}
 	case 216:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1492
+		//line go.y:1493
 		{
 			yyVAL.node = closurebody(yyDollar[3].list)
 			fixlbrace(yyDollar[2].i)
 		}
 	case 217:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1497
+		//line go.y:1498
 		{
 			yyVAL.node = closurebody(nil)
 		}
 	case 218:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1508
+		//line go.y:1509
 		{
 			yyVAL.list = nil
 		}
 	case 219:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1512
+		//line go.y:1513
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list)
 			if nsyntaxerrors == 0 {
@@ -2698,49 +2699,49 @@ yydefault:
 		}
 	case 221:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1526
+		//line go.y:1527
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
 		}
 	case 223:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1533
+		//line go.y:1534
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
 		}
 	case 224:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1539
+		//line go.y:1540
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 225:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1543
+		//line go.y:1544
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 227:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1550
+		//line go.y:1551
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
 		}
 	case 228:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1556
+		//line go.y:1557
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 229:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1560
+		//line go.y:1561
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 230:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1566
+		//line go.y:1567
 		{
 			var l *NodeList
 
@@ -2766,14 +2767,14 @@ yydefault:
 		}
 	case 231:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1590
+		//line go.y:1591
 		{
 			yyDollar[1].node.Val = yyDollar[2].val
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 232:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1595
+		//line go.y:1596
 		{
 			yyDollar[2].node.Val = yyDollar[4].val
 			yyVAL.list = list1(yyDollar[2].node)
@@ -2781,7 +2782,7 @@ yydefault:
 		}
 	case 233:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1601
+		//line go.y:1602
 		{
 			yyDollar[2].node.Right = Nod(OIND, yyDollar[2].node.Right, nil)
 			yyDollar[2].node.Val = yyDollar[3].val
@@ -2789,7 +2790,7 @@ yydefault:
 		}
 	case 234:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1607
+		//line go.y:1608
 		{
 			yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil)
 			yyDollar[3].node.Val = yyDollar[5].val
@@ -2798,7 +2799,7 @@ yydefault:
 		}
 	case 235:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1614
+		//line go.y:1615
 		{
 			yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil)
 			yyDollar[3].node.Val = yyDollar[5].val
@@ -2807,7 +2808,7 @@ yydefault:
 		}
 	case 236:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1623
+		//line go.y:1624
 		{
 			var n *Node
 
@@ -2819,7 +2820,7 @@ yydefault:
 		}
 	case 237:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1633
+		//line go.y:1634
 		{
 			var pkg *Pkg
 
@@ -2834,33 +2835,33 @@ yydefault:
 		}
 	case 238:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1648
+		//line go.y:1649
 		{
 			yyVAL.node = embedded(yyDollar[1].sym, localpkg)
 		}
 	case 239:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1654
+		//line go.y:1655
 		{
 			yyVAL.node = Nod(ODCLFIELD, yyDollar[1].node, yyDollar[2].node)
 			ifacedcl(yyVAL.node)
 		}
 	case 240:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1659
+		//line go.y:1660
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[1].sym))
 		}
 	case 241:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1663
+		//line go.y:1664
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[2].sym))
 			Yyerror("cannot parenthesize embedded type")
 		}
 	case 242:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1670
+		//line go.y:1671
 		{
 			// without func keyword
 			yyDollar[2].list = checkarglist(yyDollar[2].list, 1)
@@ -2870,7 +2871,7 @@ yydefault:
 		}
 	case 244:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1684
+		//line go.y:1685
 		{
 			yyVAL.node = Nod(ONONAME, nil, nil)
 			yyVAL.node.Sym = yyDollar[1].sym
@@ -2878,7 +2879,7 @@ yydefault:
 		}
 	case 245:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1690
+		//line go.y:1691
 		{
 			yyVAL.node = Nod(ONONAME, nil, nil)
 			yyVAL.node.Sym = yyDollar[1].sym
@@ -2886,56 +2887,56 @@ yydefault:
 		}
 	case 247:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1699
+		//line go.y:1700
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 248:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1703
+		//line go.y:1704
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 249:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1708
+		//line go.y:1709
 		{
 			yyVAL.list = nil
 		}
 	case 250:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1712
+		//line go.y:1713
 		{
 			yyVAL.list = yyDollar[1].list
 		}
 	case 251:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1720
+		//line go.y:1721
 		{
 			yyVAL.node = nil
 		}
 	case 253:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1725
+		//line go.y:1726
 		{
 			yyVAL.node = liststmt(yyDollar[1].list)
 		}
 	case 255:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1730
+		//line go.y:1731
 		{
 			yyVAL.node = nil
 		}
 	case 261:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1741
+		//line go.y:1742
 		{
 			yyDollar[1].node = Nod(OLABEL, yyDollar[1].node, nil)
 			yyDollar[1].node.Sym = dclstack // context, for goto restrictions
 		}
 	case 262:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1746
+		//line go.y:1747
 		{
 			var l *NodeList
 
@@ -2948,7 +2949,7 @@ yydefault:
 		}
 	case 263:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1757
+		//line go.y:1758
 		{
 			// will be converted to OFALL
 			yyVAL.node = Nod(OXFALL, nil, nil)
@@ -2956,38 +2957,38 @@ yydefault:
 		}
 	case 264:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1763
+		//line go.y:1764
 		{
 			yyVAL.node = Nod(OBREAK, yyDollar[2].node, nil)
 		}
 	case 265:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1767
+		//line go.y:1768
 		{
 			yyVAL.node = Nod(OCONTINUE, yyDollar[2].node, nil)
 		}
 	case 266:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1771
+		//line go.y:1772
 		{
 			yyVAL.node = Nod(OPROC, yyDollar[2].node, nil)
 		}
 	case 267:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1775
+		//line go.y:1776
 		{
 			yyVAL.node = Nod(ODEFER, yyDollar[2].node, nil)
 		}
 	case 268:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1779
+		//line go.y:1780
 		{
 			yyVAL.node = Nod(OGOTO, yyDollar[2].node, nil)
 			yyVAL.node.Sym = dclstack // context, for goto restrictions
 		}
 	case 269:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1784
+		//line go.y:1785
 		{
 			yyVAL.node = Nod(ORETURN, nil, nil)
 			yyVAL.node.List = yyDollar[2].list
@@ -3009,7 +3010,7 @@ yydefault:
 		}
 	case 270:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1806
+		//line go.y:1807
 		{
 			yyVAL.list = nil
 			if yyDollar[1].node != nil {
@@ -3018,7 +3019,7 @@ yydefault:
 		}
 	case 271:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1813
+		//line go.y:1814
 		{
 			yyVAL.list = yyDollar[1].list
 			if yyDollar[3].node != nil {
@@ -3027,163 +3028,163 @@ yydefault:
 		}
 	case 272:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1822
+		//line go.y:1823
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 273:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1826
+		//line go.y:1827
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 274:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1832
+		//line go.y:1833
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 275:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1836
+		//line go.y:1837
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 276:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1842
+		//line go.y:1843
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 277:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1846
+		//line go.y:1847
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 278:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1852
+		//line go.y:1853
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 279:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1856
+		//line go.y:1857
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 280:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1865
+		//line go.y:1866
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 281:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1869
+		//line go.y:1870
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 282:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1873
+		//line go.y:1874
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 283:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1877
+		//line go.y:1878
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 284:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1882
+		//line go.y:1883
 		{
 			yyVAL.list = nil
 		}
 	case 285:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1886
+		//line go.y:1887
 		{
 			yyVAL.list = yyDollar[1].list
 		}
 	case 290:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1900
+		//line go.y:1901
 		{
 			yyVAL.node = nil
 		}
 	case 292:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1906
+		//line go.y:1907
 		{
 			yyVAL.list = nil
 		}
 	case 294:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1912
+		//line go.y:1913
 		{
 			yyVAL.node = nil
 		}
 	case 296:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1918
+		//line go.y:1919
 		{
 			yyVAL.list = nil
 		}
 	case 298:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1924
+		//line go.y:1925
 		{
 			yyVAL.list = nil
 		}
 	case 300:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1930
+		//line go.y:1931
 		{
 			yyVAL.list = nil
 		}
 	case 302:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1936
+		//line go.y:1937
 		{
 			yyVAL.val.Ctype = CTxxx
 		}
 	case 304:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1946
+		//line go.y:1947
 		{
 			importimport(yyDollar[2].sym, yyDollar[3].val.U.Sval)
 		}
 	case 305:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1950
+		//line go.y:1951
 		{
 			importvar(yyDollar[2].sym, yyDollar[3].typ)
 		}
 	case 306:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1954
+		//line go.y:1955
 		{
 			importconst(yyDollar[2].sym, Types[TIDEAL], yyDollar[4].node)
 		}
 	case 307:
 		yyDollar = yyS[yypt-6 : yypt+1]
-		//line go.y:1958
+		//line go.y:1959
 		{
 			importconst(yyDollar[2].sym, yyDollar[3].typ, yyDollar[5].node)
 		}
 	case 308:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1962
+		//line go.y:1963
 		{
 			importtype(yyDollar[2].typ, yyDollar[3].typ)
 		}
 	case 309:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1966
+		//line go.y:1967
 		{
 			if yyDollar[2].node == nil {
 				dclcontext = PEXTERN // since we skip the funcbody below
@@ -3196,35 +3197,35 @@ yydefault:
 			importlist = list(importlist, yyDollar[2].node)
 
 			if Debug['E'] > 0 {
-				print("import [%q] func %lN \n", importpkg.Path, yyDollar[2].node)
+				fmt.Printf("import [%q] func %v \n", importpkg.Path, yyDollar[2].node)
 				if Debug['m'] > 2 && yyDollar[2].node.Func.Inl != nil {
-					print("inl body:%+H\n", yyDollar[2].node.Func.Inl)
+					fmt.Printf("inl body:%v\n", yyDollar[2].node.Func.Inl)
 				}
 			}
 		}
 	case 310:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1987
+		//line go.y:1988
 		{
 			yyVAL.sym = yyDollar[1].sym
 			structpkg = yyVAL.sym.Pkg
 		}
 	case 311:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1994
+		//line go.y:1995
 		{
 			yyVAL.typ = pkgtype(yyDollar[1].sym)
 			importsym(yyDollar[1].sym, OTYPE)
 		}
 	case 317:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2014
+		//line go.y:2015
 		{
 			yyVAL.typ = pkgtype(yyDollar[1].sym)
 		}
 	case 318:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2018
+		//line go.y:2019
 		{
 			// predefined name like uint8
 			yyDollar[1].sym = Pkglookup(yyDollar[1].sym.Name, builtinpkg)
@@ -3237,43 +3238,43 @@ yydefault:
 		}
 	case 319:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2029
+		//line go.y:2030
 		{
 			yyVAL.typ = aindex(nil, yyDollar[3].typ)
 		}
 	case 320:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2033
+		//line go.y:2034
 		{
 			yyVAL.typ = aindex(nodlit(yyDollar[2].val), yyDollar[4].typ)
 		}
 	case 321:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2037
+		//line go.y:2038
 		{
 			yyVAL.typ = maptype(yyDollar[3].typ, yyDollar[5].typ)
 		}
 	case 322:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2041
+		//line go.y:2042
 		{
 			yyVAL.typ = tostruct(yyDollar[3].list)
 		}
 	case 323:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2045
+		//line go.y:2046
 		{
 			yyVAL.typ = tointerface(yyDollar[3].list)
 		}
 	case 324:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:2049
+		//line go.y:2050
 		{
 			yyVAL.typ = Ptrto(yyDollar[2].typ)
 		}
 	case 325:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:2053
+		//line go.y:2054
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[2].typ
@@ -3281,7 +3282,7 @@ yydefault:
 		}
 	case 326:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2059
+		//line go.y:2060
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[3].typ
@@ -3289,7 +3290,7 @@ yydefault:
 		}
 	case 327:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2065
+		//line go.y:2066
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[3].typ
@@ -3297,7 +3298,7 @@ yydefault:
 		}
 	case 328:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2073
+		//line go.y:2074
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[3].typ
@@ -3305,13 +3306,13 @@ yydefault:
 		}
 	case 329:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2081
+		//line go.y:2082
 		{
 			yyVAL.typ = functype(nil, yyDollar[3].list, yyDollar[5].list)
 		}
 	case 330:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2087
+		//line go.y:2088
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[2].typ))
 			if yyDollar[1].sym != nil {
@@ -3321,7 +3322,7 @@ yydefault:
 		}
 	case 331:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2095
+		//line go.y:2096
 		{
 			var t *Type
 
@@ -3338,7 +3339,7 @@ yydefault:
 		}
 	case 332:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2112
+		//line go.y:2113
 		{
 			var s *Sym
 			var p *Pkg
@@ -3362,43 +3363,43 @@ yydefault:
 		}
 	case 333:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2136
+		//line go.y:2137
 		{
 			yyVAL.node = Nod(ODCLFIELD, newname(yyDollar[1].sym), typenod(functype(fakethis(), yyDollar[3].list, yyDollar[5].list)))
 		}
 	case 334:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2140
+		//line go.y:2141
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ))
 		}
 	case 335:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:2145
+		//line go.y:2146
 		{
 			yyVAL.list = nil
 		}
 	case 337:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2152
+		//line go.y:2153
 		{
 			yyVAL.list = yyDollar[2].list
 		}
 	case 338:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2156
+		//line go.y:2157
 		{
 			yyVAL.list = list1(Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ)))
 		}
 	case 339:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2166
+		//line go.y:2167
 		{
 			yyVAL.node = nodlit(yyDollar[1].val)
 		}
 	case 340:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:2170
+		//line go.y:2171
 		{
 			yyVAL.node = nodlit(yyDollar[2].val)
 			switch yyVAL.node.Val.Ctype {
@@ -3418,7 +3419,7 @@ yydefault:
 		}
 	case 341:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2188
+		//line go.y:2189
 		{
 			yyVAL.node = oldname(Pkglookup(yyDollar[1].sym.Name, builtinpkg))
 			if yyVAL.node.Op != OLITERAL {
@@ -3427,7 +3428,7 @@ yydefault:
 		}
 	case 343:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2198
+		//line go.y:2199
 		{
 			if yyDollar[2].node.Val.Ctype == CTRUNE && yyDollar[4].node.Val.Ctype == CTINT {
 				yyVAL.node = yyDollar[2].node
@@ -3440,37 +3441,37 @@ yydefault:
 		}
 	case 346:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2214
+		//line go.y:2215
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 347:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2218
+		//line go.y:2219
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 348:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2224
+		//line go.y:2225
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 349:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2228
+		//line go.y:2229
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 350:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2234
+		//line go.y:2235
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 351:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2238
+		//line go.y:2239
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
diff --git a/src/cmd/internal/gc/y.output b/src/cmd/internal/gc/y.output
index e4a5e5c212..f105838a7f 100644
--- a/src/cmd/internal/gc/y.output
+++ b/src/cmd/internal/gc/y.output
@@ -3,7 +3,7 @@ state 0
 	$accept: .file $end 
 	$$4: .    (4)
 
-	.  reduce 4 (src line 148)
+	.  reduce 4 (src line 149)
 
 	file  goto 1
 	loadsys  goto 2
@@ -21,7 +21,7 @@ state 2
 	package: .    (2)
 
 	LPACKAGE  shift 5
-	.  reduce 2 (src line 131)
+	.  reduce 2 (src line 132)
 
 	package  goto 4
 
@@ -37,7 +37,7 @@ state 4
 	file:  loadsys package.imports xdcl_list 
 	imports: .    (6)
 
-	.  reduce 6 (src line 165)
+	.  reduce 6 (src line 166)
 
 	imports  goto 8
 
@@ -56,7 +56,7 @@ state 6
 	loadsys:  $$4 import_package.import_there 
 	$$21: .    (21)
 
-	.  reduce 21 (src line 272)
+	.  reduce 21 (src line 273)
 
 	import_there  goto 14
 	$$21  goto 15
@@ -74,7 +74,7 @@ state 8
 	xdcl_list: .    (218)
 
 	LIMPORT  shift 19
-	.  reduce 218 (src line 1507)
+	.  reduce 218 (src line 1508)
 
 	xdcl_list  goto 17
 	import  goto 18
@@ -89,19 +89,19 @@ state 9
 state 10
 	sym:  LNAME.    (157)
 
-	.  reduce 157 (src line 1113)
+	.  reduce 157 (src line 1114)
 
 
 state 11
 	sym:  hidden_importsym.    (158)
 
-	.  reduce 158 (src line 1122)
+	.  reduce 158 (src line 1123)
 
 
 state 12
 	sym:  '?'.    (159)
 
-	.  reduce 159 (src line 1123)
+	.  reduce 159 (src line 1124)
 
 
 state 13
@@ -115,14 +115,14 @@ state 13
 state 14
 	loadsys:  $$4 import_package import_there.    (5)
 
-	.  reduce 5 (src line 159)
+	.  reduce 5 (src line 160)
 
 
 state 15
 	import_there:  $$21.hidden_import_list '$' '$' 
 	hidden_import_list: .    (344)
 
-	.  reduce 344 (src line 2209)
+	.  reduce 344 (src line 2210)
 
 	hidden_import_list  goto 22
 
@@ -131,7 +131,7 @@ state 16
 	import_safety: .    (19)
 
 	LNAME  shift 24
-	.  reduce 19 (src line 264)
+	.  reduce 19 (src line 265)
 
 	import_safety  goto 23
 
@@ -140,7 +140,7 @@ state 17
 	xdcl_list:  xdcl_list.xdcl ';' 
 	xdcl: .    (23)
 
-	$end  reduce 1 (src line 122)
+	$end  reduce 1 (src line 123)
 	error  shift 29
 	LLITERAL  shift 68
 	LBREAK  shift 41
@@ -170,7 +170,7 @@ state 17
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 23 (src line 285)
+	';'  reduce 23 (src line 286)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -236,7 +236,7 @@ state 19
 state 20
 	package:  LPACKAGE sym ';'.    (3)
 
-	.  reduce 3 (src line 138)
+	.  reduce 3 (src line 139)
 
 
 state 21
@@ -271,7 +271,7 @@ state 23
 state 24
 	import_safety:  LNAME.    (20)
 
-	.  reduce 20 (src line 265)
+	.  reduce 20 (src line 266)
 
 
 state 25
@@ -284,25 +284,25 @@ state 25
 state 26
 	xdcl:  common_dcl.    (24)
 
-	.  reduce 24 (src line 290)
+	.  reduce 24 (src line 291)
 
 
 state 27
 	xdcl:  xfndcl.    (25)
 
-	.  reduce 25 (src line 291)
+	.  reduce 25 (src line 292)
 
 
 state 28
 	xdcl:  non_dcl_stmt.    (26)
 
-	.  reduce 26 (src line 295)
+	.  reduce 26 (src line 296)
 
 
 state 29
 	xdcl:  error.    (27)
 
-	.  reduce 27 (src line 300)
+	.  reduce 27 (src line 301)
 
 
 state 30
@@ -373,31 +373,31 @@ state 33
 state 34
 	non_dcl_stmt:  simple_stmt.    (256)
 
-	.  reduce 256 (src line 1734)
+	.  reduce 256 (src line 1735)
 
 
 state 35
 	non_dcl_stmt:  for_stmt.    (257)
 
-	.  reduce 257 (src line 1736)
+	.  reduce 257 (src line 1737)
 
 
 state 36
 	non_dcl_stmt:  switch_stmt.    (258)
 
-	.  reduce 258 (src line 1737)
+	.  reduce 258 (src line 1738)
 
 
 state 37
 	non_dcl_stmt:  select_stmt.    (259)
 
-	.  reduce 259 (src line 1738)
+	.  reduce 259 (src line 1739)
 
 
 state 38
 	non_dcl_stmt:  if_stmt.    (260)
 
-	.  reduce 260 (src line 1739)
+	.  reduce 260 (src line 1740)
 
 
 state 39
@@ -410,7 +410,7 @@ state 39
 state 40
 	non_dcl_stmt:  LFALL.    (263)
 
-	.  reduce 263 (src line 1756)
+	.  reduce 263 (src line 1757)
 
 
 state 41
@@ -420,7 +420,7 @@ state 41
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 155 (src line 1107)
+	.  reduce 155 (src line 1108)
 
 	sym  goto 119
 	new_name  goto 118
@@ -434,7 +434,7 @@ state 42
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 155 (src line 1107)
+	.  reduce 155 (src line 1108)
 
 	sym  goto 119
 	new_name  goto 118
@@ -538,7 +538,7 @@ state 46
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 292 (src line 1905)
+	.  reduce 292 (src line 1906)
 
 	sym  goto 123
 	expr  goto 129
@@ -562,7 +562,7 @@ state 46
 state 47
 	lconst:  LCONST.    (38)
 
-	.  reduce 38 (src line 354)
+	.  reduce 38 (src line 355)
 
 
 state 48
@@ -593,7 +593,7 @@ state 48
 	expr_list:  expr.    (276)
 
 	LASOP  shift 130
-	LCOLAS  reduce 276 (src line 1840)
+	LCOLAS  reduce 276 (src line 1841)
 	LANDAND  shift 134
 	LANDNOT  shift 149
 	LCOMM  shift 152
@@ -616,9 +616,9 @@ state 48
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	'='  reduce 276 (src line 1840)
-	','  reduce 276 (src line 1840)
-	.  reduce 49 (src line 410)
+	'='  reduce 276 (src line 1841)
+	','  reduce 276 (src line 1841)
+	.  reduce 49 (src line 411)
 
 
 state 49
@@ -636,7 +636,7 @@ state 50
 	for_stmt:  LFOR.$$74 for_body 
 	$$74: .    (74)
 
-	.  reduce 74 (src line 659)
+	.  reduce 74 (src line 660)
 
 	$$74  goto 156
 
@@ -644,7 +644,7 @@ state 51
 	switch_stmt:  LSWITCH.$$88 if_header $$89 LBODY caseblock_list '}' 
 	$$88: .    (88)
 
-	.  reduce 88 (src line 754)
+	.  reduce 88 (src line 755)
 
 	$$88  goto 157
 
@@ -652,7 +652,7 @@ state 52
 	select_stmt:  LSELECT.$$91 LBODY caseblock_list '}' 
 	$$91: .    (91)
 
-	.  reduce 91 (src line 777)
+	.  reduce 91 (src line 778)
 
 	$$91  goto 158
 
@@ -660,28 +660,28 @@ state 53
 	if_stmt:  LIF.$$78 if_header $$79 loop_body $$80 elseif_list else 
 	$$78: .    (78)
 
-	.  reduce 78 (src line 688)
+	.  reduce 78 (src line 689)
 
 	$$78  goto 159
 
 state 54
 	labelname:  new_name.    (163)
 
-	.  reduce 163 (src line 1167)
+	.  reduce 163 (src line 1168)
 
 
 state 55
 	expr:  uexpr.    (93)
 
-	.  reduce 93 (src line 793)
+	.  reduce 93 (src line 794)
 
 
 state 56
 	new_name:  sym.    (153)
 	name:  sym.    (162)
 
-	':'  reduce 153 (src line 1091)
-	.  reduce 162 (src line 1158)
+	':'  reduce 153 (src line 1092)
+	.  reduce 162 (src line 1159)
 
 
 state 57
@@ -699,7 +699,7 @@ state 57
 	'('  shift 160
 	'.'  shift 161
 	'['  shift 162
-	.  reduce 114 (src line 877)
+	.  reduce 114 (src line 878)
 
 
 state 58
@@ -1027,7 +1027,7 @@ state 66
 	pexpr:  pexpr_no_paren.    (146)
 
 	'{'  shift 171
-	.  reduce 146 (src line 1054)
+	.  reduce 146 (src line 1055)
 
 
 state 67
@@ -1078,19 +1078,19 @@ state 67
 state 68
 	pexpr_no_paren:  LLITERAL.    (126)
 
-	.  reduce 126 (src line 941)
+	.  reduce 126 (src line 942)
 
 
 state 69
 	pexpr_no_paren:  name.    (127)
 
-	.  reduce 127 (src line 946)
+	.  reduce 127 (src line 947)
 
 
 state 70
 	pexpr_no_paren:  pseudocall.    (134)
 
-	.  reduce 134 (src line 984)
+	.  reduce 134 (src line 985)
 
 
 state 71
@@ -1112,23 +1112,23 @@ state 72
 state 73
 	pexpr_no_paren:  fnliteral.    (139)
 
-	.  reduce 139 (src line 1011)
+	.  reduce 139 (src line 1012)
 
 
 state 74
 	convtype:  fntype.    (181)
 	fnlitdcl:  fntype.    (215)
 
-	'('  reduce 181 (src line 1220)
-	.  reduce 215 (src line 1484)
+	'('  reduce 181 (src line 1221)
+	.  reduce 215 (src line 1485)
 
 
 state 75
 	convtype:  othertype.    (182)
 	comptype:  othertype.    (183)
 
-	'('  reduce 182 (src line 1222)
-	.  reduce 183 (src line 1224)
+	'('  reduce 182 (src line 1223)
+	.  reduce 183 (src line 1225)
 
 
 state 76
@@ -1167,7 +1167,7 @@ state 77
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1899)
+	.  reduce 290 (src line 1900)
 
 	sym  goto 123
 	expr  goto 188
@@ -1226,13 +1226,13 @@ state 79
 state 80
 	othertype:  structtype.    (196)
 
-	.  reduce 196 (src line 1272)
+	.  reduce 196 (src line 1273)
 
 
 state 81
 	othertype:  interfacetype.    (197)
 
-	.  reduce 197 (src line 1273)
+	.  reduce 197 (src line 1274)
 
 
 state 82
@@ -1258,13 +1258,13 @@ state 83
 state 84
 	imports:  imports import ';'.    (7)
 
-	.  reduce 7 (src line 166)
+	.  reduce 7 (src line 167)
 
 
 state 85
 	import:  LIMPORT import_stmt.    (8)
 
-	.  reduce 8 (src line 168)
+	.  reduce 8 (src line 169)
 
 
 state 86
@@ -1291,7 +1291,7 @@ state 87
 	$$21: .    (21)
 
 	LPACKAGE  shift 7
-	.  reduce 21 (src line 272)
+	.  reduce 21 (src line 273)
 
 	import_package  goto 204
 	import_there  goto 205
@@ -1300,7 +1300,7 @@ state 87
 state 88
 	import_here:  LLITERAL.    (15)
 
-	.  reduce 15 (src line 224)
+	.  reduce 15 (src line 225)
 
 
 state 89
@@ -1336,7 +1336,7 @@ state 92
 state 93
 	hidden_import_list:  hidden_import_list hidden_import.    (345)
 
-	.  reduce 345 (src line 2210)
+	.  reduce 345 (src line 2211)
 
 
 state 94
@@ -1389,19 +1389,19 @@ state 98
 state 99
 	import_package:  LPACKAGE LNAME import_safety ';'.    (18)
 
-	.  reduce 18 (src line 247)
+	.  reduce 18 (src line 248)
 
 
 state 100
 	xdcl_list:  xdcl_list xdcl ';'.    (219)
 
-	.  reduce 219 (src line 1511)
+	.  reduce 219 (src line 1512)
 
 
 state 101
 	common_dcl:  LVAR vardcl.    (28)
 
-	.  reduce 28 (src line 305)
+	.  reduce 28 (src line 306)
 
 
 state 102
@@ -1458,19 +1458,19 @@ state 103
 state 104
 	dcl_name_list:  dcl_name.    (274)
 
-	.  reduce 274 (src line 1830)
+	.  reduce 274 (src line 1831)
 
 
 state 105
 	dcl_name:  sym.    (154)
 
-	.  reduce 154 (src line 1101)
+	.  reduce 154 (src line 1102)
 
 
 state 106
 	common_dcl:  lconst constdcl.    (31)
 
-	.  reduce 31 (src line 318)
+	.  reduce 31 (src line 319)
 
 
 state 107
@@ -1526,7 +1526,7 @@ state 108
 state 109
 	common_dcl:  LTYPE typedcl.    (35)
 
-	.  reduce 35 (src line 341)
+	.  reduce 35 (src line 342)
 
 
 state 110
@@ -1577,7 +1577,7 @@ state 111
 state 112
 	typedclname:  sym.    (47)
 
-	.  reduce 47 (src line 395)
+	.  reduce 47 (src line 396)
 
 
 state 113
@@ -1585,7 +1585,7 @@ state 113
 	fnbody: .    (210)
 
 	'{'  shift 242
-	.  reduce 210 (src line 1457)
+	.  reduce 210 (src line 1458)
 
 	fnbody  goto 241
 
@@ -1607,7 +1607,7 @@ state 114
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1707)
+	.  reduce 249 (src line 1708)
 
 	sym  goto 247
 	ntype  goto 249
@@ -1637,43 +1637,43 @@ state 116
 	non_dcl_stmt:  labelname ':'.$$261 stmt 
 	$$261: .    (261)
 
-	.  reduce 261 (src line 1740)
+	.  reduce 261 (src line 1741)
 
 	$$261  goto 252
 
 state 117
 	non_dcl_stmt:  LBREAK onew_name.    (264)
 
-	.  reduce 264 (src line 1762)
+	.  reduce 264 (src line 1763)
 
 
 state 118
 	onew_name:  new_name.    (156)
 
-	.  reduce 156 (src line 1111)
+	.  reduce 156 (src line 1112)
 
 
 state 119
 	new_name:  sym.    (153)
 
-	.  reduce 153 (src line 1091)
+	.  reduce 153 (src line 1092)
 
 
 state 120
 	non_dcl_stmt:  LCONTINUE onew_name.    (265)
 
-	.  reduce 265 (src line 1766)
+	.  reduce 265 (src line 1767)
 
 
 state 121
 	pexpr_no_paren:  pseudocall.    (134)
 	non_dcl_stmt:  LGO pseudocall.    (266)
 
-	'('  reduce 134 (src line 984)
-	'.'  reduce 134 (src line 984)
-	'{'  reduce 134 (src line 984)
-	'['  reduce 134 (src line 984)
-	.  reduce 266 (src line 1770)
+	'('  reduce 134 (src line 985)
+	'.'  reduce 134 (src line 985)
+	'{'  reduce 134 (src line 985)
+	'['  reduce 134 (src line 985)
+	.  reduce 266 (src line 1771)
 
 
 state 122
@@ -1696,7 +1696,7 @@ state 122
 state 123
 	name:  sym.    (162)
 
-	.  reduce 162 (src line 1158)
+	.  reduce 162 (src line 1159)
 
 
 state 124
@@ -1710,23 +1710,23 @@ state 125
 	pexpr_no_paren:  pseudocall.    (134)
 	non_dcl_stmt:  LDEFER pseudocall.    (267)
 
-	'('  reduce 134 (src line 984)
-	'.'  reduce 134 (src line 984)
-	'{'  reduce 134 (src line 984)
-	'['  reduce 134 (src line 984)
-	.  reduce 267 (src line 1774)
+	'('  reduce 134 (src line 985)
+	'.'  reduce 134 (src line 985)
+	'{'  reduce 134 (src line 985)
+	'['  reduce 134 (src line 985)
+	.  reduce 267 (src line 1775)
 
 
 state 126
 	non_dcl_stmt:  LGOTO new_name.    (268)
 
-	.  reduce 268 (src line 1778)
+	.  reduce 268 (src line 1779)
 
 
 state 127
 	non_dcl_stmt:  LRETURN oexpr_list.    (269)
 
-	.  reduce 269 (src line 1783)
+	.  reduce 269 (src line 1784)
 
 
 state 128
@@ -1734,7 +1734,7 @@ state 128
 	oexpr_list:  expr_list.    (293)
 
 	','  shift 155
-	.  reduce 293 (src line 1909)
+	.  reduce 293 (src line 1910)
 
 
 state 129
@@ -1780,7 +1780,7 @@ state 129
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 276 (src line 1840)
+	.  reduce 276 (src line 1841)
 
 
 state 130
@@ -1827,13 +1827,13 @@ state 130
 state 131
 	simple_stmt:  expr LINC.    (53)
 
-	.  reduce 53 (src line 460)
+	.  reduce 53 (src line 461)
 
 
 state 132
 	simple_stmt:  expr LDEC.    (54)
 
-	.  reduce 54 (src line 466)
+	.  reduce 54 (src line 467)
 
 
 state 133
@@ -2805,7 +2805,7 @@ state 156
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1911)
+	.  reduce 294 (src line 1912)
 
 	sym  goto 123
 	expr  goto 48
@@ -2853,7 +2853,7 @@ state 157
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1911)
+	.  reduce 294 (src line 1912)
 
 	sym  goto 123
 	expr  goto 48
@@ -2906,7 +2906,7 @@ state 159
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1911)
+	.  reduce 294 (src line 1912)
 
 	sym  goto 123
 	expr  goto 48
@@ -3016,7 +3016,7 @@ state 162
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1899)
+	.  reduce 290 (src line 1900)
 
 	sym  goto 123
 	expr  goto 294
@@ -3039,56 +3039,56 @@ state 162
 state 163
 	uexpr:  '*' uexpr.    (115)
 
-	.  reduce 115 (src line 879)
+	.  reduce 115 (src line 880)
 
 
 state 164
 	uexpr:  '&' uexpr.    (116)
 
-	.  reduce 116 (src line 883)
+	.  reduce 116 (src line 884)
 
 
 state 165
 	uexpr:  '+' uexpr.    (117)
 
-	.  reduce 117 (src line 894)
+	.  reduce 117 (src line 895)
 
 
 state 166
 	uexpr:  '-' uexpr.    (118)
 
-	.  reduce 118 (src line 898)
+	.  reduce 118 (src line 899)
 
 
 state 167
 	uexpr:  '!' uexpr.    (119)
 
-	.  reduce 119 (src line 902)
+	.  reduce 119 (src line 903)
 
 
 state 168
 	uexpr:  '~' uexpr.    (120)
 
-	.  reduce 120 (src line 906)
+	.  reduce 120 (src line 907)
 
 
 state 169
 	uexpr:  '^' uexpr.    (121)
 
-	.  reduce 121 (src line 911)
+	.  reduce 121 (src line 912)
 
 
 state 170
 	uexpr:  LCOMM uexpr.    (122)
 
-	.  reduce 122 (src line 915)
+	.  reduce 122 (src line 916)
 
 
 state 171
 	pexpr_no_paren:  pexpr_no_paren '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1013)
+	.  reduce 140 (src line 1014)
 
 	start_complit  goto 296
 
@@ -3143,19 +3143,19 @@ state 173
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 148 (src line 1069)
+	.  reduce 148 (src line 1070)
 
 
 state 174
 	expr_or_type:  non_expr_type.    (149)
 
-	.  reduce 149 (src line 1071)
+	.  reduce 149 (src line 1072)
 
 
 state 175
 	non_expr_type:  recvchantype.    (172)
 
-	.  reduce 172 (src line 1201)
+	.  reduce 172 (src line 1202)
 
 
 state 176
@@ -3163,11 +3163,11 @@ state 176
 	convtype:  fntype.    (181)
 	fnlitdcl:  fntype.    (215)
 
-	error  reduce 215 (src line 1484)
-	LBODY  reduce 215 (src line 1484)
-	'('  reduce 181 (src line 1220)
-	'{'  reduce 215 (src line 1484)
-	.  reduce 173 (src line 1203)
+	error  reduce 215 (src line 1485)
+	LBODY  reduce 215 (src line 1485)
+	'('  reduce 181 (src line 1221)
+	'{'  reduce 215 (src line 1485)
+	.  reduce 173 (src line 1204)
 
 
 state 177
@@ -3175,10 +3175,10 @@ state 177
 	convtype:  othertype.    (182)
 	comptype:  othertype.    (183)
 
-	LBODY  reduce 183 (src line 1224)
-	'('  reduce 182 (src line 1222)
-	'{'  reduce 183 (src line 1224)
-	.  reduce 174 (src line 1204)
+	LBODY  reduce 183 (src line 1225)
+	'('  reduce 182 (src line 1223)
+	'{'  reduce 183 (src line 1225)
+	.  reduce 174 (src line 1205)
 
 
 state 178
@@ -3310,20 +3310,20 @@ state 181
 	pexpr_no_paren:  comptype lbrace.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1013)
+	.  reduce 140 (src line 1014)
 
 	start_complit  goto 301
 
 state 182
 	lbrace:  LBODY.    (151)
 
-	.  reduce 151 (src line 1076)
+	.  reduce 151 (src line 1077)
 
 
 state 183
 	lbrace:  '{'.    (152)
 
-	.  reduce 152 (src line 1081)
+	.  reduce 152 (src line 1082)
 
 
 state 184
@@ -3359,9 +3359,9 @@ state 184
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1719)
+	';'  reduce 251 (src line 1720)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1719)
+	'}'  reduce 251 (src line 1720)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -3403,7 +3403,7 @@ state 184
 state 185
 	fnliteral:  fnlitdcl error.    (217)
 
-	.  reduce 217 (src line 1496)
+	.  reduce 217 (src line 1497)
 
 
 state 186
@@ -3463,13 +3463,13 @@ state 188
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 291 (src line 1903)
+	.  reduce 291 (src line 1904)
 
 
 state 189
 	othertype:  LCHAN non_recvchantype.    (193)
 
-	.  reduce 193 (src line 1258)
+	.  reduce 193 (src line 1259)
 
 
 state 190
@@ -3504,25 +3504,25 @@ state 190
 state 191
 	non_recvchantype:  fntype.    (176)
 
-	.  reduce 176 (src line 1210)
+	.  reduce 176 (src line 1211)
 
 
 state 192
 	non_recvchantype:  othertype.    (177)
 
-	.  reduce 177 (src line 1212)
+	.  reduce 177 (src line 1213)
 
 
 state 193
 	non_recvchantype:  ptrtype.    (178)
 
-	.  reduce 178 (src line 1213)
+	.  reduce 178 (src line 1214)
 
 
 state 194
 	non_recvchantype:  dotname.    (179)
 
-	.  reduce 179 (src line 1214)
+	.  reduce 179 (src line 1215)
 
 
 state 195
@@ -3588,7 +3588,7 @@ state 197
 	dotname:  name.'.' sym 
 
 	'.'  shift 314
-	.  reduce 189 (src line 1234)
+	.  reduce 189 (src line 1235)
 
 
 state 198
@@ -3665,27 +3665,27 @@ state 201
 	osemi: .    (286)
 
 	';'  shift 333
-	.  reduce 286 (src line 1893)
+	.  reduce 286 (src line 1894)
 
 	osemi  goto 332
 
 state 202
 	import:  LIMPORT '(' ')'.    (10)
 
-	.  reduce 10 (src line 171)
+	.  reduce 10 (src line 172)
 
 
 state 203
 	import_stmt_list:  import_stmt.    (13)
 
-	.  reduce 13 (src line 220)
+	.  reduce 13 (src line 221)
 
 
 state 204
 	import_stmt:  import_here import_package.import_there 
 	$$21: .    (21)
 
-	.  reduce 21 (src line 272)
+	.  reduce 21 (src line 273)
 
 	import_there  goto 334
 	$$21  goto 15
@@ -3693,37 +3693,37 @@ state 204
 state 205
 	import_stmt:  import_here import_there.    (12)
 
-	.  reduce 12 (src line 209)
+	.  reduce 12 (src line 210)
 
 
 state 206
 	import_here:  sym LLITERAL.    (16)
 
-	.  reduce 16 (src line 232)
+	.  reduce 16 (src line 233)
 
 
 state 207
 	import_here:  '.' LLITERAL.    (17)
 
-	.  reduce 17 (src line 239)
+	.  reduce 17 (src line 240)
 
 
 state 208
 	hidden_importsym:  '@' LLITERAL '.' LNAME.    (160)
 
-	.  reduce 160 (src line 1128)
+	.  reduce 160 (src line 1129)
 
 
 state 209
 	hidden_importsym:  '@' LLITERAL '.' '?'.    (161)
 
-	.  reduce 161 (src line 1143)
+	.  reduce 161 (src line 1144)
 
 
 state 210
 	import_there:  $$21 hidden_import_list '$' '$'.    (22)
 
-	.  reduce 22 (src line 276)
+	.  reduce 22 (src line 277)
 
 
 state 211
@@ -3757,7 +3757,7 @@ state 212
 state 213
 	hidden_pkg_importsym:  hidden_importsym.    (310)
 
-	.  reduce 310 (src line 1985)
+	.  reduce 310 (src line 1986)
 
 
 state 214
@@ -3807,7 +3807,7 @@ state 215
 state 216
 	hidden_pkgtype:  hidden_pkg_importsym.    (311)
 
-	.  reduce 311 (src line 1992)
+	.  reduce 311 (src line 1993)
 
 
 state 217
@@ -3815,7 +3815,7 @@ state 217
 	fnbody: .    (210)
 
 	'{'  shift 242
-	.  reduce 210 (src line 1457)
+	.  reduce 210 (src line 1458)
 
 	fnbody  goto 353
 
@@ -3845,20 +3845,20 @@ state 220
 	osemi: .    (286)
 
 	';'  shift 359
-	.  reduce 286 (src line 1893)
+	.  reduce 286 (src line 1894)
 
 	osemi  goto 358
 
 state 221
 	common_dcl:  LVAR '(' ')'.    (30)
 
-	.  reduce 30 (src line 314)
+	.  reduce 30 (src line 315)
 
 
 state 222
 	vardcl_list:  vardcl.    (220)
 
-	.  reduce 220 (src line 1523)
+	.  reduce 220 (src line 1524)
 
 
 state 223
@@ -3866,7 +3866,7 @@ state 223
 	vardcl:  dcl_name_list ntype.'=' expr_list 
 
 	'='  shift 360
-	.  reduce 39 (src line 360)
+	.  reduce 39 (src line 361)
 
 
 state 224
@@ -3926,31 +3926,31 @@ state 225
 state 226
 	ntype:  recvchantype.    (166)
 
-	.  reduce 166 (src line 1190)
+	.  reduce 166 (src line 1191)
 
 
 state 227
 	ntype:  fntype.    (167)
 
-	.  reduce 167 (src line 1192)
+	.  reduce 167 (src line 1193)
 
 
 state 228
 	ntype:  othertype.    (168)
 
-	.  reduce 168 (src line 1193)
+	.  reduce 168 (src line 1194)
 
 
 state 229
 	ntype:  ptrtype.    (169)
 
-	.  reduce 169 (src line 1194)
+	.  reduce 169 (src line 1195)
 
 
 state 230
 	ntype:  dotname.    (170)
 
-	.  reduce 170 (src line 1195)
+	.  reduce 170 (src line 1196)
 
 
 state 231
@@ -3995,14 +3995,14 @@ state 233
 	osemi: .    (286)
 
 	';'  shift 366
-	.  reduce 286 (src line 1893)
+	.  reduce 286 (src line 1894)
 
 	osemi  goto 365
 
 state 234
 	common_dcl:  lconst '(' ')'.    (34)
 
-	.  reduce 34 (src line 336)
+	.  reduce 34 (src line 337)
 
 
 state 235
@@ -4060,32 +4060,32 @@ state 237
 	osemi: .    (286)
 
 	';'  shift 370
-	.  reduce 286 (src line 1893)
+	.  reduce 286 (src line 1894)
 
 	osemi  goto 369
 
 state 238
 	common_dcl:  LTYPE '(' ')'.    (37)
 
-	.  reduce 37 (src line 349)
+	.  reduce 37 (src line 350)
 
 
 state 239
 	typedcl_list:  typedcl.    (224)
 
-	.  reduce 224 (src line 1537)
+	.  reduce 224 (src line 1538)
 
 
 state 240
 	typedcl:  typedclname ntype.    (48)
 
-	.  reduce 48 (src line 404)
+	.  reduce 48 (src line 405)
 
 
 state 241
 	xfndcl:  LFUNC fndcl fnbody.    (204)
 
-	.  reduce 204 (src line 1318)
+	.  reduce 204 (src line 1319)
 
 
 state 242
@@ -4121,9 +4121,9 @@ state 242
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1719)
+	';'  reduce 251 (src line 1720)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1719)
+	'}'  reduce 251 (src line 1720)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -4176,20 +4176,20 @@ state 244
 	ocomma: .    (288)
 
 	','  shift 373
-	.  reduce 288 (src line 1896)
+	.  reduce 288 (src line 1897)
 
 	ocomma  goto 374
 
 state 245
 	arg_type_list:  arg_type.    (247)
 
-	.  reduce 247 (src line 1697)
+	.  reduce 247 (src line 1698)
 
 
 state 246
 	arg_type:  name_or_type.    (243)
 
-	.  reduce 243 (src line 1681)
+	.  reduce 243 (src line 1682)
 
 
 state 247
@@ -4210,7 +4210,7 @@ state 247
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 162 (src line 1158)
+	.  reduce 162 (src line 1159)
 
 	sym  goto 123
 	ntype  goto 249
@@ -4229,13 +4229,13 @@ state 247
 state 248
 	arg_type:  dotdotdot.    (246)
 
-	.  reduce 246 (src line 1695)
+	.  reduce 246 (src line 1696)
 
 
 state 249
 	name_or_type:  ntype.    (150)
 
-	.  reduce 150 (src line 1073)
+	.  reduce 150 (src line 1074)
 
 
 state 250
@@ -4254,7 +4254,7 @@ state 250
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 164 (src line 1179)
+	.  reduce 164 (src line 1180)
 
 	sym  goto 123
 	ntype  goto 377
@@ -4285,7 +4285,7 @@ state 251
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1707)
+	.  reduce 249 (src line 1708)
 
 	sym  goto 247
 	ntype  goto 249
@@ -4311,11 +4311,11 @@ state 252
 	error  shift 307
 	LLITERAL  shift 68
 	LBREAK  shift 41
-	LCASE  reduce 251 (src line 1719)
+	LCASE  reduce 251 (src line 1720)
 	LCHAN  shift 78
 	LCONST  shift 47
 	LCONTINUE  shift 42
-	LDEFAULT  reduce 251 (src line 1719)
+	LDEFAULT  reduce 251 (src line 1720)
 	LDEFER  shift 44
 	LFALL  shift 40
 	LFOR  shift 50
@@ -4339,9 +4339,9 @@ state 252
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1719)
+	';'  reduce 251 (src line 1720)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1719)
+	'}'  reduce 251 (src line 1720)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -4396,7 +4396,7 @@ state 253
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1707)
+	.  reduce 249 (src line 1708)
 
 	sym  goto 247
 	ntype  goto 249
@@ -4458,7 +4458,7 @@ state 254
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 50 (src line 425)
+	.  reduce 50 (src line 426)
 
 
 state 255
@@ -4502,7 +4502,7 @@ state 255
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 94 (src line 795)
+	.  reduce 94 (src line 796)
 
 
 state 256
@@ -4545,7 +4545,7 @@ state 256
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 95 (src line 799)
+	.  reduce 95 (src line 800)
 
 
 state 257
@@ -4582,7 +4582,7 @@ state 257
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 96 (src line 803)
+	.  reduce 96 (src line 804)
 
 
 state 258
@@ -4619,7 +4619,7 @@ state 258
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 97 (src line 807)
+	.  reduce 97 (src line 808)
 
 
 state 259
@@ -4656,7 +4656,7 @@ state 259
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 98 (src line 811)
+	.  reduce 98 (src line 812)
 
 
 state 260
@@ -4693,7 +4693,7 @@ state 260
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 99 (src line 815)
+	.  reduce 99 (src line 816)
 
 
 state 261
@@ -4730,7 +4730,7 @@ state 261
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 100 (src line 819)
+	.  reduce 100 (src line 820)
 
 
 state 262
@@ -4767,7 +4767,7 @@ state 262
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 101 (src line 823)
+	.  reduce 101 (src line 824)
 
 
 state 263
@@ -4800,7 +4800,7 @@ state 263
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 102 (src line 827)
+	.  reduce 102 (src line 828)
 
 
 state 264
@@ -4833,7 +4833,7 @@ state 264
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 103 (src line 831)
+	.  reduce 103 (src line 832)
 
 
 state 265
@@ -4866,7 +4866,7 @@ state 265
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 104 (src line 835)
+	.  reduce 104 (src line 836)
 
 
 state 266
@@ -4899,7 +4899,7 @@ state 266
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 105 (src line 839)
+	.  reduce 105 (src line 840)
 
 
 state 267
@@ -4925,7 +4925,7 @@ state 267
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 106 (src line 843)
+	.  reduce 106 (src line 844)
 
 
 state 268
@@ -4951,7 +4951,7 @@ state 268
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 107 (src line 847)
+	.  reduce 107 (src line 848)
 
 
 state 269
@@ -4977,7 +4977,7 @@ state 269
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 108 (src line 851)
+	.  reduce 108 (src line 852)
 
 
 state 270
@@ -5003,7 +5003,7 @@ state 270
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 109 (src line 855)
+	.  reduce 109 (src line 856)
 
 
 state 271
@@ -5029,7 +5029,7 @@ state 271
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 110 (src line 859)
+	.  reduce 110 (src line 860)
 
 
 state 272
@@ -5055,7 +5055,7 @@ state 272
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 111 (src line 863)
+	.  reduce 111 (src line 864)
 
 
 state 273
@@ -5081,7 +5081,7 @@ state 273
 	expr:  expr LRSH expr.    (112)
 	expr:  expr.LCOMM expr 
 
-	.  reduce 112 (src line 867)
+	.  reduce 112 (src line 868)
 
 
 state 274
@@ -5126,7 +5126,7 @@ state 274
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 113 (src line 872)
+	.  reduce 113 (src line 873)
 
 
 state 275
@@ -5134,7 +5134,7 @@ state 275
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 51 (src line 430)
+	.  reduce 51 (src line 431)
 
 
 state 276
@@ -5142,7 +5142,7 @@ state 276
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 52 (src line 442)
+	.  reduce 52 (src line 443)
 
 
 state 277
@@ -5188,13 +5188,13 @@ state 277
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 277 (src line 1845)
+	.  reduce 277 (src line 1846)
 
 
 state 278
 	for_stmt:  LFOR $$74 for_body.    (75)
 
-	.  reduce 75 (src line 664)
+	.  reduce 75 (src line 665)
 
 
 state 279
@@ -5210,19 +5210,19 @@ state 280
 	for_header:  osimple_stmt.    (71)
 
 	';'  shift 383
-	.  reduce 71 (src line 644)
+	.  reduce 71 (src line 645)
 
 
 state 281
 	for_header:  range_stmt.    (72)
 
-	.  reduce 72 (src line 650)
+	.  reduce 72 (src line 651)
 
 
 state 282
 	osimple_stmt:  simple_stmt.    (295)
 
-	.  reduce 295 (src line 1915)
+	.  reduce 295 (src line 1916)
 
 
 state 283
@@ -5283,7 +5283,7 @@ state 285
 	switch_stmt:  LSWITCH $$88 if_header.$$89 LBODY caseblock_list '}' 
 	$$89: .    (89)
 
-	.  reduce 89 (src line 759)
+	.  reduce 89 (src line 760)
 
 	$$89  goto 387
 
@@ -5292,14 +5292,14 @@ state 286
 	if_header:  osimple_stmt.';' osimple_stmt 
 
 	';'  shift 388
-	.  reduce 76 (src line 670)
+	.  reduce 76 (src line 671)
 
 
 state 287
 	select_stmt:  LSELECT $$91 LBODY.caseblock_list '}' 
 	caseblock_list: .    (63)
 
-	.  reduce 63 (src line 590)
+	.  reduce 63 (src line 591)
 
 	caseblock_list  goto 389
 
@@ -5307,14 +5307,14 @@ state 288
 	if_stmt:  LIF $$78 if_header.$$79 loop_body $$80 elseif_list else 
 	$$79: .    (79)
 
-	.  reduce 79 (src line 693)
+	.  reduce 79 (src line 694)
 
 	$$79  goto 390
 
 state 289
 	pseudocall:  pexpr '(' ')'.    (123)
 
-	.  reduce 123 (src line 924)
+	.  reduce 123 (src line 925)
 
 
 state 290
@@ -5325,20 +5325,20 @@ state 290
 
 	LDDD  shift 392
 	','  shift 393
-	.  reduce 288 (src line 1896)
+	.  reduce 288 (src line 1897)
 
 	ocomma  goto 391
 
 state 291
 	expr_or_type_list:  expr_or_type.    (278)
 
-	.  reduce 278 (src line 1850)
+	.  reduce 278 (src line 1851)
 
 
 state 292
 	pexpr_no_paren:  pexpr '.' sym.    (128)
 
-	.  reduce 128 (src line 947)
+	.  reduce 128 (src line 948)
 
 
 state 293
@@ -5432,7 +5432,7 @@ state 294
 	'%'  shift 147
 	'&'  shift 148
 	']'  shift 396
-	.  reduce 291 (src line 1903)
+	.  reduce 291 (src line 1904)
 
 
 state 295
@@ -5467,7 +5467,7 @@ state 296
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1881)
+	.  reduce 284 (src line 1882)
 
 	sym  goto 123
 	expr  goto 402
@@ -5495,13 +5495,13 @@ state 297
 	pexpr:  '(' expr_or_type ')'.    (147)
 
 	'{'  shift 404
-	.  reduce 147 (src line 1056)
+	.  reduce 147 (src line 1057)
 
 
 state 298
 	non_expr_type:  '*' non_expr_type.    (175)
 
-	.  reduce 175 (src line 1205)
+	.  reduce 175 (src line 1206)
 
 
 state 299
@@ -5581,7 +5581,7 @@ state 300
 	'%'  shift 147
 	'&'  shift 148
 	','  shift 413
-	.  reduce 288 (src line 1896)
+	.  reduce 288 (src line 1897)
 
 	ocomma  goto 412
 
@@ -5609,7 +5609,7 @@ state 301
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1881)
+	.  reduce 284 (src line 1882)
 
 	sym  goto 123
 	expr  goto 402
@@ -5644,38 +5644,38 @@ state 302
 state 303
 	stmt_list:  stmt.    (270)
 
-	.  reduce 270 (src line 1804)
+	.  reduce 270 (src line 1805)
 
 
 state 304
 	stmt:  compound_stmt.    (252)
 
-	.  reduce 252 (src line 1723)
+	.  reduce 252 (src line 1724)
 
 
 state 305
 	stmt:  common_dcl.    (253)
 
-	.  reduce 253 (src line 1724)
+	.  reduce 253 (src line 1725)
 
 
 state 306
 	stmt:  non_dcl_stmt.    (254)
 
-	.  reduce 254 (src line 1728)
+	.  reduce 254 (src line 1729)
 
 
 state 307
 	stmt:  error.    (255)
 
-	.  reduce 255 (src line 1729)
+	.  reduce 255 (src line 1730)
 
 
 state 308
 	compound_stmt:  '{'.$$59 stmt_list '}' 
 	$$59: .    (59)
 
-	.  reduce 59 (src line 544)
+	.  reduce 59 (src line 545)
 
 	$$59  goto 417
 
@@ -5740,7 +5740,7 @@ state 310
 state 311
 	othertype:  LCHAN LCOMM ntype.    (194)
 
-	.  reduce 194 (src line 1263)
+	.  reduce 194 (src line 1264)
 
 
 state 312
@@ -5753,7 +5753,7 @@ state 312
 state 313
 	ptrtype:  '*' ntype.    (198)
 
-	.  reduce 198 (src line 1275)
+	.  reduce 198 (src line 1276)
 
 
 state 314
@@ -5780,20 +5780,20 @@ state 316
 	osemi: .    (286)
 
 	';'  shift 424
-	.  reduce 286 (src line 1893)
+	.  reduce 286 (src line 1894)
 
 	osemi  goto 423
 
 state 317
 	structtype:  LSTRUCT lbrace '}'.    (201)
 
-	.  reduce 201 (src line 1295)
+	.  reduce 201 (src line 1296)
 
 
 state 318
 	structdcl_list:  structdcl.    (226)
 
-	.  reduce 226 (src line 1547)
+	.  reduce 226 (src line 1548)
 
 
 state 319
@@ -5832,7 +5832,7 @@ state 320
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 427
 
@@ -5861,13 +5861,13 @@ state 322
 state 323
 	new_name_list:  new_name.    (272)
 
-	.  reduce 272 (src line 1820)
+	.  reduce 272 (src line 1821)
 
 
 state 324
 	embed:  packname.    (238)
 
-	.  reduce 238 (src line 1646)
+	.  reduce 238 (src line 1647)
 
 
 state 325
@@ -5875,11 +5875,11 @@ state 325
 	packname:  LNAME.    (236)
 	packname:  LNAME.'.' sym 
 
-	LLITERAL  reduce 236 (src line 1621)
-	';'  reduce 236 (src line 1621)
+	LLITERAL  reduce 236 (src line 1622)
+	';'  reduce 236 (src line 1622)
 	'.'  shift 434
-	'}'  reduce 236 (src line 1621)
-	.  reduce 157 (src line 1113)
+	'}'  reduce 236 (src line 1622)
+	.  reduce 157 (src line 1114)
 
 
 state 326
@@ -5888,20 +5888,20 @@ state 326
 	osemi: .    (286)
 
 	';'  shift 436
-	.  reduce 286 (src line 1893)
+	.  reduce 286 (src line 1894)
 
 	osemi  goto 435
 
 state 327
 	interfacetype:  LINTERFACE lbrace '}'.    (203)
 
-	.  reduce 203 (src line 1308)
+	.  reduce 203 (src line 1309)
 
 
 state 328
 	interfacedcl_list:  interfacedcl.    (228)
 
-	.  reduce 228 (src line 1554)
+	.  reduce 228 (src line 1555)
 
 
 state 329
@@ -5915,7 +5915,7 @@ state 329
 state 330
 	interfacedcl:  packname.    (240)
 
-	.  reduce 240 (src line 1658)
+	.  reduce 240 (src line 1659)
 
 
 state 331
@@ -5942,7 +5942,7 @@ state 333
 	'.'  shift 90
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1894)
+	.  reduce 287 (src line 1895)
 
 	import_here  goto 87
 	sym  goto 89
@@ -5952,7 +5952,7 @@ state 333
 state 334
 	import_stmt:  import_here import_package import_there.    (11)
 
-	.  reduce 11 (src line 173)
+	.  reduce 11 (src line 174)
 
 
 state 335
@@ -5972,31 +5972,31 @@ state 336
 state 337
 	hidden_type:  hidden_type_misc.    (312)
 
-	.  reduce 312 (src line 2003)
+	.  reduce 312 (src line 2004)
 
 
 state 338
 	hidden_type:  hidden_type_recv_chan.    (313)
 
-	.  reduce 313 (src line 2005)
+	.  reduce 313 (src line 2006)
 
 
 state 339
 	hidden_type:  hidden_type_func.    (314)
 
-	.  reduce 314 (src line 2006)
+	.  reduce 314 (src line 2007)
 
 
 state 340
 	hidden_type_misc:  hidden_importsym.    (317)
 
-	.  reduce 317 (src line 2012)
+	.  reduce 317 (src line 2013)
 
 
 state 341
 	hidden_type_misc:  LNAME.    (318)
 
-	.  reduce 318 (src line 2017)
+	.  reduce 318 (src line 2018)
 
 
 state 342
@@ -6131,7 +6131,7 @@ state 354
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1917)
+	.  reduce 296 (src line 1918)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -6151,7 +6151,7 @@ state 355
 state 356
 	hidden_funarg_list:  hidden_funarg.    (346)
 
-	.  reduce 346 (src line 2212)
+	.  reduce 346 (src line 2213)
 
 
 state 357
@@ -6191,7 +6191,7 @@ state 359
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1894)
+	.  reduce 287 (src line 1895)
 
 	sym  goto 105
 	dcl_name  goto 104
@@ -6246,13 +6246,13 @@ state 361
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 41 (src line 369)
+	.  reduce 41 (src line 370)
 
 
 state 362
 	dcl_name_list:  dcl_name_list ',' dcl_name.    (275)
 
-	.  reduce 275 (src line 1835)
+	.  reduce 275 (src line 1836)
 
 
 state 363
@@ -6305,7 +6305,7 @@ state 366
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1894)
+	.  reduce 287 (src line 1895)
 
 	sym  goto 105
 	dcl_name  goto 104
@@ -6362,7 +6362,7 @@ state 368
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 43 (src line 379)
+	.  reduce 43 (src line 380)
 
 
 state 369
@@ -6379,7 +6379,7 @@ state 370
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1894)
+	.  reduce 287 (src line 1895)
 
 	sym  goto 112
 	typedclname  goto 111
@@ -6412,7 +6412,7 @@ state 372
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1469)
+	.  reduce 212 (src line 1470)
 
 	sym  goto 485
 	dotname  goto 493
@@ -6444,7 +6444,7 @@ state 373
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 289 (src line 1897)
+	.  reduce 289 (src line 1898)
 
 	sym  goto 247
 	ntype  goto 249
@@ -6464,25 +6464,25 @@ state 373
 state 374
 	oarg_type_list_ocomma:  arg_type_list ocomma.    (250)
 
-	.  reduce 250 (src line 1711)
+	.  reduce 250 (src line 1712)
 
 
 state 375
 	arg_type:  sym name_or_type.    (244)
 
-	.  reduce 244 (src line 1683)
+	.  reduce 244 (src line 1684)
 
 
 state 376
 	arg_type:  sym dotdotdot.    (245)
 
-	.  reduce 245 (src line 1689)
+	.  reduce 245 (src line 1690)
 
 
 state 377
 	dotdotdot:  LDDD ntype.    (165)
 
-	.  reduce 165 (src line 1185)
+	.  reduce 165 (src line 1186)
 
 
 state 378
@@ -6495,7 +6495,7 @@ state 378
 state 379
 	non_dcl_stmt:  labelname ':' $$261 stmt.    (262)
 
-	.  reduce 262 (src line 1745)
+	.  reduce 262 (src line 1746)
 
 
 state 380
@@ -6508,14 +6508,14 @@ state 380
 state 381
 	for_body:  for_header loop_body.    (73)
 
-	.  reduce 73 (src line 652)
+	.  reduce 73 (src line 653)
 
 
 state 382
 	loop_body:  LBODY.$$65 stmt_list '}' 
 	$$65: .    (65)
 
-	.  reduce 65 (src line 599)
+	.  reduce 65 (src line 600)
 
 	$$65  goto 497
 
@@ -6542,7 +6542,7 @@ state 383
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1911)
+	.  reduce 294 (src line 1912)
 
 	sym  goto 123
 	expr  goto 48
@@ -6695,7 +6695,7 @@ state 386
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 69 (src line 624)
+	.  reduce 69 (src line 625)
 
 
 state 387
@@ -6728,7 +6728,7 @@ state 388
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1911)
+	.  reduce 294 (src line 1912)
 
 	sym  goto 123
 	expr  goto 48
@@ -6782,7 +6782,7 @@ state 392
 	ocomma: .    (288)
 
 	','  shift 413
-	.  reduce 288 (src line 1896)
+	.  reduce 288 (src line 1897)
 
 	ocomma  goto 510
 
@@ -6809,7 +6809,7 @@ state 393
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 289 (src line 1897)
+	.  reduce 289 (src line 1898)
 
 	sym  goto 123
 	expr  goto 173
@@ -6848,7 +6848,7 @@ state 395
 state 396
 	pexpr_no_paren:  pexpr '[' expr ']'.    (131)
 
-	.  reduce 131 (src line 966)
+	.  reduce 131 (src line 967)
 
 
 state 397
@@ -6875,7 +6875,7 @@ state 397
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1899)
+	.  reduce 290 (src line 1900)
 
 	sym  goto 123
 	expr  goto 188
@@ -6909,20 +6909,20 @@ state 399
 	ocomma: .    (288)
 
 	','  shift 516
-	.  reduce 288 (src line 1896)
+	.  reduce 288 (src line 1897)
 
 	ocomma  goto 517
 
 state 400
 	keyval_list:  keyval.    (280)
 
-	.  reduce 280 (src line 1863)
+	.  reduce 280 (src line 1864)
 
 
 state 401
 	keyval_list:  bare_complitexpr.    (281)
 
-	.  reduce 281 (src line 1868)
+	.  reduce 281 (src line 1869)
 
 
 state 402
@@ -6970,14 +6970,14 @@ state 402
 	'%'  shift 147
 	'&'  shift 148
 	':'  shift 518
-	.  reduce 142 (src line 1026)
+	.  reduce 142 (src line 1027)
 
 
 state 403
 	bare_complitexpr:  '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1013)
+	.  reduce 140 (src line 1014)
 
 	start_complit  goto 519
 
@@ -6985,7 +6985,7 @@ state 404
 	pexpr_no_paren:  '(' expr_or_type ')' '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1013)
+	.  reduce 140 (src line 1014)
 
 	start_complit  goto 520
 
@@ -7022,47 +7022,47 @@ state 405
 state 406
 	recvchantype:  LCOMM LCHAN ntype.    (199)
 
-	.  reduce 199 (src line 1281)
+	.  reduce 199 (src line 1282)
 
 
 state 407
 	ntype:  fntype.    (167)
 	non_recvchantype:  fntype.    (176)
 
-	LBODY  reduce 176 (src line 1210)
-	'('  reduce 176 (src line 1210)
-	'{'  reduce 176 (src line 1210)
-	.  reduce 167 (src line 1192)
+	LBODY  reduce 176 (src line 1211)
+	'('  reduce 176 (src line 1211)
+	'{'  reduce 176 (src line 1211)
+	.  reduce 167 (src line 1193)
 
 
 state 408
 	ntype:  othertype.    (168)
 	non_recvchantype:  othertype.    (177)
 
-	LBODY  reduce 177 (src line 1212)
-	'('  reduce 177 (src line 1212)
-	'{'  reduce 177 (src line 1212)
-	.  reduce 168 (src line 1193)
+	LBODY  reduce 177 (src line 1213)
+	'('  reduce 177 (src line 1213)
+	'{'  reduce 177 (src line 1213)
+	.  reduce 168 (src line 1194)
 
 
 state 409
 	ntype:  ptrtype.    (169)
 	non_recvchantype:  ptrtype.    (178)
 
-	LBODY  reduce 178 (src line 1213)
-	'('  reduce 178 (src line 1213)
-	'{'  reduce 178 (src line 1213)
-	.  reduce 169 (src line 1194)
+	LBODY  reduce 178 (src line 1214)
+	'('  reduce 178 (src line 1214)
+	'{'  reduce 178 (src line 1214)
+	.  reduce 169 (src line 1195)
 
 
 state 410
 	ntype:  dotname.    (170)
 	non_recvchantype:  dotname.    (179)
 
-	LBODY  reduce 179 (src line 1214)
-	'('  reduce 179 (src line 1214)
-	'{'  reduce 179 (src line 1214)
-	.  reduce 170 (src line 1195)
+	LBODY  reduce 179 (src line 1215)
+	'('  reduce 179 (src line 1215)
+	'{'  reduce 179 (src line 1215)
+	.  reduce 170 (src line 1196)
 
 
 state 411
@@ -7105,7 +7105,7 @@ state 412
 state 413
 	ocomma:  ','.    (289)
 
-	.  reduce 289 (src line 1897)
+	.  reduce 289 (src line 1898)
 
 
 state 414
@@ -7118,7 +7118,7 @@ state 414
 state 415
 	fnliteral:  fnlitdcl lbrace stmt_list '}'.    (216)
 
-	.  reduce 216 (src line 1490)
+	.  reduce 216 (src line 1491)
 
 
 state 416
@@ -7128,11 +7128,11 @@ state 416
 	error  shift 307
 	LLITERAL  shift 68
 	LBREAK  shift 41
-	LCASE  reduce 251 (src line 1719)
+	LCASE  reduce 251 (src line 1720)
 	LCHAN  shift 78
 	LCONST  shift 47
 	LCONTINUE  shift 42
-	LDEFAULT  reduce 251 (src line 1719)
+	LDEFAULT  reduce 251 (src line 1720)
 	LDEFER  shift 44
 	LFALL  shift 40
 	LFOR  shift 50
@@ -7156,9 +7156,9 @@ state 416
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1719)
+	';'  reduce 251 (src line 1720)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1719)
+	'}'  reduce 251 (src line 1720)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -7229,9 +7229,9 @@ state 417
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1719)
+	';'  reduce 251 (src line 1720)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1719)
+	'}'  reduce 251 (src line 1720)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -7273,25 +7273,25 @@ state 417
 state 418
 	othertype:  '[' oexpr ']' ntype.    (191)
 
-	.  reduce 191 (src line 1248)
+	.  reduce 191 (src line 1249)
 
 
 state 419
 	othertype:  '[' LDDD ']' ntype.    (192)
 
-	.  reduce 192 (src line 1253)
+	.  reduce 192 (src line 1254)
 
 
 state 420
 	non_recvchantype:  '(' ntype ')'.    (180)
 
-	.  reduce 180 (src line 1215)
+	.  reduce 180 (src line 1216)
 
 
 state 421
 	dotname:  name '.' sym.    (190)
 
-	.  reduce 190 (src line 1236)
+	.  reduce 190 (src line 1237)
 
 
 state 422
@@ -7339,7 +7339,7 @@ state 424
 	'('  shift 321
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1894)
+	.  reduce 287 (src line 1895)
 
 	sym  goto 119
 	packname  goto 324
@@ -7354,7 +7354,7 @@ state 425
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 529
 
@@ -7373,13 +7373,13 @@ state 426
 state 427
 	structdcl:  embed oliteral.    (231)
 
-	.  reduce 231 (src line 1589)
+	.  reduce 231 (src line 1590)
 
 
 state 428
 	oliteral:  LLITERAL.    (303)
 
-	.  reduce 303 (src line 1939)
+	.  reduce 303 (src line 1940)
 
 
 state 429
@@ -7403,7 +7403,7 @@ state 431
 	packname:  LNAME.'.' sym 
 
 	'.'  shift 434
-	.  reduce 236 (src line 1621)
+	.  reduce 236 (src line 1622)
 
 
 state 432
@@ -7411,7 +7411,7 @@ state 432
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 533
 
@@ -7450,7 +7450,7 @@ state 436
 	'('  shift 331
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1894)
+	.  reduce 287 (src line 1895)
 
 	sym  goto 119
 	packname  goto 330
@@ -7461,7 +7461,7 @@ state 436
 state 437
 	interfacedcl:  new_name indcl.    (239)
 
-	.  reduce 239 (src line 1652)
+	.  reduce 239 (src line 1653)
 
 
 state 438
@@ -7481,7 +7481,7 @@ state 438
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1707)
+	.  reduce 249 (src line 1708)
 
 	sym  goto 247
 	ntype  goto 249
@@ -7510,25 +7510,25 @@ state 439
 state 440
 	import:  LIMPORT '(' import_stmt_list osemi ')'.    (9)
 
-	.  reduce 9 (src line 170)
+	.  reduce 9 (src line 171)
 
 
 state 441
 	import_stmt_list:  import_stmt_list ';' import_stmt.    (14)
 
-	.  reduce 14 (src line 222)
+	.  reduce 14 (src line 223)
 
 
 state 442
 	hidden_import:  LIMPORT LNAME LLITERAL ';'.    (304)
 
-	.  reduce 304 (src line 1944)
+	.  reduce 304 (src line 1945)
 
 
 state 443
 	hidden_import:  LVAR hidden_pkg_importsym hidden_type ';'.    (305)
 
-	.  reduce 305 (src line 1949)
+	.  reduce 305 (src line 1950)
 
 
 state 444
@@ -7587,7 +7587,7 @@ state 447
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 298 (src line 1923)
+	.  reduce 298 (src line 1924)
 
 	sym  goto 546
 	hidden_importsym  goto 11
@@ -7610,7 +7610,7 @@ state 448
 	'['  shift 342
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 300 (src line 1929)
+	.  reduce 300 (src line 1930)
 
 	sym  goto 550
 	hidden_importsym  goto 553
@@ -7625,13 +7625,13 @@ state 448
 state 449
 	hidden_type_misc:  '*' hidden_type.    (324)
 
-	.  reduce 324 (src line 2048)
+	.  reduce 324 (src line 2049)
 
 
 state 450
 	hidden_type_misc:  LCHAN hidden_type_non_recv_chan.    (325)
 
-	.  reduce 325 (src line 2052)
+	.  reduce 325 (src line 2053)
 
 
 state 451
@@ -7666,13 +7666,13 @@ state 452
 state 453
 	hidden_type_non_recv_chan:  hidden_type_misc.    (315)
 
-	.  reduce 315 (src line 2008)
+	.  reduce 315 (src line 2009)
 
 
 state 454
 	hidden_type_non_recv_chan:  hidden_type_func.    (316)
 
-	.  reduce 316 (src line 2010)
+	.  reduce 316 (src line 2011)
 
 
 state 455
@@ -7703,7 +7703,7 @@ state 456
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1917)
+	.  reduce 296 (src line 1918)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -7721,7 +7721,7 @@ state 457
 state 458
 	hidden_constant:  hidden_literal.    (342)
 
-	.  reduce 342 (src line 2195)
+	.  reduce 342 (src line 2196)
 
 
 state 459
@@ -7741,7 +7741,7 @@ state 459
 state 460
 	hidden_literal:  LLITERAL.    (339)
 
-	.  reduce 339 (src line 2164)
+	.  reduce 339 (src line 2165)
 
 
 state 461
@@ -7754,7 +7754,7 @@ state 461
 state 462
 	hidden_literal:  sym.    (341)
 
-	.  reduce 341 (src line 2187)
+	.  reduce 341 (src line 2188)
 
 
 state 463
@@ -7776,13 +7776,13 @@ state 463
 state 464
 	hidden_import:  LTYPE hidden_pkgtype hidden_type ';'.    (308)
 
-	.  reduce 308 (src line 1961)
+	.  reduce 308 (src line 1962)
 
 
 state 465
 	hidden_import:  LFUNC hidden_fndcl fnbody ';'.    (309)
 
-	.  reduce 309 (src line 1965)
+	.  reduce 309 (src line 1966)
 
 
 state 466
@@ -7797,7 +7797,7 @@ state 467
 	hidden_funarg_list:  hidden_funarg_list.',' hidden_funarg 
 
 	','  shift 469
-	.  reduce 297 (src line 1921)
+	.  reduce 297 (src line 1922)
 
 
 state 468
@@ -7828,7 +7828,7 @@ state 470
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 565
 
@@ -7856,13 +7856,13 @@ state 471
 state 472
 	common_dcl:  LVAR '(' vardcl_list osemi ')'.    (29)
 
-	.  reduce 29 (src line 310)
+	.  reduce 29 (src line 311)
 
 
 state 473
 	vardcl_list:  vardcl_list ';' vardcl.    (221)
 
-	.  reduce 221 (src line 1525)
+	.  reduce 221 (src line 1526)
 
 
 state 474
@@ -7870,19 +7870,19 @@ state 474
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 40 (src line 365)
+	.  reduce 40 (src line 366)
 
 
 state 475
 	ntype:  '(' ntype ')'.    (171)
 
-	.  reduce 171 (src line 1196)
+	.  reduce 171 (src line 1197)
 
 
 state 476
 	common_dcl:  lconst '(' constdcl osemi ')'.    (32)
 
-	.  reduce 32 (src line 324)
+	.  reduce 32 (src line 325)
 
 
 state 477
@@ -7891,20 +7891,20 @@ state 477
 	osemi: .    (286)
 
 	';'  shift 568
-	.  reduce 286 (src line 1893)
+	.  reduce 286 (src line 1894)
 
 	osemi  goto 567
 
 state 478
 	constdcl_list:  constdcl1.    (222)
 
-	.  reduce 222 (src line 1530)
+	.  reduce 222 (src line 1531)
 
 
 state 479
 	constdcl1:  constdcl.    (44)
 
-	.  reduce 44 (src line 384)
+	.  reduce 44 (src line 385)
 
 
 state 480
@@ -7928,7 +7928,7 @@ state 480
 	'?'  shift 12
 	'@'  shift 13
 	','  shift 225
-	.  reduce 46 (src line 390)
+	.  reduce 46 (src line 391)
 
 	sym  goto 123
 	ntype  goto 569
@@ -7947,25 +7947,25 @@ state 481
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 42 (src line 374)
+	.  reduce 42 (src line 375)
 
 
 state 482
 	common_dcl:  LTYPE '(' typedcl_list osemi ')'.    (36)
 
-	.  reduce 36 (src line 345)
+	.  reduce 36 (src line 346)
 
 
 state 483
 	typedcl_list:  typedcl_list ';' typedcl.    (225)
 
-	.  reduce 225 (src line 1542)
+	.  reduce 225 (src line 1543)
 
 
 state 484
 	fnbody:  '{' stmt_list '}'.    (211)
 
-	.  reduce 211 (src line 1461)
+	.  reduce 211 (src line 1462)
 
 
 state 485
@@ -7973,19 +7973,19 @@ state 485
 	fndcl:  '(' oarg_type_list_ocomma ')' sym.'(' oarg_type_list_ocomma ')' fnres 
 
 	'('  shift 570
-	.  reduce 162 (src line 1158)
+	.  reduce 162 (src line 1159)
 
 
 state 486
 	fntype:  LFUNC '(' oarg_type_list_ocomma ')' fnres.    (209)
 
-	.  reduce 209 (src line 1448)
+	.  reduce 209 (src line 1449)
 
 
 state 487
 	fnres:  fnret_type.    (213)
 
-	.  reduce 213 (src line 1474)
+	.  reduce 213 (src line 1475)
 
 
 state 488
@@ -8005,7 +8005,7 @@ state 488
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1707)
+	.  reduce 249 (src line 1708)
 
 	sym  goto 247
 	ntype  goto 249
@@ -8027,37 +8027,37 @@ state 488
 state 489
 	fnret_type:  recvchantype.    (184)
 
-	.  reduce 184 (src line 1227)
+	.  reduce 184 (src line 1228)
 
 
 state 490
 	fnret_type:  fntype.    (185)
 
-	.  reduce 185 (src line 1229)
+	.  reduce 185 (src line 1230)
 
 
 state 491
 	fnret_type:  othertype.    (186)
 
-	.  reduce 186 (src line 1230)
+	.  reduce 186 (src line 1231)
 
 
 state 492
 	fnret_type:  ptrtype.    (187)
 
-	.  reduce 187 (src line 1231)
+	.  reduce 187 (src line 1232)
 
 
 state 493
 	fnret_type:  dotname.    (188)
 
-	.  reduce 188 (src line 1232)
+	.  reduce 188 (src line 1233)
 
 
 state 494
 	arg_type_list:  arg_type_list ',' arg_type.    (248)
 
-	.  reduce 248 (src line 1702)
+	.  reduce 248 (src line 1703)
 
 
 state 495
@@ -8076,7 +8076,7 @@ state 495
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1469)
+	.  reduce 212 (src line 1470)
 
 	sym  goto 123
 	dotname  goto 493
@@ -8107,7 +8107,7 @@ state 496
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1469)
+	.  reduce 212 (src line 1470)
 
 	sym  goto 123
 	dotname  goto 493
@@ -8155,9 +8155,9 @@ state 497
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1719)
+	';'  reduce 251 (src line 1720)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1719)
+	'}'  reduce 251 (src line 1720)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -8289,33 +8289,33 @@ state 501
 	switch_stmt:  LSWITCH $$88 if_header $$89 LBODY.caseblock_list '}' 
 	caseblock_list: .    (63)
 
-	.  reduce 63 (src line 590)
+	.  reduce 63 (src line 591)
 
 	caseblock_list  goto 577
 
 state 502
 	if_header:  osimple_stmt ';' osimple_stmt.    (77)
 
-	.  reduce 77 (src line 677)
+	.  reduce 77 (src line 678)
 
 
 state 503
 	caseblock_list:  caseblock_list caseblock.    (64)
 
-	.  reduce 64 (src line 594)
+	.  reduce 64 (src line 595)
 
 
 state 504
 	select_stmt:  LSELECT $$91 LBODY caseblock_list '}'.    (92)
 
-	.  reduce 92 (src line 782)
+	.  reduce 92 (src line 783)
 
 
 state 505
 	caseblock:  case.$$61 stmt_list 
 	$$61: .    (61)
 
-	.  reduce 61 (src line 559)
+	.  reduce 61 (src line 560)
 
 	$$61  goto 578
 
@@ -8377,14 +8377,14 @@ state 508
 	if_stmt:  LIF $$78 if_header $$79 loop_body.$$80 elseif_list else 
 	$$80: .    (80)
 
-	.  reduce 80 (src line 699)
+	.  reduce 80 (src line 700)
 
 	$$80  goto 581
 
 state 509
 	pseudocall:  pexpr '(' expr_or_type_list ocomma ')'.    (124)
 
-	.  reduce 124 (src line 929)
+	.  reduce 124 (src line 930)
 
 
 state 510
@@ -8397,19 +8397,19 @@ state 510
 state 511
 	expr_or_type_list:  expr_or_type_list ',' expr_or_type.    (279)
 
-	.  reduce 279 (src line 1855)
+	.  reduce 279 (src line 1856)
 
 
 state 512
 	pexpr_no_paren:  pexpr '.' '(' expr_or_type ')'.    (129)
 
-	.  reduce 129 (src line 958)
+	.  reduce 129 (src line 959)
 
 
 state 513
 	pexpr_no_paren:  pexpr '.' '(' LTYPE ')'.    (130)
 
-	.  reduce 130 (src line 962)
+	.  reduce 130 (src line 963)
 
 
 state 514
@@ -8424,7 +8424,7 @@ state 514
 state 515
 	pexpr_no_paren:  pexpr_no_paren '{' start_complit braced_keyval_list '}'.    (137)
 
-	.  reduce 137 (src line 998)
+	.  reduce 137 (src line 999)
 
 
 state 516
@@ -8452,7 +8452,7 @@ state 516
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 289 (src line 1897)
+	.  reduce 289 (src line 1898)
 
 	sym  goto 123
 	expr  goto 402
@@ -8476,7 +8476,7 @@ state 516
 state 517
 	braced_keyval_list:  keyval_list ocomma.    (285)
 
-	.  reduce 285 (src line 1885)
+	.  reduce 285 (src line 1886)
 
 
 state 518
@@ -8546,7 +8546,7 @@ state 519
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1881)
+	.  reduce 284 (src line 1882)
 
 	sym  goto 123
 	expr  goto 402
@@ -8593,7 +8593,7 @@ state 520
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1881)
+	.  reduce 284 (src line 1882)
 
 	sym  goto 123
 	expr  goto 402
@@ -8627,19 +8627,19 @@ state 521
 state 522
 	pexpr_no_paren:  convtype '(' expr ocomma ')'.    (135)
 
-	.  reduce 135 (src line 985)
+	.  reduce 135 (src line 986)
 
 
 state 523
 	pexpr_no_paren:  comptype lbrace start_complit braced_keyval_list '}'.    (136)
 
-	.  reduce 136 (src line 991)
+	.  reduce 136 (src line 992)
 
 
 state 524
 	stmt_list:  stmt_list ';' stmt.    (271)
 
-	.  reduce 271 (src line 1812)
+	.  reduce 271 (src line 1813)
 
 
 state 525
@@ -8654,31 +8654,31 @@ state 525
 state 526
 	othertype:  LMAP '[' ntype ']' ntype.    (195)
 
-	.  reduce 195 (src line 1268)
+	.  reduce 195 (src line 1269)
 
 
 state 527
 	structtype:  LSTRUCT lbrace structdcl_list osemi '}'.    (200)
 
-	.  reduce 200 (src line 1288)
+	.  reduce 200 (src line 1289)
 
 
 state 528
 	structdcl_list:  structdcl_list ';' structdcl.    (227)
 
-	.  reduce 227 (src line 1549)
+	.  reduce 227 (src line 1550)
 
 
 state 529
 	structdcl:  new_name_list ntype oliteral.    (230)
 
-	.  reduce 230 (src line 1564)
+	.  reduce 230 (src line 1565)
 
 
 state 530
 	new_name_list:  new_name_list ',' new_name.    (273)
 
-	.  reduce 273 (src line 1825)
+	.  reduce 273 (src line 1826)
 
 
 state 531
@@ -8686,7 +8686,7 @@ state 531
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 594
 
@@ -8700,7 +8700,7 @@ state 532
 state 533
 	structdcl:  '*' embed oliteral.    (233)
 
-	.  reduce 233 (src line 1600)
+	.  reduce 233 (src line 1601)
 
 
 state 534
@@ -8713,19 +8713,19 @@ state 534
 state 535
 	packname:  LNAME '.' sym.    (237)
 
-	.  reduce 237 (src line 1632)
+	.  reduce 237 (src line 1633)
 
 
 state 536
 	interfacetype:  LINTERFACE lbrace interfacedcl_list osemi '}'.    (202)
 
-	.  reduce 202 (src line 1301)
+	.  reduce 202 (src line 1302)
 
 
 state 537
 	interfacedcl_list:  interfacedcl_list ';' interfacedcl.    (229)
 
-	.  reduce 229 (src line 1559)
+	.  reduce 229 (src line 1560)
 
 
 state 538
@@ -8738,13 +8738,13 @@ state 538
 state 539
 	interfacedcl:  '(' packname ')'.    (241)
 
-	.  reduce 241 (src line 1662)
+	.  reduce 241 (src line 1663)
 
 
 state 540
 	hidden_type_misc:  '[' ']' hidden_type.    (319)
 
-	.  reduce 319 (src line 2028)
+	.  reduce 319 (src line 2029)
 
 
 state 541
@@ -8787,13 +8787,13 @@ state 544
 	hidden_structdcl_list:  hidden_structdcl_list.';' hidden_structdcl 
 
 	';'  shift 601
-	.  reduce 299 (src line 1927)
+	.  reduce 299 (src line 1928)
 
 
 state 545
 	hidden_structdcl_list:  hidden_structdcl.    (348)
 
-	.  reduce 348 (src line 2222)
+	.  reduce 348 (src line 2223)
 
 
 state 546
@@ -8829,13 +8829,13 @@ state 548
 	hidden_interfacedcl_list:  hidden_interfacedcl_list.';' hidden_interfacedcl 
 
 	';'  shift 604
-	.  reduce 301 (src line 1933)
+	.  reduce 301 (src line 1934)
 
 
 state 549
 	hidden_interfacedcl_list:  hidden_interfacedcl.    (350)
 
-	.  reduce 350 (src line 2232)
+	.  reduce 350 (src line 2233)
 
 
 state 550
@@ -8848,23 +8848,23 @@ state 550
 state 551
 	hidden_interfacedcl:  hidden_type.    (334)
 
-	.  reduce 334 (src line 2139)
+	.  reduce 334 (src line 2140)
 
 
 state 552
 	sym:  LNAME.    (157)
 	hidden_type_misc:  LNAME.    (318)
 
-	'('  reduce 157 (src line 1113)
-	.  reduce 318 (src line 2017)
+	'('  reduce 157 (src line 1114)
+	.  reduce 318 (src line 2018)
 
 
 state 553
 	sym:  hidden_importsym.    (158)
 	hidden_type_misc:  hidden_importsym.    (317)
 
-	'('  reduce 158 (src line 1122)
-	.  reduce 317 (src line 2012)
+	'('  reduce 158 (src line 1123)
+	.  reduce 317 (src line 2013)
 
 
 state 554
@@ -8877,13 +8877,13 @@ state 554
 state 555
 	hidden_type_misc:  LCHAN LCOMM hidden_type.    (327)
 
-	.  reduce 327 (src line 2064)
+	.  reduce 327 (src line 2065)
 
 
 state 556
 	hidden_type_recv_chan:  LCOMM LCHAN hidden_type.    (328)
 
-	.  reduce 328 (src line 2071)
+	.  reduce 328 (src line 2072)
 
 
 state 557
@@ -8896,7 +8896,7 @@ state 557
 state 558
 	hidden_import:  LCONST hidden_pkg_importsym '=' hidden_constant ';'.    (306)
 
-	.  reduce 306 (src line 1953)
+	.  reduce 306 (src line 1954)
 
 
 state 559
@@ -8909,7 +8909,7 @@ state 559
 state 560
 	hidden_literal:  '-' LLITERAL.    (340)
 
-	.  reduce 340 (src line 2169)
+	.  reduce 340 (src line 2170)
 
 
 state 561
@@ -8934,7 +8934,7 @@ state 562
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2144)
+	.  reduce 335 (src line 2145)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -8954,13 +8954,13 @@ state 563
 state 564
 	hidden_funarg_list:  hidden_funarg_list ',' hidden_funarg.    (347)
 
-	.  reduce 347 (src line 2217)
+	.  reduce 347 (src line 2218)
 
 
 state 565
 	hidden_funarg:  sym hidden_type oliteral.    (330)
 
-	.  reduce 330 (src line 2085)
+	.  reduce 330 (src line 2086)
 
 
 state 566
@@ -8968,7 +8968,7 @@ state 566
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 615
 
@@ -8986,7 +8986,7 @@ state 568
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1894)
+	.  reduce 287 (src line 1895)
 
 	sym  goto 105
 	dcl_name  goto 104
@@ -9000,7 +9000,7 @@ state 569
 	constdcl1:  dcl_name_list ntype.    (45)
 
 	'='  shift 367
-	.  reduce 45 (src line 386)
+	.  reduce 45 (src line 387)
 
 
 state 570
@@ -9020,7 +9020,7 @@ state 570
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1707)
+	.  reduce 249 (src line 1708)
 
 	sym  goto 247
 	ntype  goto 249
@@ -9049,7 +9049,7 @@ state 571
 state 572
 	fndcl:  sym '(' oarg_type_list_ocomma ')' fnres.    (205)
 
-	.  reduce 205 (src line 1336)
+	.  reduce 205 (src line 1337)
 
 
 state 573
@@ -9084,7 +9084,7 @@ state 574
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1911)
+	.  reduce 294 (src line 1912)
 
 	sym  goto 123
 	expr  goto 48
@@ -9149,7 +9149,7 @@ state 575
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 67 (src line 610)
+	.  reduce 67 (src line 611)
 
 
 state 576
@@ -9195,7 +9195,7 @@ state 576
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 68 (src line 617)
+	.  reduce 68 (src line 618)
 
 
 state 577
@@ -9217,11 +9217,11 @@ state 578
 	error  shift 307
 	LLITERAL  shift 68
 	LBREAK  shift 41
-	LCASE  reduce 251 (src line 1719)
+	LCASE  reduce 251 (src line 1720)
 	LCHAN  shift 78
 	LCONST  shift 47
 	LCONTINUE  shift 42
-	LDEFAULT  reduce 251 (src line 1719)
+	LDEFAULT  reduce 251 (src line 1720)
 	LDEFER  shift 44
 	LFALL  shift 40
 	LFOR  shift 50
@@ -9245,9 +9245,9 @@ state 578
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1719)
+	';'  reduce 251 (src line 1720)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1719)
+	'}'  reduce 251 (src line 1720)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -9302,27 +9302,27 @@ state 579
 state 580
 	case:  LDEFAULT ':'.    (58)
 
-	.  reduce 58 (src line 524)
+	.  reduce 58 (src line 525)
 
 
 state 581
 	if_stmt:  LIF $$78 if_header $$79 loop_body $$80.elseif_list else 
 	elseif_list: .    (84)
 
-	.  reduce 84 (src line 734)
+	.  reduce 84 (src line 735)
 
 	elseif_list  goto 628
 
 state 582
 	pseudocall:  pexpr '(' expr_or_type_list LDDD ocomma ')'.    (125)
 
-	.  reduce 125 (src line 934)
+	.  reduce 125 (src line 935)
 
 
 state 583
 	pexpr_no_paren:  pexpr '[' oexpr ':' oexpr ']'.    (132)
 
-	.  reduce 132 (src line 970)
+	.  reduce 132 (src line 971)
 
 
 state 584
@@ -9348,7 +9348,7 @@ state 584
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1899)
+	.  reduce 290 (src line 1900)
 
 	sym  goto 123
 	expr  goto 188
@@ -9371,19 +9371,19 @@ state 584
 state 585
 	keyval_list:  keyval_list ',' keyval.    (282)
 
-	.  reduce 282 (src line 1872)
+	.  reduce 282 (src line 1873)
 
 
 state 586
 	keyval_list:  keyval_list ',' bare_complitexpr.    (283)
 
-	.  reduce 283 (src line 1876)
+	.  reduce 283 (src line 1877)
 
 
 state 587
 	keyval:  expr ':' complitexpr.    (141)
 
-	.  reduce 141 (src line 1020)
+	.  reduce 141 (src line 1021)
 
 
 state 588
@@ -9429,14 +9429,14 @@ state 588
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 144 (src line 1046)
+	.  reduce 144 (src line 1047)
 
 
 state 589
 	complitexpr:  '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1013)
+	.  reduce 140 (src line 1014)
 
 	start_complit  goto 630
 
@@ -9458,22 +9458,22 @@ state 592
 	ntype:  '(' ntype ')'.    (171)
 	non_recvchantype:  '(' ntype ')'.    (180)
 
-	LBODY  reduce 180 (src line 1215)
-	'('  reduce 180 (src line 1215)
-	'{'  reduce 180 (src line 1215)
-	.  reduce 171 (src line 1196)
+	LBODY  reduce 180 (src line 1216)
+	'('  reduce 180 (src line 1216)
+	'{'  reduce 180 (src line 1216)
+	.  reduce 171 (src line 1197)
 
 
 state 593
 	compound_stmt:  '{' $$59 stmt_list '}'.    (60)
 
-	.  reduce 60 (src line 549)
+	.  reduce 60 (src line 550)
 
 
 state 594
 	structdcl:  '(' embed ')' oliteral.    (232)
 
-	.  reduce 232 (src line 1594)
+	.  reduce 232 (src line 1595)
 
 
 state 595
@@ -9481,7 +9481,7 @@ state 595
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 633
 
@@ -9490,7 +9490,7 @@ state 596
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 634
 
@@ -9510,7 +9510,7 @@ state 597
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1469)
+	.  reduce 212 (src line 1470)
 
 	sym  goto 123
 	dotname  goto 493
@@ -9528,7 +9528,7 @@ state 597
 state 598
 	hidden_type_misc:  '[' LLITERAL ']' hidden_type.    (320)
 
-	.  reduce 320 (src line 2032)
+	.  reduce 320 (src line 2033)
 
 
 state 599
@@ -9555,7 +9555,7 @@ state 599
 state 600
 	hidden_type_misc:  LSTRUCT '{' ohidden_structdcl_list '}'.    (322)
 
-	.  reduce 322 (src line 2040)
+	.  reduce 322 (src line 2041)
 
 
 state 601
@@ -9575,14 +9575,14 @@ state 602
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1935)
+	.  reduce 302 (src line 1936)
 
 	oliteral  goto 638
 
 state 603
 	hidden_type_misc:  LINTERFACE '{' ohidden_interfacedcl_list '}'.    (323)
 
-	.  reduce 323 (src line 2044)
+	.  reduce 323 (src line 2045)
 
 
 state 604
@@ -9616,7 +9616,7 @@ state 605
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1917)
+	.  reduce 296 (src line 1918)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -9627,7 +9627,7 @@ state 605
 state 606
 	hidden_type_misc:  LCHAN '(' hidden_type_recv_chan ')'.    (326)
 
-	.  reduce 326 (src line 2058)
+	.  reduce 326 (src line 2059)
 
 
 state 607
@@ -9645,7 +9645,7 @@ state 607
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2144)
+	.  reduce 335 (src line 2145)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -9672,19 +9672,19 @@ state 608
 state 609
 	hidden_import:  LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'.    (307)
 
-	.  reduce 307 (src line 1957)
+	.  reduce 307 (src line 1958)
 
 
 state 610
 	hidden_fndcl:  hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres.    (207)
 
-	.  reduce 207 (src line 1405)
+	.  reduce 207 (src line 1406)
 
 
 state 611
 	ohidden_funres:  hidden_funres.    (336)
 
-	.  reduce 336 (src line 2148)
+	.  reduce 336 (src line 2149)
 
 
 state 612
@@ -9694,7 +9694,7 @@ state 612
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1917)
+	.  reduce 296 (src line 1918)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -9705,7 +9705,7 @@ state 612
 state 613
 	hidden_funres:  hidden_type.    (338)
 
-	.  reduce 338 (src line 2155)
+	.  reduce 338 (src line 2156)
 
 
 state 614
@@ -9715,7 +9715,7 @@ state 614
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1917)
+	.  reduce 296 (src line 1918)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -9726,19 +9726,19 @@ state 614
 state 615
 	hidden_funarg:  sym LDDD hidden_type oliteral.    (331)
 
-	.  reduce 331 (src line 2094)
+	.  reduce 331 (src line 2095)
 
 
 state 616
 	common_dcl:  lconst '(' constdcl ';' constdcl_list osemi ')'.    (33)
 
-	.  reduce 33 (src line 330)
+	.  reduce 33 (src line 331)
 
 
 state 617
 	constdcl_list:  constdcl_list ';' constdcl1.    (223)
 
-	.  reduce 223 (src line 1532)
+	.  reduce 223 (src line 1533)
 
 
 state 618
@@ -9751,25 +9751,25 @@ state 618
 state 619
 	fnres:  '(' oarg_type_list_ocomma ')'.    (214)
 
-	.  reduce 214 (src line 1478)
+	.  reduce 214 (src line 1479)
 
 
 state 620
 	loop_body:  LBODY $$65 stmt_list '}'.    (66)
 
-	.  reduce 66 (src line 604)
+	.  reduce 66 (src line 605)
 
 
 state 621
 	for_header:  osimple_stmt ';' osimple_stmt ';' osimple_stmt.    (70)
 
-	.  reduce 70 (src line 630)
+	.  reduce 70 (src line 631)
 
 
 state 622
 	switch_stmt:  LSWITCH $$88 if_header $$89 LBODY caseblock_list '}'.    (90)
 
-	.  reduce 90 (src line 768)
+	.  reduce 90 (src line 769)
 
 
 state 623
@@ -9777,13 +9777,13 @@ state 623
 	stmt_list:  stmt_list.';' stmt 
 
 	';'  shift 416
-	.  reduce 62 (src line 571)
+	.  reduce 62 (src line 572)
 
 
 state 624
 	case:  LCASE expr_or_type_list ':'.    (55)
 
-	.  reduce 55 (src line 473)
+	.  reduce 55 (src line 474)
 
 
 state 625
@@ -9918,7 +9918,7 @@ state 628
 	else: .    (86)
 
 	LELSE  shift 650
-	.  reduce 86 (src line 743)
+	.  reduce 86 (src line 744)
 
 	elseif  goto 649
 	else  goto 648
@@ -9954,7 +9954,7 @@ state 630
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1881)
+	.  reduce 284 (src line 1882)
 
 	sym  goto 123
 	expr  goto 402
@@ -9980,55 +9980,55 @@ state 630
 state 631
 	bare_complitexpr:  '{' start_complit braced_keyval_list '}'.    (143)
 
-	.  reduce 143 (src line 1040)
+	.  reduce 143 (src line 1041)
 
 
 state 632
 	pexpr_no_paren:  '(' expr_or_type ')' '{' start_complit braced_keyval_list '}'.    (138)
 
-	.  reduce 138 (src line 1004)
+	.  reduce 138 (src line 1005)
 
 
 state 633
 	structdcl:  '(' '*' embed ')' oliteral.    (234)
 
-	.  reduce 234 (src line 1606)
+	.  reduce 234 (src line 1607)
 
 
 state 634
 	structdcl:  '*' '(' embed ')' oliteral.    (235)
 
-	.  reduce 235 (src line 1613)
+	.  reduce 235 (src line 1614)
 
 
 state 635
 	indcl:  '(' oarg_type_list_ocomma ')' fnres.    (242)
 
-	.  reduce 242 (src line 1668)
+	.  reduce 242 (src line 1669)
 
 
 state 636
 	hidden_type_misc:  LMAP '[' hidden_type ']' hidden_type.    (321)
 
-	.  reduce 321 (src line 2036)
+	.  reduce 321 (src line 2037)
 
 
 state 637
 	hidden_structdcl_list:  hidden_structdcl_list ';' hidden_structdcl.    (349)
 
-	.  reduce 349 (src line 2227)
+	.  reduce 349 (src line 2228)
 
 
 state 638
 	hidden_structdcl:  sym hidden_type oliteral.    (332)
 
-	.  reduce 332 (src line 2110)
+	.  reduce 332 (src line 2111)
 
 
 state 639
 	hidden_interfacedcl_list:  hidden_interfacedcl_list ';' hidden_interfacedcl.    (351)
 
-	.  reduce 351 (src line 2237)
+	.  reduce 351 (src line 2238)
 
 
 state 640
@@ -10041,7 +10041,7 @@ state 640
 state 641
 	hidden_type_func:  LFUNC '(' ohidden_funarg_list ')' ohidden_funres.    (329)
 
-	.  reduce 329 (src line 2079)
+	.  reduce 329 (src line 2080)
 
 
 state 642
@@ -10081,7 +10081,7 @@ state 645
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1469)
+	.  reduce 212 (src line 1470)
 
 	sym  goto 123
 	dotname  goto 493
@@ -10193,13 +10193,13 @@ state 647
 state 648
 	if_stmt:  LIF $$78 if_header $$79 loop_body $$80 elseif_list else.    (81)
 
-	.  reduce 81 (src line 703)
+	.  reduce 81 (src line 704)
 
 
 state 649
 	elseif_list:  elseif_list elseif.    (85)
 
-	.  reduce 85 (src line 738)
+	.  reduce 85 (src line 739)
 
 
 state 650
@@ -10215,7 +10215,7 @@ state 650
 state 651
 	pexpr_no_paren:  pexpr '[' oexpr ':' oexpr ':' oexpr ']'.    (133)
 
-	.  reduce 133 (src line 974)
+	.  reduce 133 (src line 975)
 
 
 state 652
@@ -10240,7 +10240,7 @@ state 653
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2144)
+	.  reduce 335 (src line 2145)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -10253,13 +10253,13 @@ state 653
 state 654
 	hidden_constant:  '(' hidden_literal '+' hidden_literal ')'.    (343)
 
-	.  reduce 343 (src line 2197)
+	.  reduce 343 (src line 2198)
 
 
 state 655
 	hidden_funres:  '(' ohidden_funarg_list ')'.    (337)
 
-	.  reduce 337 (src line 2150)
+	.  reduce 337 (src line 2151)
 
 
 state 656
@@ -10277,7 +10277,7 @@ state 656
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2144)
+	.  reduce 335 (src line 2145)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -10290,51 +10290,51 @@ state 656
 state 657
 	fndcl:  '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres.    (206)
 
-	.  reduce 206 (src line 1368)
+	.  reduce 206 (src line 1369)
 
 
 state 658
 	case:  LCASE expr_or_type_list '=' expr ':'.    (56)
 
-	.  reduce 56 (src line 497)
+	.  reduce 56 (src line 498)
 
 
 state 659
 	case:  LCASE expr_or_type_list LCOLAS expr ':'.    (57)
 
-	.  reduce 57 (src line 515)
+	.  reduce 57 (src line 516)
 
 
 state 660
 	elseif:  LELSE LIF.$$82 if_header loop_body 
 	$$82: .    (82)
 
-	.  reduce 82 (src line 720)
+	.  reduce 82 (src line 721)
 
 	$$82  goto 665
 
 state 661
 	else:  LELSE compound_stmt.    (87)
 
-	.  reduce 87 (src line 747)
+	.  reduce 87 (src line 748)
 
 
 state 662
 	complitexpr:  '{' start_complit braced_keyval_list '}'.    (145)
 
-	.  reduce 145 (src line 1048)
+	.  reduce 145 (src line 1049)
 
 
 state 663
 	hidden_interfacedcl:  sym '(' ohidden_funarg_list ')' ohidden_funres.    (333)
 
-	.  reduce 333 (src line 2134)
+	.  reduce 333 (src line 2135)
 
 
 state 664
 	hidden_fndcl:  '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres.    (208)
 
-	.  reduce 208 (src line 1431)
+	.  reduce 208 (src line 1432)
 
 
 state 665
@@ -10360,7 +10360,7 @@ state 665
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1911)
+	.  reduce 294 (src line 1912)
 
 	sym  goto 123
 	expr  goto 48
@@ -10394,7 +10394,7 @@ state 666
 state 667
 	elseif:  LELSE LIF $$82 if_header loop_body.    (83)
 
-	.  reduce 83 (src line 725)
+	.  reduce 83 (src line 726)
 
 
 76 terminals, 142 nonterminals

From 85a15778005c8412005ef7366cf40b0a50ace5ba Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Wed, 13 May 2015 20:28:05 -0400
Subject: [PATCH 079/232] math/big, cmd/internal/gc/big: fix vet detected
 printf problem

Change-Id: I54425d8cbe0277d7a0c9d66c37f2128a0dfa6441
Reviewed-on: https://go-review.googlesource.com/10041
Run-TryBot: Minux Ma 
Reviewed-by: Robert Griesemer 
---
 src/cmd/internal/gc/big/float_test.go | 2 +-
 src/math/big/float_test.go            | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/cmd/internal/gc/big/float_test.go b/src/cmd/internal/gc/big/float_test.go
index 2a48ec4465..de79b07aaf 100644
--- a/src/cmd/internal/gc/big/float_test.go
+++ b/src/cmd/internal/gc/big/float_test.go
@@ -1656,7 +1656,7 @@ func TestFloatCmpSpecialValues(t *testing.T) {
 					want = +1
 				}
 				if got != want {
-					t.Errorf("(%g).Cmp(%g) = %s; want %s", x, y, got, want)
+					t.Errorf("(%g).Cmp(%g) = %v; want %v", x, y, got, want)
 				}
 			}
 		}
diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index 5b5a0247b1..5d241a503b 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -1659,7 +1659,7 @@ func TestFloatCmpSpecialValues(t *testing.T) {
 					want = +1
 				}
 				if got != want {
-					t.Errorf("(%g).Cmp(%g) = %s; want %s", x, y, got, want)
+					t.Errorf("(%g).Cmp(%g) = %v; want %v", x, y, got, want)
 				}
 			}
 		}

From b83b01110090c41fc24750ecabf0b87c5fbff233 Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Thu, 14 May 2015 12:26:27 +0900
Subject: [PATCH 080/232] net: fix vet missed format error in test

Change-Id: I73c0aeb4b27fec84149c8e89753b27ff2190eabf
Reviewed-on: https://go-review.googlesource.com/10074
Reviewed-by: Alex Brainman 
---
 src/net/error_test.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/net/error_test.go b/src/net/error_test.go
index c65d3f9d8a..772e0c7f5f 100644
--- a/src/net/error_test.go
+++ b/src/net/error_test.go
@@ -521,7 +521,7 @@ third:
 func TestFileError(t *testing.T) {
 	switch runtime.GOOS {
 	case "windows":
-		t.Skip("not supported on %s", runtime.GOOS)
+		t.Skipf("not supported on %s", runtime.GOOS)
 	}
 
 	f, err := ioutil.TempFile("", "go-nettest")

From 5b3739357aa409548a4c4f9ac7499726c8de9a23 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 7 May 2015 15:50:54 -0400
Subject: [PATCH 081/232] runtime: skip atomics in heapBitsSetType when GC is
 not running
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Suggested by Rick during code review of this code,
but separated out for easier diagnosis in case it causes
problems (and also easier rollback).

name                    old mean              new mean              delta
SetTypePtr              13.9ns × (0.98,1.05)   6.2ns × (0.99,1.01)  -55.18% (p=0.000)
SetTypePtr8             15.5ns × (0.95,1.10)  15.5ns × (0.99,1.05)     ~    (p=0.952)
SetTypePtr16            17.8ns × (0.99,1.05)  18.0ns × (1.00,1.00)     ~    (p=0.157)
SetTypePtr32            25.2ns × (0.99,1.01)  24.3ns × (0.99,1.01)   -3.86% (p=0.000)
SetTypePtr64            42.2ns × (0.93,1.13)  40.8ns × (0.99,1.01)     ~    (p=0.239)
SetTypePtr126           67.3ns × (1.00,1.00)  67.5ns × (0.99,1.02)     ~    (p=0.365)
SetTypePtr128           67.6ns × (1.00,1.01)  70.1ns × (0.97,1.10)     ~    (p=0.063)
SetTypePtrSlice          575ns × (0.98,1.06)   543ns × (0.95,1.17)   -5.54% (p=0.034)
SetTypeNode1            12.4ns × (0.98,1.09)  12.8ns × (0.99,1.01)   +3.40% (p=0.021)
SetTypeNode1Slice       97.1ns × (0.97,1.09)  89.5ns × (1.00,1.00)   -7.78% (p=0.000)
SetTypeNode8            29.8ns × (1.00,1.01)  17.7ns × (1.00,1.01)  -40.74% (p=0.000)
SetTypeNode8Slice        204ns × (0.99,1.04)   190ns × (0.97,1.06)   -6.96% (p=0.000)
SetTypeNode64           42.8ns × (0.99,1.01)  44.0ns × (0.95,1.12)     ~    (p=0.163)
SetTypeNode64Slice      1.00µs × (0.95,1.09)  0.98µs × (0.96,1.08)     ~    (p=0.356)
SetTypeNode64Dead       12.2ns × (0.99,1.04)  12.7ns × (1.00,1.01)   +4.34% (p=0.000)
SetTypeNode64DeadSlice  1.14µs × (0.94,1.11)  0.99µs × (0.99,1.03)  -13.74% (p=0.000)
SetTypeNode124          67.9ns × (0.99,1.03)  70.4ns × (0.95,1.15)     ~    (p=0.115)
SetTypeNode124Slice     1.76µs × (0.99,1.04)  1.88µs × (0.91,1.23)     ~    (p=0.096)
SetTypeNode126          67.7ns × (1.00,1.01)  68.2ns × (0.99,1.02)   +0.72% (p=0.014)
SetTypeNode126Slice     1.76µs × (1.00,1.01)  1.87µs × (0.93,1.15)   +6.15% (p=0.035)
SetTypeNode1024          462ns × (0.96,1.10)   451ns × (0.99,1.05)     ~    (p=0.224)
SetTypeNode1024Slice    14.4µs × (0.95,1.15)  14.2µs × (0.97,1.19)     ~    (p=0.676)

name                   old mean              new mean              delta
BinaryTree17            5.87s × (0.98,1.04)   5.87s × (0.98,1.03)    ~    (p=0.993)
Fannkuch11              4.39s × (0.99,1.01)   4.34s × (1.00,1.01)  -1.22% (p=0.000)
FmtFprintfEmpty        90.6ns × (0.97,1.06)  89.4ns × (0.97,1.03)    ~    (p=0.070)
FmtFprintfString        305ns × (0.98,1.02)   296ns × (0.99,1.02)  -2.94% (p=0.000)
FmtFprintfInt           276ns × (0.97,1.04)   270ns × (0.98,1.03)  -2.17% (p=0.001)
FmtFprintfIntInt        490ns × (0.97,1.05)   473ns × (0.99,1.02)  -3.59% (p=0.000)
FmtFprintfPrefixedInt   402ns × (0.99,1.02)   397ns × (0.99,1.01)  -1.15% (p=0.000)
FmtFprintfFloat         577ns × (0.99,1.01)   549ns × (0.99,1.01)  -4.78% (p=0.000)
FmtManyArgs            1.89µs × (0.99,1.02)  1.87µs × (0.99,1.01)  -1.43% (p=0.000)
GobDecode              15.2ms × (0.99,1.01)  14.7ms × (0.99,1.02)  -3.55% (p=0.000)
GobEncode              11.7ms × (0.98,1.04)  11.5ms × (0.99,1.02)  -1.63% (p=0.002)
Gzip                    647ms × (0.99,1.01)   647ms × (1.00,1.01)    ~    (p=0.486)
Gunzip                  142ms × (1.00,1.00)   143ms × (1.00,1.00)    ~    (p=0.234)
HTTPClientServer       90.7µs × (0.99,1.01)  90.4µs × (0.98,1.04)    ~    (p=0.331)
JSONEncode             31.9ms × (0.97,1.06)  31.6ms × (0.98,1.02)    ~    (p=0.206)
JSONDecode              110ms × (0.99,1.01)   112ms × (0.99,1.02)  +1.48% (p=0.000)
Mandelbrot200          6.00ms × (1.00,1.00)  6.01ms × (1.00,1.00)    ~    (p=0.058)
GoParse                6.63ms × (0.98,1.03)  6.61ms × (0.98,1.02)    ~    (p=0.353)
RegexpMatchEasy0_32     162ns × (0.99,1.01)   161ns × (1.00,1.00)  -0.33% (p=0.004)
RegexpMatchEasy0_1K     539ns × (0.99,1.01)   540ns × (0.99,1.02)    ~    (p=0.222)
RegexpMatchEasy1_32     139ns × (0.99,1.01)   140ns × (0.97,1.03)    ~    (p=0.054)
RegexpMatchEasy1_1K     886ns × (1.00,1.00)   887ns × (1.00,1.00)  +0.18% (p=0.001)
RegexpMatchMedium_32    252ns × (1.00,1.01)   252ns × (1.00,1.00)  +0.21% (p=0.010)
RegexpMatchMedium_1K   72.7µs × (1.00,1.01)  72.6µs × (1.00,1.00)    ~    (p=0.060)
RegexpMatchHard_32     3.84µs × (1.00,1.00)  3.84µs × (1.00,1.00)    ~    (p=0.065)
RegexpMatchHard_1K      117µs × (1.00,1.00)   117µs × (1.00,1.00)  -0.27% (p=0.000)
Revcomp                 916ms × (0.98,1.04)   909ms × (0.99,1.01)    ~    (p=0.054)
Template                126ms × (0.99,1.01)   128ms × (0.99,1.02)  +1.43% (p=0.000)
TimeParse               632ns × (0.99,1.01)   625ns × (1.00,1.01)  -1.05% (p=0.000)
TimeFormat              655ns × (0.99,1.02)   669ns × (0.99,1.02)  +2.01% (p=0.000)

Change-Id: I9477b7c9489c6fa98e860c190ce06cd73c53c6a1
Reviewed-on: https://go-review.googlesource.com/9829
Reviewed-by: Rick Hudson 
Reviewed-by: Austin Clements 
---
 src/runtime/mbitmap.go | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 5472d28e02..56e773ad5e 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -549,11 +549,19 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		// The bitmap byte is shared with the one-word object
 		// next to it, and concurrent GC might be marking that
 		// object, so we must use an atomic update.
+		// We can skip this if the GC is completely off.
+		// Note that there is some marking that happens during
+		// gcphase == _GCscan, for completely scalar objects,
+		// so it is not safe to check just for the marking phases.
 		// TODO(rsc): It may make sense to set all the pointer bits
 		// when initializing the span, and then the atomicor8 here
 		// goes away - heapBitsSetType would be a no-op
 		// in that case.
-		atomicor8(h.bitp, bitPointer<>= 2
 		nb -= 2
 		// Note: no bitMarker in hb because the first two words don't get markers from us.
-		atomicor8(hbitp, uint8(hb))
+		if gcphase == _GCoff {
+			*hbitp |= uint8(hb)
+		} else {
+			atomicor8(hbitp, uint8(hb))
+		}
 		hbitp = subtractb(hbitp, 1)
 		if w += 2; w >= nw {
 			// We know that there is more data, because we handled 2-word objects above.

From 94934f843ee8b0a4a09dc336d4e2b57601b34206 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Sun, 10 May 2015 20:22:32 -0400
Subject: [PATCH 082/232] runtime: rewrite addb/subtractb to be simpler to
 compile; introduce add1, subtract1

This reduces the depth of the inlining at a particular call site.
The inliner introduces many temporary variables, and the compiler can do
a better job with fewer. Being verbose in the bodies of these helper functions
seems like a reasonable tradeoff: the uses are still just as readable, and
they run faster in some important cases.

Change-Id: I5323976ed3704d0acd18fb31176cfbf5ba23a89c
Reviewed-on: https://go-review.googlesource.com/9883
Reviewed-by: Rick Hudson 
Reviewed-by: Austin Clements 
---
 src/runtime/mbitmap.go | 66 ++++++++++++++++++++++++++++--------------
 1 file changed, 45 insertions(+), 21 deletions(-)

diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 56e773ad5e..db43e482d2 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -83,13 +83,37 @@ const (
 // addb returns the byte pointer p+n.
 //go:nowritebarrier
 func addb(p *byte, n uintptr) *byte {
-	return (*byte)(add(unsafe.Pointer(p), n))
+	// Note: wrote out full expression instead of calling add(p, n)
+	// to reduce the number of temporaries generated by the
+	// compiler for this trivial expression during inlining.
+	return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + n))
 }
 
 // subtractb returns the byte pointer p-n.
 //go:nowritebarrier
 func subtractb(p *byte, n uintptr) *byte {
-	return (*byte)(add(unsafe.Pointer(p), -n))
+	// Note: wrote out full expression instead of calling add(p, -n)
+	// to reduce the number of temporaries generated by the
+	// compiler for this trivial expression during inlining.
+	return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) - n))
+}
+
+// add1 returns the byte pointer p+1.
+//go:nowritebarrier
+func add1(p *byte) *byte {
+	// Note: wrote out full expression instead of calling addb(p, 1)
+	// to reduce the number of temporaries generated by the
+	// compiler for this trivial expression during inlining.
+	return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + 1))
+}
+
+// subtract1 returns the byte pointer p-1.
+//go:nowritebarrier
+func subtract1(p *byte) *byte {
+	// Note: wrote out full expression instead of calling subtractb(p, 1)
+	// to reduce the number of temporaries generated by the
+	// compiler for this trivial expression during inlining.
+	return (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) - 1))
 }
 
 // mHeap_MapBits is called each time arena_used is extended.
@@ -220,7 +244,7 @@ func (h heapBits) next() heapBits {
 	if h.shift < 3*heapBitsShift {
 		return heapBits{h.bitp, h.shift + heapBitsShift}
 	}
-	return heapBits{subtractb(h.bitp, 1), 0}
+	return heapBits{subtract1(h.bitp), 0}
 }
 
 // forward returns the heapBits describing n pointer-sized words ahead of h in memory.
@@ -291,7 +315,7 @@ func (h heapBits) hasPointers(size uintptr) bool {
 	if h.shift == 0 {
 		return b&(bitMarked<<(2*heapBitsShift)) != 0
 	}
-	return uint32(*subtractb(h.bitp, 1))&bitMarked != 0
+	return uint32(*subtract1(h.bitp))&bitMarked != 0
 }
 
 // isCheckmarked reports whether the heap bits have the checkmarked bit set.
@@ -378,7 +402,7 @@ func (h heapBits) initCheckmarkSpan(size, n, total uintptr) {
 		bitp := h.bitp
 		for i := uintptr(0); i < n; i += 4 {
 			*bitp &^= bitPointerAll
-			bitp = subtractb(bitp, 1)
+			bitp = subtract1(bitp)
 		}
 		return
 	}
@@ -402,7 +426,7 @@ func (h heapBits) clearCheckmarkSpan(size, n, total uintptr) {
 		bitp := h.bitp
 		for i := uintptr(0); i < n; i += 4 {
 			*bitp |= bitPointerAll
-			bitp = subtractb(bitp, 1)
+			bitp = subtract1(bitp)
 		}
 	}
 }
@@ -449,7 +473,7 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) {
 				f(base + (i+3)*ptrSize)
 			}
 			*bitp = uint8(x)
-			bitp = subtractb(bitp, 1)
+			bitp = subtract1(bitp)
 		}
 
 	case size%(4*ptrSize) == 0:
@@ -499,7 +523,7 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) {
 				x &^= (bitMarked|bitPointer)<<(2*heapBitsShift) | (bitMarked|bitPointer)<<(3*heapBitsShift)
 				f(base + (i+1)*size)
 				if size > 2*ptrSize {
-					*subtractb(bitp, 1) = 0
+					*subtract1(bitp) = 0
 				}
 			}
 			*bitp = uint8(x)
@@ -590,7 +614,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 				unrollgcprog_m(typ)
 			})
 		}
-		ptrmask = addb(ptrmask, 1) // skip the unroll flag byte
+		ptrmask = add1(ptrmask) // skip the unroll flag byte
 	}
 
 	// Heap bitmap bits for 2-word object are only 4 bits,
@@ -687,7 +711,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			nb = typ.ptrdata / ptrSize
 			for i := uintptr(0); i < nb; i += 8 {
 				b |= uintptr(*p) << i
-				p = addb(p, 1)
+				p = add1(p)
 			}
 			nb = typ.size / ptrSize
 
@@ -724,7 +748,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 	}
 	if p != nil {
 		b = uintptr(*p)
-		p = addb(p, 1)
+		p = add1(p)
 		nb = 8
 	}
 
@@ -776,7 +800,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			goto Phase3
 		}
 		*hbitp = uint8(hb)
-		hbitp = subtractb(hbitp, 1)
+		hbitp = subtract1(hbitp)
 		b >>= 4
 		nb -= 4
 
@@ -800,7 +824,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		} else {
 			atomicor8(hbitp, uint8(hb))
 		}
-		hbitp = subtractb(hbitp, 1)
+		hbitp = subtract1(hbitp)
 		if w += 2; w >= nw {
 			// We know that there is more data, because we handled 2-word objects above.
 			// This must be at least a 6-word object. If we're out of pointer words,
@@ -830,7 +854,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			break
 		}
 		*hbitp = uint8(hb)
-		hbitp = subtractb(hbitp, 1)
+		hbitp = subtract1(hbitp)
 		b >>= 4
 
 		// Load more bits. b has nb right now.
@@ -840,7 +864,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			// and the next iteration will consume 8 bits,
 			// leaving us with the same nb the next time we're here.
 			b |= uintptr(*p) << nb
-			p = addb(p, 1)
+			p = add1(p)
 		} else if p == nil {
 			// Almost as fast path: track bit count and refill from pbits.
 			// For short repetitions.
@@ -856,7 +880,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			nb += endnb
 			if nb < 8 {
 				b |= uintptr(*ptrmask) << nb
-				p = addb(ptrmask, 1)
+				p = add1(ptrmask)
 			} else {
 				nb -= 8
 				p = ptrmask
@@ -870,7 +894,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			break
 		}
 		*hbitp = uint8(hb)
-		hbitp = subtractb(hbitp, 1)
+		hbitp = subtract1(hbitp)
 		b >>= 4
 	}
 
@@ -891,11 +915,11 @@ Phase3:
 	// The first is hb, the rest are zero.
 	if w <= nw {
 		*hbitp = uint8(hb)
-		hbitp = subtractb(hbitp, 1)
+		hbitp = subtract1(hbitp)
 		hb = 0 // for possible final half-byte below
 		for w += 4; w <= nw; w += 4 {
 			*hbitp = 0
-			hbitp = subtractb(hbitp, 1)
+			hbitp = subtract1(hbitp)
 		}
 	}
 
@@ -1021,9 +1045,9 @@ func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace bool) *byte {
 			throw("unrollgcprog: unknown instruction")
 
 		case insData:
-			prog = addb(prog, 1)
+			prog = add1(prog)
 			siz := int(*prog)
-			prog = addb(prog, 1)
+			prog = add1(prog)
 			p := (*[1 << 30]byte)(unsafe.Pointer(prog))
 			for i := 0; i < siz; i++ {
 				v := p[i/8] >> (uint(i) % 8) & 1

From ecfe42cab0c22da35aed6752cafaf0d3c7bb44d4 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Sun, 10 May 2015 13:43:51 -0400
Subject: [PATCH 083/232] runtime: keep pointer bits set always in 1-word spans
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

It's dumb to clear them in initSpan, set them in heapBitsSetType,
clear them in heapBitsSweepSpan, set them again in heapBitsSetType,
clear them again in heapBitsSweepSpan, and so on.

Set them in initSpan and be done with it (until the span is reused
for objects of a different size).

This avoids an atomic operation in a common case (one-word allocation).
Suggested by rlh.

name                   old mean              new mean              delta
BinaryTree17            5.87s × (0.97,1.03)   5.93s × (0.98,1.04)              ~    (p=0.056)
Fannkuch11              4.34s × (1.00,1.01)   4.41s × (1.00,1.00)            +1.42% (p=0.000)
FmtFprintfEmpty        86.1ns × (0.98,1.03)  88.9ns × (0.95,1.14)              ~    (p=0.066)
FmtFprintfString        292ns × (0.97,1.04)   284ns × (0.98,1.03)            -2.64% (p=0.000)
FmtFprintfInt           271ns × (0.98,1.06)   274ns × (0.98,1.05)              ~    (p=0.148)
FmtFprintfIntInt        478ns × (0.98,1.05)   487ns × (0.98,1.03)            +1.85% (p=0.004)
FmtFprintfPrefixedInt   397ns × (0.98,1.05)   394ns × (0.98,1.02)              ~    (p=0.184)
FmtFprintfFloat         553ns × (0.99,1.02)   543ns × (0.99,1.01)            -1.71% (p=0.000)
FmtManyArgs            1.90µs × (0.98,1.05)  1.88µs × (0.99,1.01)            -0.97% (p=0.037)
GobDecode              15.1ms × (0.99,1.01)  15.3ms × (0.99,1.01)            +0.78% (p=0.001)
GobEncode              11.7ms × (0.98,1.05)  11.6ms × (0.99,1.02)            -1.39% (p=0.009)
Gzip                    646ms × (1.00,1.01)   647ms × (1.00,1.01)              ~    (p=0.120)
Gunzip                  142ms × (1.00,1.00)   142ms × (1.00,1.00)              ~    (p=0.068)
HTTPClientServer       89.7µs × (0.99,1.01)  90.1µs × (0.98,1.03)              ~    (p=0.224)
JSONEncode             31.3ms × (0.99,1.01)  31.2ms × (0.99,1.02)              ~    (p=0.149)
JSONDecode              113ms × (0.99,1.01)   111ms × (0.99,1.01)            -1.25% (p=0.000)
Mandelbrot200          6.01ms × (1.00,1.00)  6.01ms × (1.00,1.00)            +0.09% (p=0.015)
GoParse                6.63ms × (0.98,1.03)  6.55ms × (0.99,1.02)            -1.10% (p=0.006)
RegexpMatchEasy0_32     161ns × (1.00,1.00)   161ns × (1.00,1.00)  (sample has zero variance)
RegexpMatchEasy0_1K     539ns × (0.99,1.01)   563ns × (0.99,1.01)            +4.51% (p=0.000)
RegexpMatchEasy1_32     140ns × (0.99,1.01)   141ns × (0.99,1.01)            +1.34% (p=0.000)
RegexpMatchEasy1_1K     886ns × (1.00,1.01)   888ns × (1.00,1.00)            +0.20% (p=0.003)
RegexpMatchMedium_32    252ns × (1.00,1.02)   255ns × (0.99,1.01)            +1.32% (p=0.000)
RegexpMatchMedium_1K   72.7µs × (1.00,1.00)  72.6µs × (1.00,1.00)              ~    (p=0.296)
RegexpMatchHard_32     3.84µs × (1.00,1.01)  3.84µs × (1.00,1.00)              ~    (p=0.339)
RegexpMatchHard_1K      117µs × (1.00,1.01)   117µs × (1.00,1.00)            -0.28% (p=0.022)
Revcomp                 914ms × (0.99,1.01)   909ms × (0.99,1.01)            -0.49% (p=0.031)
Template                128ms × (0.99,1.01)   127ms × (0.99,1.01)            -1.10% (p=0.000)
TimeParse               628ns × (0.99,1.01)   639ns × (0.99,1.01)            +1.69% (p=0.000)
TimeFormat              660ns × (0.99,1.01)   662ns × (0.99,1.02)              ~    (p=0.287)

Change-Id: I3127b0ab89708267c74aa7d0eae1db1a1bcdfda5
Reviewed-on: https://go-review.googlesource.com/9884
Reviewed-by: Austin Clements 
---
 src/runtime/mbitmap.go | 49 +++++++++++++++++++++++++-----------------
 1 file changed, 29 insertions(+), 20 deletions(-)

diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index db43e482d2..2d2abca643 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -387,6 +387,18 @@ func (h heapBits) initSpan(size, n, total uintptr) {
 		throw("initSpan: unaligned length")
 	}
 	nbyte := total / heapBitmapScale
+	if ptrSize == 8 && size == ptrSize {
+		end := h.bitp
+		bitp := subtractb(end, nbyte-1)
+		for {
+			*bitp = bitPointerAll
+			if bitp == end {
+				break
+			}
+			bitp = add1(bitp)
+		}
+		return
+	}
 	memclr(unsafe.Pointer(subtractb(h.bitp, nbyte-1)), nbyte)
 }
 
@@ -443,33 +455,34 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) {
 	switch {
 	default:
 		throw("heapBitsSweepSpan")
-	case size == ptrSize:
+	case ptrSize == 8 && size == ptrSize:
 		// Consider mark bits in all four 2-bit entries of each bitmap byte.
 		bitp := h.bitp
 		for i := uintptr(0); i < n; i += 4 {
 			x := uint32(*bitp)
+			// Note that unlike the other size cases, we leave the pointer bits set here.
+			// These are initialized during initSpan when the span is created and left
+			// in place the whole time the span is used for pointer-sized objects.
+			// That lets heapBitsSetType avoid an atomic update to set the pointer bit
+			// during allocation.
 			if x&bitMarked != 0 {
 				x &^= bitMarked
 			} else {
-				x &^= bitPointer
 				f(base + i*ptrSize)
 			}
 			if x&(bitMarked<
Date: Wed, 13 May 2015 12:59:33 -0700
Subject: [PATCH 084/232] text/template: need to validate type when an argument
 is a function call

Missed a case; just need to call validateType.

Fixes #10800.

Change-Id: I81997ca7a9feb1be31c8b47e631b32712d7ffb86
Reviewed-on: https://go-review.googlesource.com/10031
Reviewed-by: Andrew Gerrand 
---
 src/text/template/exec.go      | 2 +-
 src/text/template/exec_test.go | 8 ++++++++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/text/template/exec.go b/src/text/template/exec.go
index e6e1287993..ebafb4b5dc 100644
--- a/src/text/template/exec.go
+++ b/src/text/template/exec.go
@@ -660,7 +660,7 @@ func (s *state) evalArg(dot reflect.Value, typ reflect.Type, n parse.Node) refle
 	case *parse.PipeNode:
 		return s.validateType(s.evalPipeline(dot, arg), typ)
 	case *parse.IdentifierNode:
-		return s.evalFunction(dot, arg, arg, nil, zero)
+		return s.validateType(s.evalFunction(dot, arg, arg, nil, zero), typ)
 	case *parse.ChainNode:
 		return s.validateType(s.evalChainNode(dot, arg, nil, zero), typ)
 	}
diff --git a/src/text/template/exec_test.go b/src/text/template/exec_test.go
index abce27ff3d..0f1ad62380 100644
--- a/src/text/template/exec_test.go
+++ b/src/text/template/exec_test.go
@@ -531,6 +531,8 @@ var execTests = []execTest{
 	{"bug14a", "{{(nil).True}}", "", tVal, false},
 	{"bug14b", "{{$x := nil}}{{$x.anything}}", "", tVal, false},
 	{"bug14c", `{{$x := (1.0)}}{{$y := ("hello")}}{{$x.anything}}{{$y.true}}`, "", tVal, false},
+	// Didn't call validateType on function results. Issue 10800.
+	{"bug15", "{{valueString returnInt}}", "", tVal, false},
 }
 
 func zeroArgs() string {
@@ -570,6 +572,11 @@ func valueString(v string) string {
 	return "value is ignored"
 }
 
+// returnInt returns an int
+func returnInt() int {
+	return 7
+}
+
 func add(args ...int) int {
 	sum := 0
 	for _, x := range args {
@@ -611,6 +618,7 @@ func testExecute(execTests []execTest, template *Template, t *testing.T) {
 		"makemap":     makemap,
 		"mapOfThree":  mapOfThree,
 		"oneArg":      oneArg,
+		"returnInt":   returnInt,
 		"stringer":    stringer,
 		"typeOf":      typeOf,
 		"valueString": valueString,

From d2130573e871dc7437b14713c2a7107a20bb0390 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 14 May 2015 11:20:08 -0700
Subject: [PATCH 085/232] go/scanner: don't return previous comment as literal
 value if none is expected

Fixes #10213.

Change-Id: Ia587dd51eea702058da926717ad305792c9fc42b
Reviewed-on: https://go-review.googlesource.com/10081
Reviewed-by: Alan Donovan 
---
 src/go/scanner/scanner.go      |  3 ++-
 src/go/scanner/scanner_test.go | 35 ++++++++++++++++++++++++++++++++++
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/src/go/scanner/scanner.go b/src/go/scanner/scanner.go
index cec82ea10e..e9476c4dee 100644
--- a/src/go/scanner/scanner.go
+++ b/src/go/scanner/scanner.go
@@ -706,13 +706,14 @@ scanAgain:
 					s.insertSemi = false // newline consumed
 					return pos, token.SEMICOLON, "\n"
 				}
-				lit = s.scanComment()
+				comment := s.scanComment()
 				if s.mode&ScanComments == 0 {
 					// skip comment
 					s.insertSemi = false // newline consumed
 					goto scanAgain
 				}
 				tok = token.COMMENT
+				lit = comment
 			} else {
 				tok = s.switch2(token.QUO, token.QUO_ASSIGN)
 			}
diff --git a/src/go/scanner/scanner_test.go b/src/go/scanner/scanner_test.go
index fc450d8a6e..0d21905166 100644
--- a/src/go/scanner/scanner_test.go
+++ b/src/go/scanner/scanner_test.go
@@ -734,6 +734,41 @@ func TestScanErrors(t *testing.T) {
 	}
 }
 
+// Verify that no comments show up as literal values when skipping comments.
+func TestIssue10213(t *testing.T) {
+	var src = `
+		var (
+			A = 1 // foo
+		)
+
+		var (
+			B = 2
+			// foo
+		)
+
+		var C = 3 // foo
+
+		var D = 4
+		// foo
+
+		func anycode() {
+		// foo
+		}
+	`
+	var s Scanner
+	s.Init(fset.AddFile("", fset.Base(), len(src)), []byte(src), nil, 0)
+	for {
+		pos, tok, lit := s.Scan()
+		class := tokenclass(tok)
+		if lit != "" && class != keyword && class != literal && tok != token.SEMICOLON {
+			t.Errorf("%s: tok = %s, lit = %q", fset.Position(pos), tok, lit)
+		}
+		if tok <= token.EOF {
+			break
+		}
+	}
+}
+
 func BenchmarkScan(b *testing.B) {
 	b.StopTimer()
 	fset := token.NewFileSet()

From 138498183cb53382c115fc761cbf2898fb72eac8 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 14 May 2015 11:36:19 -0700
Subject: [PATCH 086/232] go/types: remove _ imports that are not needed
 anymore

Change-Id: I392b0a0083d6bea80a65f9eef46dd06b02a70e1b
Reviewed-on: https://go-review.googlesource.com/10082
Reviewed-by: Alan Donovan 
---
 src/go/types/eval_test.go | 1 -
 src/go/types/self_test.go | 1 -
 2 files changed, 2 deletions(-)

diff --git a/src/go/types/eval_test.go b/src/go/types/eval_test.go
index bc27a8bb23..36e1cb954e 100644
--- a/src/go/types/eval_test.go
+++ b/src/go/types/eval_test.go
@@ -14,7 +14,6 @@ import (
 	"strings"
 	"testing"
 
-	_ "go/internal/gcimporter"
 	. "go/types"
 )
 
diff --git a/src/go/types/self_test.go b/src/go/types/self_test.go
index e52c5afdc8..4ff4e4d4a8 100644
--- a/src/go/types/self_test.go
+++ b/src/go/types/self_test.go
@@ -15,7 +15,6 @@ import (
 	"testing"
 	"time"
 
-	_ "go/internal/gcimporter"
 	. "go/types"
 )
 

From 30aacd4ce2d3ecd802aa281d54267608ca35a34c Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 14 May 2015 14:23:12 -0400
Subject: [PATCH 087/232] runtime: add Node128, Node130 benchmarks

Change-Id: I815a7ceeea48cc652b3c8568967665af39b02834
Reviewed-on: https://go-review.googlesource.com/10045
Reviewed-by: Brad Fitzpatrick 
---
 src/runtime/gc_test.go | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/src/runtime/gc_test.go b/src/runtime/gc_test.go
index f049bad499..e3e0c3a583 100644
--- a/src/runtime/gc_test.go
+++ b/src/runtime/gc_test.go
@@ -308,6 +308,32 @@ func BenchmarkSetTypeNode126Slice(b *testing.B) {
 	benchSetType(b, make([]Node126, 32))
 }
 
+type Node128 struct {
+	Value       [128]uintptr
+	Left, Right *byte
+}
+
+func BenchmarkSetTypeNode128(b *testing.B) {
+	benchSetType(b, new(Node128))
+}
+
+func BenchmarkSetTypeNode128Slice(b *testing.B) {
+	benchSetType(b, make([]Node128, 32))
+}
+
+type Node130 struct {
+	Value       [130]uintptr
+	Left, Right *byte
+}
+
+func BenchmarkSetTypeNode130(b *testing.B) {
+	benchSetType(b, new(Node130))
+}
+
+func BenchmarkSetTypeNode130Slice(b *testing.B) {
+	benchSetType(b, make([]Node130, 32))
+}
+
 type Node1024 struct {
 	Value       [1024]uintptr
 	Left, Right *byte

From 1e7f57954baf8fa991e4551504856897f438f490 Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Thu, 14 May 2015 00:58:06 -0400
Subject: [PATCH 088/232] go/build: introduce go1.5 build tag

Change-Id: Iab2f8e1c4443f39b79c1c63a7a30062074b48764
Signed-off-by: Shenghou Ma 
Reviewed-on: https://go-review.googlesource.com/10042
Reviewed-by: Brad Fitzpatrick 
---
 src/go/build/build.go | 6 +-----
 src/go/build/doc.go   | 1 +
 2 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/go/build/build.go b/src/go/build/build.go
index d91eb0b24d..820434bc4a 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -296,11 +296,7 @@ func defaultContext() Context {
 	// in all releases >= Go 1.x. Code that requires Go 1.x or later should
 	// say "+build go1.x", and code that should only be built before Go 1.x
 	// (perhaps it is the stub to use in that case) should say "+build !go1.x".
-	//
-	// When we reach Go 1.5 the line will read
-	//	c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5"}
-	// and so on.
-	c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4"}
+	c.ReleaseTags = []string{"go1.1", "go1.2", "go1.3", "go1.4", "go1.5"}
 
 	switch os.Getenv("CGO_ENABLED") {
 	case "1":
diff --git a/src/go/build/doc.go b/src/go/build/doc.go
index 78e17b220a..233f8b989d 100644
--- a/src/go/build/doc.go
+++ b/src/go/build/doc.go
@@ -101,6 +101,7 @@
 //	- "go1.2", from Go version 1.2 onward
 //	- "go1.3", from Go version 1.3 onward
 //	- "go1.4", from Go version 1.4 onward
+//	- "go1.5", from Go version 1.5 onward
 //	- any additional words listed in ctxt.BuildTags
 //
 // If a file's name, after stripping the extension and a possible _test suffix,

From a901d7fb8f3126f5fbaf3be097449769b490503a Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Thu, 14 May 2015 13:03:02 -0700
Subject: [PATCH 089/232] cmd/dist: support test filtering via repurposed env
 variable, negation

For upcoming sharded ARM builders.

Updates #10029

Change-Id: I3b1df9560be697c514a8ced0462814d406e23132
Reviewed-on: https://go-review.googlesource.com/10055
Reviewed-by: Ian Lance Taylor 
Run-TryBot: Brad Fitzpatrick 
TryBot-Result: Gobot Gobot 
---
 src/cmd/dist/test.go | 28 +++++++++++++++++++---------
 1 file changed, 19 insertions(+), 9 deletions(-)

diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 848790ad2c..addf61dad9 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -14,6 +14,7 @@ import (
 	"os/exec"
 	"path/filepath"
 	"regexp"
+	"runtime"
 	"strconv"
 	"strings"
 	"time"
@@ -24,7 +25,9 @@ func cmdtest() {
 	flag.BoolVar(&t.listMode, "list", false, "list available tests")
 	flag.BoolVar(&t.noRebuild, "no-rebuild", false, "don't rebuild std and cmd packages")
 	flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
-	flag.StringVar(&t.runRxStr, "run", "", "run only those tests matching the regular expression; empty means to run all")
+	flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"),
+		"run only those tests matching the regular expression; empty means to run all. "+
+			"Special exception: if the string begins with '!', the match is inverted.")
 	xflagparse(0)
 	t.run()
 }
@@ -35,6 +38,7 @@ type tester struct {
 	noRebuild bool
 	runRxStr  string
 	runRx     *regexp.Regexp
+	runRxWant bool
 	banner    string // prefix, or "" for none
 
 	goroot     string
@@ -129,6 +133,19 @@ func (t *tester) run() {
 	}
 
 	if t.runRxStr != "" {
+		// Temporary (2015-05-14) special case for "std",
+		// which the plan9 builder was using for ages. Delete
+		// this once we update dashboard/builders.go to use a
+		// regexp instead.
+		if runtime.GOOS == "plan9" && t.runRxStr == "std" {
+			t.runRxStr = "^go_test:"
+		}
+		if t.runRxStr[0] == '!' {
+			t.runRxWant = false
+			t.runRxStr = t.runRxStr[1:]
+		} else {
+			t.runRxWant = true
+		}
 		t.runRx = regexp.MustCompile(t.runRxStr)
 	}
 
@@ -147,7 +164,7 @@ func (t *tester) run() {
 
 	var lastHeading string
 	for _, dt := range t.tests {
-		if t.runRx != nil && !t.runRx.MatchString(dt.name) {
+		if t.runRx != nil && (t.runRx.MatchString(dt.name) != t.runRxWant) {
 			t.partial = true
 			continue
 		}
@@ -214,13 +231,6 @@ func (t *tester) registerTests() {
 		})
 	}
 
-	// Old hack for when Plan 9 on GCE was too slow.
-	// We're keeping this until test sharding (Issue 10029) is finished, though.
-	if os.Getenv("GOTESTONLY") == "std" {
-		t.partial = true
-		return
-	}
-
 	// Runtime CPU tests.
 	testName := "runtime:cpu124"
 	t.tests = append(t.tests, distTest{

From a4292c31206f798a3a4a8498006f4a055f581860 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 14 May 2015 16:21:01 -0400
Subject: [PATCH 090/232] api: refresh next.txt

Change-Id: I5e902bb3a3a51620b21840783087ed3cc410dbc5
Reviewed-on: https://go-review.googlesource.com/10048
Reviewed-by: Brad Fitzpatrick 
---
 api/next.txt | 172 ++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 130 insertions(+), 42 deletions(-)

diff --git a/api/next.txt b/api/next.txt
index cebbe877b6..b8e09df5c2 100644
--- a/api/next.txt
+++ b/api/next.txt
@@ -1,6 +1,7 @@
 pkg archive/zip, method (*Writer) SetOffset(int64)
 pkg bufio, method (*Reader) Discard(int) (int, error)
 pkg bufio, method (ReadWriter) Discard(int) (int, error)
+pkg bytes, func LastIndexByte([]uint8, uint8) int
 pkg bytes, method (*Buffer) Cap() int
 pkg bytes, method (*Reader) Size() int64
 pkg crypto, type Decrypter interface { Decrypt, Public }
@@ -18,16 +19,56 @@ pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 49196
 pkg crypto/tls, const TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 uint16
 pkg crypto/tls, const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 49200
 pkg crypto/tls, const TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 uint16
+pkg crypto/tls, method (*Config) SetSessionTicketKeys([][32]uint8)
+pkg crypto/tls, type Certificate struct, SignedCertificateTimestamps [][]uint8
+pkg crypto/tls, type ConnectionState struct, OCSPResponse []uint8
+pkg crypto/tls, type ConnectionState struct, SignedCertificateTimestamps [][]uint8
+pkg crypto/x509, method (*CertificateRequest) CheckSignature() error
+pkg crypto/x509, type Certificate struct, UnhandledCriticalExtensions []asn1.ObjectIdentifier
 pkg crypto/x509/pkix, type Name struct, ExtraNames []AttributeTypeAndValue
 pkg database/sql, method (*DB) Stats() DBStats
 pkg database/sql, type DBStats struct
 pkg database/sql, type DBStats struct, OpenConnections int
+pkg debug/dwarf, const ClassAddress = 1
+pkg debug/dwarf, const ClassAddress Class
+pkg debug/dwarf, const ClassBlock = 2
+pkg debug/dwarf, const ClassBlock Class
+pkg debug/dwarf, const ClassConstant = 3
+pkg debug/dwarf, const ClassConstant Class
+pkg debug/dwarf, const ClassExprLoc = 4
+pkg debug/dwarf, const ClassExprLoc Class
+pkg debug/dwarf, const ClassFlag = 5
+pkg debug/dwarf, const ClassFlag Class
+pkg debug/dwarf, const ClassLinePtr = 6
+pkg debug/dwarf, const ClassLinePtr Class
+pkg debug/dwarf, const ClassLocListPtr = 7
+pkg debug/dwarf, const ClassLocListPtr Class
+pkg debug/dwarf, const ClassMacPtr = 8
+pkg debug/dwarf, const ClassMacPtr Class
+pkg debug/dwarf, const ClassRangeListPtr = 9
+pkg debug/dwarf, const ClassRangeListPtr Class
+pkg debug/dwarf, const ClassReference = 10
+pkg debug/dwarf, const ClassReference Class
+pkg debug/dwarf, const ClassReferenceAlt = 13
+pkg debug/dwarf, const ClassReferenceAlt Class
+pkg debug/dwarf, const ClassReferenceSig = 11
+pkg debug/dwarf, const ClassReferenceSig Class
+pkg debug/dwarf, const ClassString = 12
+pkg debug/dwarf, const ClassString Class
+pkg debug/dwarf, const ClassStringAlt = 14
+pkg debug/dwarf, const ClassStringAlt Class
 pkg debug/dwarf, method (*Data) LineReader(*Entry) (*LineReader, error)
+pkg debug/dwarf, method (*Entry) AttrField(Attr) *Field
 pkg debug/dwarf, method (*LineReader) Next(*LineEntry) error
 pkg debug/dwarf, method (*LineReader) Reset()
 pkg debug/dwarf, method (*LineReader) Seek(LineReaderPos)
 pkg debug/dwarf, method (*LineReader) SeekPC(uint64, *LineEntry) error
 pkg debug/dwarf, method (*LineReader) Tell() LineReaderPos
+pkg debug/dwarf, method (*Reader) AddressSize() int
+pkg debug/dwarf, method (Class) GoString() string
+pkg debug/dwarf, method (Class) String() string
+pkg debug/dwarf, type Class int
+pkg debug/dwarf, type Field struct, Class Class
 pkg debug/dwarf, type LineEntry struct
 pkg debug/dwarf, type LineEntry struct, Address uint64
 pkg debug/dwarf, type LineEntry struct, BasicBlock bool
@@ -227,48 +268,52 @@ pkg encoding/base64, var RawURLEncoding *Encoding
 pkg encoding/json, type UnmarshalTypeError struct, Offset int64
 pkg flag, func UnquoteUsage(*Flag) (string, string)
 pkg go/ast, type EmptyStmt struct, Implicit bool
-pkg go/exact, const Bool = 1
-pkg go/exact, const Bool Kind
-pkg go/exact, const Complex = 5
-pkg go/exact, const Complex Kind
-pkg go/exact, const Float = 4
-pkg go/exact, const Float Kind
-pkg go/exact, const Int = 3
-pkg go/exact, const Int Kind
-pkg go/exact, const String = 2
-pkg go/exact, const String Kind
-pkg go/exact, const Unknown = 0
-pkg go/exact, const Unknown Kind
-pkg go/exact, func BinaryOp(Value, token.Token, Value) Value
-pkg go/exact, func BitLen(Value) int
-pkg go/exact, func BoolVal(Value) bool
-pkg go/exact, func Bytes(Value) []uint8
-pkg go/exact, func Compare(Value, token.Token, Value) bool
-pkg go/exact, func Denom(Value) Value
-pkg go/exact, func Float32Val(Value) (float32, bool)
-pkg go/exact, func Float64Val(Value) (float64, bool)
-pkg go/exact, func Imag(Value) Value
-pkg go/exact, func Int64Val(Value) (int64, bool)
-pkg go/exact, func MakeBool(bool) Value
-pkg go/exact, func MakeFloat64(float64) Value
-pkg go/exact, func MakeFromBytes([]uint8) Value
-pkg go/exact, func MakeFromLiteral(string, token.Token) Value
-pkg go/exact, func MakeImag(Value) Value
-pkg go/exact, func MakeInt64(int64) Value
-pkg go/exact, func MakeString(string) Value
-pkg go/exact, func MakeUint64(uint64) Value
-pkg go/exact, func MakeUnknown() Value
-pkg go/exact, func Num(Value) Value
-pkg go/exact, func Real(Value) Value
-pkg go/exact, func Shift(Value, token.Token, uint) Value
-pkg go/exact, func Sign(Value) int
-pkg go/exact, func StringVal(Value) string
-pkg go/exact, func Uint64Val(Value) (uint64, bool)
-pkg go/exact, func UnaryOp(token.Token, Value, int) Value
-pkg go/exact, type Kind int
-pkg go/exact, type Value interface, Kind() Kind
-pkg go/exact, type Value interface, String() string
-pkg go/exact, type Value interface, unexported methods
+pkg go/build, type Package struct, PkgTargetRoot string
+pkg go/constant, const Bool = 1
+pkg go/constant, const Bool Kind
+pkg go/constant, const Complex = 5
+pkg go/constant, const Complex Kind
+pkg go/constant, const Float = 4
+pkg go/constant, const Float Kind
+pkg go/constant, const Int = 3
+pkg go/constant, const Int Kind
+pkg go/constant, const String = 2
+pkg go/constant, const String Kind
+pkg go/constant, const Unknown = 0
+pkg go/constant, const Unknown Kind
+pkg go/constant, func BinaryOp(Value, token.Token, Value) Value
+pkg go/constant, func BitLen(Value) int
+pkg go/constant, func BoolVal(Value) bool
+pkg go/constant, func Bytes(Value) []uint8
+pkg go/constant, func Compare(Value, token.Token, Value) bool
+pkg go/constant, func Denom(Value) Value
+pkg go/constant, func Float32Val(Value) (float32, bool)
+pkg go/constant, func Float64Val(Value) (float64, bool)
+pkg go/constant, func Imag(Value) Value
+pkg go/constant, func Int64Val(Value) (int64, bool)
+pkg go/constant, func MakeBool(bool) Value
+pkg go/constant, func MakeFloat64(float64) Value
+pkg go/constant, func MakeFromBytes([]uint8) Value
+pkg go/constant, func MakeFromLiteral(string, token.Token, uint) Value
+pkg go/constant, func MakeImag(Value) Value
+pkg go/constant, func MakeInt64(int64) Value
+pkg go/constant, func MakeString(string) Value
+pkg go/constant, func MakeUint64(uint64) Value
+pkg go/constant, func MakeUnknown() Value
+pkg go/constant, func Num(Value) Value
+pkg go/constant, func Real(Value) Value
+pkg go/constant, func Shift(Value, token.Token, uint) Value
+pkg go/constant, func Sign(Value) int
+pkg go/constant, func StringVal(Value) string
+pkg go/constant, func Uint64Val(Value) (uint64, bool)
+pkg go/constant, func UnaryOp(token.Token, Value, uint) Value
+pkg go/constant, type Kind int
+pkg go/constant, type Value interface, Kind() Kind
+pkg go/constant, type Value interface, String() string
+pkg go/constant, type Value interface, unexported methods
+pkg go/importer, func Default() types.Importer
+pkg go/importer, func For(string, Lookup) types.Importer
+pkg go/importer, type Lookup func(string) (io.ReadCloser, error)
 pkg go/types, const Bool = 1
 pkg go/types, const Bool BasicKind
 pkg go/types, const Byte = 8
@@ -376,6 +421,7 @@ pkg go/types, func New(string) Type
 pkg go/types, func NewArray(Type, int64) *Array
 pkg go/types, func NewChan(ChanDir, Type) *Chan
 pkg go/types, func NewChecker(*Config, *token.FileSet, *Package, *Info) *Checker
+pkg go/types, func NewConst(token.Pos, *Package, string, Type, constant.Value) *Const
 pkg go/types, func NewConst(token.Pos, *Package, string, Type, exact.Value) *Const
 pkg go/types, func NewField(token.Pos, *Package, string, Type, bool) *Var
 pkg go/types, func NewFunc(token.Pos, *Package, string, *Signature) *Func
@@ -432,6 +478,7 @@ pkg go/types, method (*Const) Pkg() *Package
 pkg go/types, method (*Const) Pos() token.Pos
 pkg go/types, method (*Const) String() string
 pkg go/types, method (*Const) Type() Type
+pkg go/types, method (*Const) Val() constant.Value
 pkg go/types, method (*Const) Val() exact.Value
 pkg go/types, method (*Func) Exported() bool
 pkg go/types, method (*Func) FullName() string
@@ -590,6 +637,7 @@ pkg go/types, type Config struct, Error func(error)
 pkg go/types, type Config struct, FakeImportC bool
 pkg go/types, type Config struct, IgnoreFuncBodies bool
 pkg go/types, type Config struct, Import Importer
+pkg go/types, type Config struct, Importer Importer
 pkg go/types, type Config struct, Packages map[string]*Package
 pkg go/types, type Config struct, Sizes Sizes
 pkg go/types, type Const struct
@@ -600,6 +648,8 @@ pkg go/types, type Error struct, Pos token.Pos
 pkg go/types, type Error struct, Soft bool
 pkg go/types, type Func struct
 pkg go/types, type Importer func(map[string]*Package, string) (*Package, error)
+pkg go/types, type Importer interface { Import }
+pkg go/types, type Importer interface, Import(string) (*Package, error)
 pkg go/types, type Info struct
 pkg go/types, type Info struct, Defs map[*ast.Ident]Object
 pkg go/types, type Info struct, Implicits map[ast.Node]Object
@@ -649,6 +699,7 @@ pkg go/types, type Type interface, String() string
 pkg go/types, type Type interface, Underlying() Type
 pkg go/types, type TypeAndValue struct
 pkg go/types, type TypeAndValue struct, Type Type
+pkg go/types, type TypeAndValue struct, Value constant.Value
 pkg go/types, type TypeAndValue struct, Value exact.Value
 pkg go/types, type TypeName struct
 pkg go/types, type Var struct
@@ -690,6 +741,18 @@ pkg image/color, type CMYK struct, K uint8
 pkg image/color, type CMYK struct, M uint8
 pkg image/color, type CMYK struct, Y uint8
 pkg image/color, var CMYKModel Model
+pkg image/gif, const DisposalBackground = 2
+pkg image/gif, const DisposalBackground ideal-int
+pkg image/gif, const DisposalNone = 1
+pkg image/gif, const DisposalNone ideal-int
+pkg image/gif, const DisposalPrevious = 3
+pkg image/gif, const DisposalPrevious ideal-int
+pkg image/gif, type GIF struct, BackgroundIndex uint8
+pkg image/gif, type GIF struct, Config image.Config
+pkg image/gif, type GIF struct, Disposal []uint8
+pkg io, func CopyBuffer(Writer, Reader, []uint8) (int64, error)
+pkg log, const LUTC = 32
+pkg log, const LUTC ideal-int
 pkg log, func Output(int, string) error
 pkg log, method (*Logger) SetOutput(io.Writer)
 pkg math/big, const Above = 1
@@ -716,6 +779,7 @@ pkg math/big, const ToPositiveInf = 5
 pkg math/big, const ToPositiveInf RoundingMode
 pkg math/big, const ToZero = 2
 pkg math/big, const ToZero RoundingMode
+pkg math/big, func Jacobi(*Int, *Int) int
 pkg math/big, func NewFloat(float64) *Float
 pkg math/big, func ParseFloat(string, int, uint, RoundingMode) (*Float, int, error)
 pkg math/big, func ScanFloat(io.ByteScanner, int, uint, RoundingMode) (*Float, int, error)
@@ -758,6 +822,7 @@ pkg math/big, method (*Float) Signbit() bool
 pkg math/big, method (*Float) String() string
 pkg math/big, method (*Float) Sub(*Float, *Float) *Float
 pkg math/big, method (*Float) Uint64() (uint64, Accuracy)
+pkg math/big, method (*Int) ModSqrt(*Int, *Int) *Int
 pkg math/big, method (Accuracy) String() string
 pkg math/big, method (ErrNaN) Error() string
 pkg math/big, method (RoundingMode) String() string
@@ -765,25 +830,48 @@ pkg math/big, type Accuracy int8
 pkg math/big, type ErrNaN struct
 pkg math/big, type Float struct
 pkg math/big, type RoundingMode uint8
+pkg mime, const BEncoding = 98
+pkg mime, const BEncoding WordEncoder
+pkg mime, const QEncoding = 113
+pkg mime, const QEncoding WordEncoder
 pkg mime, func ExtensionsByType(string) ([]string, error)
+pkg mime, method (*WordDecoder) Decode(string) (string, error)
+pkg mime, method (*WordDecoder) DecodeHeader(string) (string, error)
+pkg mime, method (WordEncoder) Encode(string, string) string
+pkg mime, type WordDecoder struct
+pkg mime, type WordDecoder struct, CharsetReader func(string, io.Reader) (io.Reader, error)
+pkg mime, type WordEncoder uint8
+pkg mime/quotedprintable, func NewReader(io.Reader) *Reader
 pkg mime/quotedprintable, func NewReader(io.Reader) io.Reader
 pkg mime/quotedprintable, func NewWriter(io.Writer) *Writer
+pkg mime/quotedprintable, method (*Reader) Read([]uint8) (int, error)
 pkg mime/quotedprintable, method (*Writer) Close() error
 pkg mime/quotedprintable, method (*Writer) Write([]uint8) (int, error)
+pkg mime/quotedprintable, type Reader struct
 pkg mime/quotedprintable, type Writer struct
 pkg mime/quotedprintable, type Writer struct, Binary bool
+pkg net, func SocketConn(*os.File, SocketAddr) (Conn, error)
+pkg net, func SocketPacketConn(*os.File, SocketAddr) (PacketConn, error)
+pkg net, type OpError struct, Source Addr
+pkg net, type SocketAddr interface { Addr, Raw }
+pkg net, type SocketAddr interface, Addr([]uint8) Addr
+pkg net, type SocketAddr interface, Raw(Addr) []uint8
 pkg net/http/fcgi, var ErrConnClosed error
 pkg net/http/fcgi, var ErrRequestAborted error
 pkg net/http/pprof, func Trace(http.ResponseWriter, *http.Request)
 pkg net/smtp, method (*Client) TLSConnectionState() (tls.ConnectionState, bool)
+pkg os, func LookupEnv(string) (string, bool)
 pkg os/signal, func Ignore(...os.Signal)
 pkg os/signal, func Reset(...os.Signal)
+pkg reflect, func ArrayOf(int, Type) Type
+pkg reflect, func FuncOf([]Type, []Type, bool) Type
 pkg runtime, func ReadTrace() []uint8
 pkg runtime, func StartTrace() error
 pkg runtime, func StopTrace()
 pkg runtime/pprof, func StartTrace(io.Writer) error
 pkg runtime/pprof, func StopTrace()
 pkg strings, func Compare(string, string) int
+pkg strings, func LastIndexByte(string, uint8) int
 pkg strings, method (*Reader) Size() int64
 pkg syscall (darwin-386), type SysProcAttr struct, Ctty int
 pkg syscall (darwin-386), type SysProcAttr struct, Foreground bool

From 5abdc24b006b98f32d5390691733a5c12b521b11 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 14 May 2015 15:14:57 -0700
Subject: [PATCH 091/232] go/types: remove "vendoring" script - not useful
 anymore

Change-Id: I4f4e6b99a22054666cd2284679cb0eca7f1042b8
Reviewed-on: https://go-review.googlesource.com/10086
Reviewed-by: Rob Pike 
---
 src/go/types.bash | 97 -----------------------------------------------
 1 file changed, 97 deletions(-)
 delete mode 100644 src/go/types.bash

diff --git a/src/go/types.bash b/src/go/types.bash
deleted file mode 100644
index 1a384d410a..0000000000
--- a/src/go/types.bash
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/bin/bash
-
-# Copyright 2015 The Go Authors.  All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Run this script to update the packages ./exact and ./types
-# in the $GOROOT/src/go directory. They are vendored from the
-# original sources in x/tools. Imports are renamed as needed.
-#
-# Delete this script once go/exact and go/types don't exist anymore in x/tools.
-#
-# NOTE(adonovan): the standard packages have intentionally diverged
-# from x/tools, so this script is a unlikely to be useful.  Upstream
-# changes should be cherry-picked in to the standard library.
-
-set -e
-
-### Safety first.
-if [ ! -d "$GOPATH" ]; then
-	echo 2>&1 '$GOPATH must be set.'
-	exit 1
-fi
-if [ ! -d "$GOROOT" ]; then
-	echo 2>&1 '$GOROOT must be set.'
-	exit 1
-fi
-
-GODIR=$GOROOT/src/go
-
-function vendor() (
-	SRCDIR=$GOPATH/src/golang.org/x/tools/$1
-	DSTDIR=$GODIR/$2
-
-	echo 2>&1 "vendoring $SRCDIR => $DSTDIR"
-
-	# create directory
-	rm -rf $DSTDIR
-	mkdir -p $DSTDIR
-	cd $DSTDIR
-
-	# copy go sources and update import paths
-	for f in $SRCDIR/*.go; do
-		# copy $f and update imports
-		sed -e 's|"golang.org/x/tools/go/exact"|"go/exact"|' \
-		    -e 's|"golang.org/x/tools/go/types"|"go/types"|' \
-		    -e 's|"golang.org/x/tools/go/gcimporter"|"go/internal/gcimporter"|' \
-		    $f | gofmt > tmp.go
-		mv -f tmp.go `basename $f`
-	done
-
-	# copy testdata, if any
-	if [ -e $SRCDIR/testdata ]; then
-		cp -R $SRCDIR/testdata/ $DSTDIR/testdata/
-	fi
-)
-
-function install() (
-	PKG=$GODIR/$1
-
-	echo 2>&1 "installing $PKG"
-	cd $PKG
-	go install
-)
-
-function test() (
-	PKG=$GODIR/$1
-
-	echo 2>&1 "testing $PKG"
-	cd $PKG
-	if ! go test; then
-		echo 2>&1 "TESTING $PKG FAILED"
-		exit 1
-	fi
-)
-
-### go/exact
-vendor go/exact exact
-test exact
-install exact
-
-### go/types
-vendor go/types types
-# cannot test w/o gcimporter
-install types
-
-### go/gcimporter
-vendor go/gcimporter internal/gcimporter
-test internal/gcimporter
-install internal/gcimporter
-
-### test go/types (requires gcimporter)
-test types
-
-# All done.
-echo 2>&1 "DONE"
-exit 0

From fb7e2449b62e16313fec593329954f25e595dfff Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Thu, 14 May 2015 16:02:53 -0700
Subject: [PATCH 092/232] doc: update go1.5.txt

Change-Id: Idbceaa44f4c823510632381b36b42302e63d8a29
Reviewed-on: https://go-review.googlesource.com/10057
Reviewed-by: Brad Fitzpatrick 
---
 doc/go1.5.txt | 30 +++++++++++++++++-------------
 1 file changed, 17 insertions(+), 13 deletions(-)

diff --git a/doc/go1.5.txt b/doc/go1.5.txt
index 571a9f17d0..10095d0c18 100644
--- a/doc/go1.5.txt
+++ b/doc/go1.5.txt
@@ -1,25 +1,27 @@
 Overall:
-toolchain in Go
-new GC
+- toolchain in Go
+- new GC
 
 Language:
-permit omission of key type in map composite literals where key is a composite literal (https://golang.org/cl/2591)
+- permit omission of key type in map composite literals where key is a composite literal (https://golang.org/cl/2591)
 
 Build:
-Go 1.4 required to build (https://golang.org/cl/2470, https://golang.org/cl/2993)
+- Go 1.4 required to build (https://golang.org/cl/2470, https://golang.org/cl/2993)
 
 New Ports:
-darwin/arm, a.k.a iOS. (https://golang.org/cl/2118, 2119, 3273, 2121, 2122, ..., 2127)
-darwin/arm64
-linux/arm64 (cgo is supported, but only with external linking)
-openbsd/arm (no cgo or external linking)
-The port to Snow Leopard (OS X 10.6) is no longer actively maintained.
-
-Runtime:
-goroutine scheduling order changed; never guaranteed by language, but can break tests that implicitly assume a specific execution order
+- darwin/arm, a.k.a iOS. (https://golang.org/cl/2118, 2119, 3273, 2121, 2122, ..., 2127)
+- darwin/arm64
+- linux/arm64 (cgo is supported, but only with external linking)
+- openbsd/arm (no cgo or external linking)
 
 Removed Ports:
-dragonfly/386 (https://golang.org/cl/7543)
+- dragonfly/386 (https://golang.org/cl/7543)
+- The port to Snow Leopard (OS X 10.6) is no longer actively maintained.
+
+Runtime:
+- goroutine scheduling order changed; never guaranteed by language,
+  but can break tests that implicitly assume a specific execution
+  order
 
 API additions and behavior changes:
 
@@ -107,6 +109,8 @@ cmd/gc: allocate backing storage for non-escaping interfaces on stack (https://g
 encoding/xml: avoid an allocation for tags without attributes (https://golang.org/cl/4160)
 image: many optimizations
 runtime: add ARM runtime.cmpstring and bytes.Compare (https://golang.org/cl/8010)
+runtime: do not scan maps when k/v do not contain pointers (https://golang.org/cl/3288)
+runtime: reduce thrashing of gs between ps (https://golang.org/cl/9872)
 sort: number of Sort performance optimizations (https://golang.org/cl/2100, https://golang.org/cl/2614, ...)
 strconv: optimize decimal to string conversion (https://golang.org/cl/2105)
 strconv: optimize float to string conversion (https://golang.org/cl/5600)

From 83c7b60f2745e7b0e35e762e3fc2702f59169a7b Mon Sep 17 00:00:00 2001
From: Rob Pike 
Date: Thu, 14 May 2015 15:45:10 -0700
Subject: [PATCH 093/232] cmd/doc: trim unexported methods from interfaces

Fixes #10856.

Change-Id: I5de65b8dd94eec3451ee0ba9c75698cdd88f5fea
Reviewed-on: https://go-review.googlesource.com/10088
Reviewed-by: Andrew Gerrand 
---
 src/cmd/doc/pkg.go | 56 ++++++++++++++++++++++++++--------------------
 1 file changed, 32 insertions(+), 24 deletions(-)

diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 835313e902..53f336ff1c 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -345,7 +345,7 @@ func (pkg *Package) symbolDoc(symbol string) {
 		}
 		decl := typ.Decl
 		spec := pkg.findTypeSpec(decl, typ.Name)
-		trimUnexportedFields(spec)
+		trimUnexportedElems(spec)
 		// If there are multiple types defined, reduce to just this one.
 		if len(decl.Specs) > 1 {
 			decl.Specs = []ast.Spec{spec}
@@ -366,22 +366,26 @@ func (pkg *Package) symbolDoc(symbol string) {
 	}
 }
 
-// trimUnexportedFields modifies spec in place to elide unexported fields (unless
-// the unexported flag is set). If spec is not a structure declartion, nothing happens.
-func trimUnexportedFields(spec *ast.TypeSpec) {
+// trimUnexportedElems modifies spec in place to elide unexported fields from
+// structs and methods from interfaces (unless the unexported flag is set).
+func trimUnexportedElems(spec *ast.TypeSpec) {
 	if *unexported {
-		// We're printing all fields.
-		return
+		return fields
 	}
-	// It must be a struct for us to care. (We show unexported methods in interfaces.)
-	structType, ok := spec.Type.(*ast.StructType)
-	if !ok {
-		return
+	switch typ := spec.Type.(type) {
+	case *ast.StructType:
+		typ.Fields = trimUnexportedFields(typ.Fields, "fields")
+	case *ast.InterfaceType:
+		typ.Methods = trimUnexportedFields(typ.Methods, "methods")
 	}
+}
+
+// trimUnexportedFields returns the field list trimmed of unexported fields.
+func trimUnexportedFields(fields *ast.FieldList, what string) *ast.FieldList {
 	trimmed := false
-	list := make([]*ast.Field, 0, len(structType.Fields.List))
-	for _, field := range structType.Fields.List {
-		// Trims if any is unexported. Fine in practice.
+	list := make([]*ast.Field, 0, len(fields.List))
+	for _, field := range fields.List {
+		// Trims if any is unexported. Good enough in practice.
 		ok := true
 		for _, name := range field.Names {
 			if !isExported(name.Name) {
@@ -394,19 +398,23 @@ func trimUnexportedFields(spec *ast.TypeSpec) {
 			list = append(list, field)
 		}
 	}
-	if trimmed {
-		unexportedField := &ast.Field{
-			Type: ast.NewIdent(""), // Hack: printer will treat this as a field with a named type.
-			Comment: &ast.CommentGroup{
-				List: []*ast.Comment{
-					&ast.Comment{
-						Text: "// Has unexported fields.\n",
-					},
+	if !trimmed {
+		return fields
+	}
+	unexportedField := &ast.Field{
+		Type: ast.NewIdent(""), // Hack: printer will treat this as a field with a named type.
+		Comment: &ast.CommentGroup{
+			List: []*ast.Comment{
+				&ast.Comment{
+					Text: fmt.Sprintf("// Has unexported %s.\n", what),
 				},
 			},
-		}
-		list = append(list, unexportedField)
-		structType.Fields.List = list
+		},
+	}
+	return &ast.FieldList{
+		Opening: fields.Opening,
+		List:    append(list, unexportedField),
+		Closing: fields.Closing,
 	}
 }
 

From 5069452d6d23c9d1725305746ca948602f7a597c Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Thu, 14 May 2015 03:26:31 -0400
Subject: [PATCH 094/232] syscall: fix F_SETLK{,W} on linux/ppc64

Change-Id: Ia81675b0f01ceafada32bdd2bc59088016a7421e
Reviewed-on: https://go-review.googlesource.com/10043
Reviewed-by: Ian Lance Taylor 
---
 src/syscall/zerrors_linux_ppc64.go | 4 ++--
 src/syscall/ztypes_linux_ppc64.go  | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/syscall/zerrors_linux_ppc64.go b/src/syscall/zerrors_linux_ppc64.go
index 15e0770c18..fbaf0dc9af 100644
--- a/src/syscall/zerrors_linux_ppc64.go
+++ b/src/syscall/zerrors_linux_ppc64.go
@@ -366,9 +366,9 @@ const (
 	F_SETFD                          = 0x2
 	F_SETFL                          = 0x4
 	F_SETLEASE                       = 0x400
-	F_SETLK                          = 0xd
+	F_SETLK                          = 0x6
 	F_SETLK64                        = 0xd
-	F_SETLKW                         = 0xe
+	F_SETLKW                         = 0x7
 	F_SETLKW64                       = 0xe
 	F_SETOWN                         = 0x8
 	F_SETOWN_EX                      = 0xf
diff --git a/src/syscall/ztypes_linux_ppc64.go b/src/syscall/ztypes_linux_ppc64.go
index fe438364d4..bce0350b86 100644
--- a/src/syscall/ztypes_linux_ppc64.go
+++ b/src/syscall/ztypes_linux_ppc64.go
@@ -549,7 +549,7 @@ type Sysinfo_t struct {
 	Totalhigh uint64
 	Freehigh  uint64
 	Unit      uint32
-	X_f       [0]byte
+	X_f       [0]uint8
 	Pad_cgo_1 [4]byte
 }
 

From 37eb1d1964049df2feb847ccef501f2807fdf618 Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Fri, 15 May 2015 09:39:16 +0900
Subject: [PATCH 095/232] cmd/doc: fix build

Change-Id: Ic8437a1d2aeb424d6d5ce9e608c1293bba4c7bbc
Reviewed-on: https://go-review.googlesource.com/10093
Run-TryBot: Mikio Hara 
Reviewed-by: Josh Bleecher Snyder 
---
 src/cmd/doc/pkg.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index 53f336ff1c..ed4b0b82db 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -370,7 +370,7 @@ func (pkg *Package) symbolDoc(symbol string) {
 // structs and methods from interfaces (unless the unexported flag is set).
 func trimUnexportedElems(spec *ast.TypeSpec) {
 	if *unexported {
-		return fields
+		return
 	}
 	switch typ := spec.Type.(type) {
 	case *ast.StructType:

From 38631846bf6f5c290bdef15c22a09695fd6018c4 Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Thu, 14 May 2015 21:01:52 -0400
Subject: [PATCH 096/232] syscall: add explicit build tags

Auto-generated using the following bash script:
for i in z*_*_*.go; do
        goosgoarch=`basename ${i/${i/_*/}_/} .go`
        goos=${goosgoarch/_*/}
        goarch=${goosgoarch/*_/}
        echo $i $goos $goarch
        [ "$goos" = "windows" ] && continue
        sed -i -e "/^package /i\/\/ +build $goarch,$goos\n" "$i"
done

Change-Id: I756fee551d1698080e4591fed8f058ae0450aaa5
Signed-off-by: Shenghou Ma 
Reviewed-on: https://go-review.googlesource.com/10113
Reviewed-by: Ian Lance Taylor 
---
 src/syscall/zerrors_darwin_386.go       | 2 ++
 src/syscall/zerrors_darwin_amd64.go     | 2 ++
 src/syscall/zerrors_darwin_arm.go       | 2 ++
 src/syscall/zerrors_darwin_arm64.go     | 2 ++
 src/syscall/zerrors_dragonfly_amd64.go  | 2 ++
 src/syscall/zerrors_freebsd_386.go      | 2 ++
 src/syscall/zerrors_freebsd_amd64.go    | 2 ++
 src/syscall/zerrors_freebsd_arm.go      | 2 ++
 src/syscall/zerrors_linux_386.go        | 2 ++
 src/syscall/zerrors_linux_amd64.go      | 2 ++
 src/syscall/zerrors_linux_arm.go        | 2 ++
 src/syscall/zerrors_linux_arm64.go      | 2 ++
 src/syscall/zerrors_linux_ppc64.go      | 2 ++
 src/syscall/zerrors_linux_ppc64le.go    | 2 ++
 src/syscall/zerrors_netbsd_386.go       | 2 ++
 src/syscall/zerrors_netbsd_amd64.go     | 2 ++
 src/syscall/zerrors_netbsd_arm.go       | 2 ++
 src/syscall/zerrors_openbsd_386.go      | 2 ++
 src/syscall/zerrors_openbsd_amd64.go    | 2 ++
 src/syscall/zerrors_openbsd_arm.go      | 2 ++
 src/syscall/zerrors_solaris_amd64.go    | 2 ++
 src/syscall/zsyscall_darwin_386.go      | 2 ++
 src/syscall/zsyscall_darwin_amd64.go    | 2 ++
 src/syscall/zsyscall_darwin_arm.go      | 2 ++
 src/syscall/zsyscall_darwin_arm64.go    | 2 ++
 src/syscall/zsyscall_dragonfly_amd64.go | 2 ++
 src/syscall/zsyscall_freebsd_386.go     | 2 ++
 src/syscall/zsyscall_freebsd_amd64.go   | 2 ++
 src/syscall/zsyscall_freebsd_arm.go     | 2 ++
 src/syscall/zsyscall_linux_386.go       | 2 ++
 src/syscall/zsyscall_linux_amd64.go     | 2 ++
 src/syscall/zsyscall_linux_arm.go       | 2 ++
 src/syscall/zsyscall_linux_arm64.go     | 2 ++
 src/syscall/zsyscall_linux_ppc64.go     | 2 ++
 src/syscall/zsyscall_linux_ppc64le.go   | 2 ++
 src/syscall/zsyscall_nacl_386.go        | 2 ++
 src/syscall/zsyscall_nacl_amd64p32.go   | 2 ++
 src/syscall/zsyscall_nacl_arm.go        | 2 ++
 src/syscall/zsyscall_netbsd_386.go      | 2 ++
 src/syscall/zsyscall_netbsd_amd64.go    | 2 ++
 src/syscall/zsyscall_netbsd_arm.go      | 2 ++
 src/syscall/zsyscall_openbsd_386.go     | 2 ++
 src/syscall/zsyscall_openbsd_amd64.go   | 2 ++
 src/syscall/zsyscall_openbsd_arm.go     | 2 ++
 src/syscall/zsyscall_plan9_386.go       | 2 ++
 src/syscall/zsyscall_plan9_amd64.go     | 2 ++
 src/syscall/zsyscall_solaris_amd64.go   | 2 ++
 src/syscall/zsysnum_darwin_386.go       | 2 ++
 src/syscall/zsysnum_darwin_amd64.go     | 2 ++
 src/syscall/zsysnum_darwin_arm.go       | 2 ++
 src/syscall/zsysnum_darwin_arm64.go     | 2 ++
 src/syscall/zsysnum_dragonfly_amd64.go  | 2 ++
 src/syscall/zsysnum_freebsd_386.go      | 2 ++
 src/syscall/zsysnum_freebsd_amd64.go    | 2 ++
 src/syscall/zsysnum_freebsd_arm.go      | 2 ++
 src/syscall/zsysnum_linux_386.go        | 2 ++
 src/syscall/zsysnum_linux_amd64.go      | 2 ++
 src/syscall/zsysnum_linux_arm.go        | 2 ++
 src/syscall/zsysnum_linux_arm64.go      | 2 ++
 src/syscall/zsysnum_linux_ppc64.go      | 2 ++
 src/syscall/zsysnum_linux_ppc64le.go    | 2 ++
 src/syscall/zsysnum_netbsd_386.go       | 2 ++
 src/syscall/zsysnum_netbsd_amd64.go     | 2 ++
 src/syscall/zsysnum_netbsd_arm.go       | 2 ++
 src/syscall/zsysnum_openbsd_386.go      | 2 ++
 src/syscall/zsysnum_openbsd_amd64.go    | 2 ++
 src/syscall/zsysnum_openbsd_arm.go      | 2 ++
 src/syscall/zsysnum_solaris_amd64.go    | 2 ++
 src/syscall/ztypes_darwin_386.go        | 2 ++
 src/syscall/ztypes_darwin_amd64.go      | 2 ++
 src/syscall/ztypes_darwin_arm.go        | 2 ++
 src/syscall/ztypes_darwin_arm64.go      | 2 ++
 src/syscall/ztypes_dragonfly_amd64.go   | 2 ++
 src/syscall/ztypes_freebsd_386.go       | 2 ++
 src/syscall/ztypes_freebsd_amd64.go     | 2 ++
 src/syscall/ztypes_freebsd_arm.go       | 2 ++
 src/syscall/ztypes_linux_386.go         | 2 ++
 src/syscall/ztypes_linux_amd64.go       | 2 ++
 src/syscall/ztypes_linux_arm.go         | 2 ++
 src/syscall/ztypes_linux_arm64.go       | 2 ++
 src/syscall/ztypes_linux_ppc64.go       | 2 ++
 src/syscall/ztypes_linux_ppc64le.go     | 2 ++
 src/syscall/ztypes_netbsd_386.go        | 2 ++
 src/syscall/ztypes_netbsd_amd64.go      | 2 ++
 src/syscall/ztypes_netbsd_arm.go        | 2 ++
 src/syscall/ztypes_openbsd_386.go       | 2 ++
 src/syscall/ztypes_openbsd_amd64.go     | 2 ++
 src/syscall/ztypes_openbsd_arm.go       | 2 ++
 src/syscall/ztypes_solaris_amd64.go     | 2 ++
 89 files changed, 178 insertions(+)

diff --git a/src/syscall/zerrors_darwin_386.go b/src/syscall/zerrors_darwin_386.go
index bb3a1610c0..debadaa9ce 100644
--- a/src/syscall/zerrors_darwin_386.go
+++ b/src/syscall/zerrors_darwin_386.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m32 _const.go
 
+// +build 386,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_darwin_amd64.go b/src/syscall/zerrors_darwin_amd64.go
index 05ab48ee32..d4262ba55a 100644
--- a/src/syscall/zerrors_darwin_amd64.go
+++ b/src/syscall/zerrors_darwin_amd64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build amd64,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_darwin_arm.go b/src/syscall/zerrors_darwin_arm.go
index 7e800d4259..a64f3735e2 100644
--- a/src/syscall/zerrors_darwin_arm.go
+++ b/src/syscall/zerrors_darwin_arm.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- _const.go
 
+// +build arm,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_darwin_arm64.go b/src/syscall/zerrors_darwin_arm64.go
index a3841a277a..98f431c4e7 100644
--- a/src/syscall/zerrors_darwin_arm64.go
+++ b/src/syscall/zerrors_darwin_arm64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build arm64,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_dragonfly_amd64.go b/src/syscall/zerrors_dragonfly_amd64.go
index 59bff751cb..15d300fd2d 100644
--- a/src/syscall/zerrors_dragonfly_amd64.go
+++ b/src/syscall/zerrors_dragonfly_amd64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build amd64,dragonfly
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_freebsd_386.go b/src/syscall/zerrors_freebsd_386.go
index cd3aa80a9c..bbad05f069 100644
--- a/src/syscall/zerrors_freebsd_386.go
+++ b/src/syscall/zerrors_freebsd_386.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m32 _const.go
 
+// +build 386,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_freebsd_amd64.go b/src/syscall/zerrors_freebsd_amd64.go
index 9edce6e2fa..b36125b6d3 100644
--- a/src/syscall/zerrors_freebsd_amd64.go
+++ b/src/syscall/zerrors_freebsd_amd64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build amd64,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_freebsd_arm.go b/src/syscall/zerrors_freebsd_arm.go
index f29dd057b6..0c844f1387 100644
--- a/src/syscall/zerrors_freebsd_arm.go
+++ b/src/syscall/zerrors_freebsd_arm.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- _const.go
 
+// +build arm,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_linux_386.go b/src/syscall/zerrors_linux_386.go
index 7aa8ff07a7..d433a4f1a4 100644
--- a/src/syscall/zerrors_linux_386.go
+++ b/src/syscall/zerrors_linux_386.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m32 _const.go
 
+// +build 386,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_linux_amd64.go b/src/syscall/zerrors_linux_amd64.go
index 94d051d8aa..dd86a3b0a1 100644
--- a/src/syscall/zerrors_linux_amd64.go
+++ b/src/syscall/zerrors_linux_amd64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build amd64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_linux_arm.go b/src/syscall/zerrors_linux_arm.go
index dcaaef7423..2a9c0f93c1 100644
--- a/src/syscall/zerrors_linux_arm.go
+++ b/src/syscall/zerrors_linux_arm.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- _const.go
 
+// +build arm,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_linux_arm64.go b/src/syscall/zerrors_linux_arm64.go
index f3d708be9c..35d9acefe5 100644
--- a/src/syscall/zerrors_linux_arm64.go
+++ b/src/syscall/zerrors_linux_arm64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- _const.go
 
+// +build arm64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_linux_ppc64.go b/src/syscall/zerrors_linux_ppc64.go
index fbaf0dc9af..1c769cdc77 100644
--- a/src/syscall/zerrors_linux_ppc64.go
+++ b/src/syscall/zerrors_linux_ppc64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build ppc64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_linux_ppc64le.go b/src/syscall/zerrors_linux_ppc64le.go
index 17c4c4cf3a..73727a4197 100644
--- a/src/syscall/zerrors_linux_ppc64le.go
+++ b/src/syscall/zerrors_linux_ppc64le.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build ppc64le,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_netbsd_386.go b/src/syscall/zerrors_netbsd_386.go
index 1e3dff7fac..6f32def661 100644
--- a/src/syscall/zerrors_netbsd_386.go
+++ b/src/syscall/zerrors_netbsd_386.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m32 _const.go
 
+// +build 386,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_netbsd_amd64.go b/src/syscall/zerrors_netbsd_amd64.go
index 1469d00b78..a6d1701f8c 100644
--- a/src/syscall/zerrors_netbsd_amd64.go
+++ b/src/syscall/zerrors_netbsd_amd64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build amd64,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_netbsd_arm.go b/src/syscall/zerrors_netbsd_arm.go
index 1a88c0d225..7f99279e55 100644
--- a/src/syscall/zerrors_netbsd_arm.go
+++ b/src/syscall/zerrors_netbsd_arm.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -marm _const.go
 
+// +build arm,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_openbsd_386.go b/src/syscall/zerrors_openbsd_386.go
index 0829834943..540d310e9f 100644
--- a/src/syscall/zerrors_openbsd_386.go
+++ b/src/syscall/zerrors_openbsd_386.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m32 _const.go
 
+// +build 386,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_openbsd_amd64.go b/src/syscall/zerrors_openbsd_amd64.go
index e9fa37cdee..ae5b8c955a 100644
--- a/src/syscall/zerrors_openbsd_amd64.go
+++ b/src/syscall/zerrors_openbsd_amd64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build amd64,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_openbsd_arm.go b/src/syscall/zerrors_openbsd_arm.go
index 5376fe6e85..c49ebcfd03 100644
--- a/src/syscall/zerrors_openbsd_arm.go
+++ b/src/syscall/zerrors_openbsd_arm.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- _const.go
 
+// +build arm,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zerrors_solaris_amd64.go b/src/syscall/zerrors_solaris_amd64.go
index 3f4cbfd984..62ec81be6b 100644
--- a/src/syscall/zerrors_solaris_amd64.go
+++ b/src/syscall/zerrors_solaris_amd64.go
@@ -4,6 +4,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -m64 _const.go
 
+// +build amd64,solaris
+
 package syscall
 
 const (
diff --git a/src/syscall/zsyscall_darwin_386.go b/src/syscall/zsyscall_darwin_386.go
index 0ce383fb3b..23e7b5e420 100644
--- a/src/syscall/zsyscall_darwin_386.go
+++ b/src/syscall/zsyscall_darwin_386.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 syscall_bsd.go syscall_darwin.go syscall_darwin_386.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build 386,darwin
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_darwin_amd64.go b/src/syscall/zsyscall_darwin_amd64.go
index 2370630f09..6e63d9a074 100644
--- a/src/syscall/zsyscall_darwin_amd64.go
+++ b/src/syscall/zsyscall_darwin_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl syscall_bsd.go syscall_darwin.go syscall_darwin_amd64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,darwin
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_darwin_arm.go b/src/syscall/zsyscall_darwin_arm.go
index c9426ee85a..f996a508f0 100644
--- a/src/syscall/zsyscall_darwin_arm.go
+++ b/src/syscall/zsyscall_darwin_arm.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 syscall_bsd.go syscall_darwin.go syscall_darwin_arm.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm,darwin
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_darwin_arm64.go b/src/syscall/zsyscall_darwin_arm64.go
index 828b167fea..c260cc7acd 100644
--- a/src/syscall/zsyscall_darwin_arm64.go
+++ b/src/syscall/zsyscall_darwin_arm64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl syscall_bsd.go syscall_darwin.go syscall_darwin_arm64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm64,darwin
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_dragonfly_amd64.go b/src/syscall/zsyscall_dragonfly_amd64.go
index 5eef1380ff..88e09d3a14 100644
--- a/src/syscall/zsyscall_dragonfly_amd64.go
+++ b/src/syscall/zsyscall_dragonfly_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -dragonfly syscall_bsd.go syscall_dragonfly.go syscall_dragonfly_amd64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,dragonfly
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_freebsd_386.go b/src/syscall/zsyscall_freebsd_386.go
index 7cb21c9b39..30f29e52a9 100644
--- a/src/syscall/zsyscall_freebsd_386.go
+++ b/src/syscall/zsyscall_freebsd_386.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 syscall_bsd.go syscall_freebsd.go syscall_freebsd_386.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build 386,freebsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_freebsd_amd64.go b/src/syscall/zsyscall_freebsd_amd64.go
index 3cb87c1a02..93059d1b5b 100644
--- a/src/syscall/zsyscall_freebsd_amd64.go
+++ b/src/syscall/zsyscall_freebsd_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl syscall_bsd.go syscall_freebsd.go syscall_freebsd_amd64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,freebsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_freebsd_arm.go b/src/syscall/zsyscall_freebsd_arm.go
index 340418345b..84096b07a5 100644
--- a/src/syscall/zsyscall_freebsd_arm.go
+++ b/src/syscall/zsyscall_freebsd_arm.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -arm syscall_bsd.go syscall_freebsd.go syscall_freebsd_arm.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm,freebsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_linux_386.go b/src/syscall/zsyscall_linux_386.go
index 2584d61e2f..620fba287a 100644
--- a/src/syscall/zsyscall_linux_386.go
+++ b/src/syscall/zsyscall_linux_386.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 syscall_linux.go syscall_linux_386.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build 386,linux
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_linux_amd64.go b/src/syscall/zsyscall_linux_amd64.go
index 141f4f39be..16cafbf06e 100644
--- a/src/syscall/zsyscall_linux_amd64.go
+++ b/src/syscall/zsyscall_linux_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl syscall_linux.go syscall_linux_amd64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,linux
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_linux_arm.go b/src/syscall/zsyscall_linux_arm.go
index ee4f6e1245..9bc3a54f31 100644
--- a/src/syscall/zsyscall_linux_arm.go
+++ b/src/syscall/zsyscall_linux_arm.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -arm syscall_linux.go syscall_linux_arm.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm,linux
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_linux_arm64.go b/src/syscall/zsyscall_linux_arm64.go
index a294eb6096..2ee58cfc8b 100644
--- a/src/syscall/zsyscall_linux_arm64.go
+++ b/src/syscall/zsyscall_linux_arm64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl syscall_linux.go syscall_linux_arm64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm64,linux
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_linux_ppc64.go b/src/syscall/zsyscall_linux_ppc64.go
index ba287e27e1..4f61114d5f 100644
--- a/src/syscall/zsyscall_linux_ppc64.go
+++ b/src/syscall/zsyscall_linux_ppc64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl syscall_linux.go syscall_linux_ppc64x.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build ppc64,linux
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_linux_ppc64le.go b/src/syscall/zsyscall_linux_ppc64le.go
index ba287e27e1..4073a0fa34 100644
--- a/src/syscall/zsyscall_linux_ppc64le.go
+++ b/src/syscall/zsyscall_linux_ppc64le.go
@@ -1,6 +1,8 @@
 // mksyscall.pl syscall_linux.go syscall_linux_ppc64x.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build ppc64le,linux
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_nacl_386.go b/src/syscall/zsyscall_nacl_386.go
index d0ff66c819..bf3f9e3dcd 100644
--- a/src/syscall/zsyscall_nacl_386.go
+++ b/src/syscall/zsyscall_nacl_386.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -nacl syscall_nacl.go syscall_nacl_386.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build 386,nacl
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_nacl_amd64p32.go b/src/syscall/zsyscall_nacl_amd64p32.go
index 592c8d84a5..3f08da6e77 100644
--- a/src/syscall/zsyscall_nacl_amd64p32.go
+++ b/src/syscall/zsyscall_nacl_amd64p32.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -nacl syscall_nacl.go syscall_nacl_amd64p32.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64p32,nacl
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_nacl_arm.go b/src/syscall/zsyscall_nacl_arm.go
index 10657ad080..77d46c3d81 100644
--- a/src/syscall/zsyscall_nacl_arm.go
+++ b/src/syscall/zsyscall_nacl_arm.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -nacl -arm syscall_nacl.go syscall_nacl_arm.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm,nacl
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_netbsd_386.go b/src/syscall/zsyscall_netbsd_386.go
index 5131c25696..e24c3b71cd 100644
--- a/src/syscall/zsyscall_netbsd_386.go
+++ b/src/syscall/zsyscall_netbsd_386.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -netbsd syscall_bsd.go syscall_netbsd.go syscall_netbsd_386.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build 386,netbsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_netbsd_amd64.go b/src/syscall/zsyscall_netbsd_amd64.go
index 0e26f6869d..7aa75ab12d 100644
--- a/src/syscall/zsyscall_netbsd_amd64.go
+++ b/src/syscall/zsyscall_netbsd_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -netbsd syscall_bsd.go syscall_netbsd.go syscall_netbsd_amd64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,netbsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_netbsd_arm.go b/src/syscall/zsyscall_netbsd_arm.go
index 4aae1040ad..21f482b40f 100644
--- a/src/syscall/zsyscall_netbsd_arm.go
+++ b/src/syscall/zsyscall_netbsd_arm.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -arm syscall_bsd.go syscall_netbsd.go syscall_netbsd_arm.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm,netbsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_openbsd_386.go b/src/syscall/zsyscall_openbsd_386.go
index f6a19833ec..df7df1e7e4 100644
--- a/src/syscall/zsyscall_openbsd_386.go
+++ b/src/syscall/zsyscall_openbsd_386.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -openbsd syscall_bsd.go syscall_openbsd.go syscall_openbsd_386.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build 386,openbsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_openbsd_amd64.go b/src/syscall/zsyscall_openbsd_amd64.go
index 93f5fc0085..1d640700f7 100644
--- a/src/syscall/zsyscall_openbsd_amd64.go
+++ b/src/syscall/zsyscall_openbsd_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -openbsd syscall_bsd.go syscall_openbsd.go syscall_openbsd_amd64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,openbsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_openbsd_arm.go b/src/syscall/zsyscall_openbsd_arm.go
index f59739c1e0..f40fb31c98 100644
--- a/src/syscall/zsyscall_openbsd_arm.go
+++ b/src/syscall/zsyscall_openbsd_arm.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -openbsd -arm syscall_bsd.go syscall_openbsd.go syscall_openbsd_arm.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build arm,openbsd
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_plan9_386.go b/src/syscall/zsyscall_plan9_386.go
index 06f1f04114..a424e789e3 100644
--- a/src/syscall/zsyscall_plan9_386.go
+++ b/src/syscall/zsyscall_plan9_386.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -plan9 syscall_plan9.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build 386,plan9
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_plan9_amd64.go b/src/syscall/zsyscall_plan9_amd64.go
index 06f1f04114..d58556b01a 100644
--- a/src/syscall/zsyscall_plan9_amd64.go
+++ b/src/syscall/zsyscall_plan9_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall.pl -l32 -plan9 syscall_plan9.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,plan9
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsyscall_solaris_amd64.go b/src/syscall/zsyscall_solaris_amd64.go
index be9bc28474..cabab7ece3 100644
--- a/src/syscall/zsyscall_solaris_amd64.go
+++ b/src/syscall/zsyscall_solaris_amd64.go
@@ -1,6 +1,8 @@
 // mksyscall_solaris.pl syscall_solaris.go syscall_solaris_amd64.go
 // MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
 
+// +build amd64,solaris
+
 package syscall
 
 import "unsafe"
diff --git a/src/syscall/zsysnum_darwin_386.go b/src/syscall/zsysnum_darwin_386.go
index abdef77436..c6f8342841 100644
--- a/src/syscall/zsysnum_darwin_386.go
+++ b/src/syscall/zsysnum_darwin_386.go
@@ -1,6 +1,8 @@
 // mksysnum_darwin.pl /usr/include/sys/syscall.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build 386,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_darwin_amd64.go b/src/syscall/zsysnum_darwin_amd64.go
index abdef77436..7189abe3db 100644
--- a/src/syscall/zsysnum_darwin_amd64.go
+++ b/src/syscall/zsysnum_darwin_amd64.go
@@ -1,6 +1,8 @@
 // mksysnum_darwin.pl /usr/include/sys/syscall.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build amd64,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_darwin_arm.go b/src/syscall/zsysnum_darwin_arm.go
index 1a53f13eff..1d76861204 100644
--- a/src/syscall/zsysnum_darwin_arm.go
+++ b/src/syscall/zsysnum_darwin_arm.go
@@ -1,6 +1,8 @@
 // mksysnum_darwin.pl /usr/include/sys/syscall.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build arm,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_darwin_arm64.go b/src/syscall/zsysnum_darwin_arm64.go
index 723b1aa19e..ddf8e83c87 100644
--- a/src/syscall/zsysnum_darwin_arm64.go
+++ b/src/syscall/zsysnum_darwin_arm64.go
@@ -1,6 +1,8 @@
 // mksysnum_darwin.pl /usr/include/sys/syscall.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build arm64,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_dragonfly_amd64.go b/src/syscall/zsysnum_dragonfly_amd64.go
index 4b086b9214..277478d208 100644
--- a/src/syscall/zsysnum_dragonfly_amd64.go
+++ b/src/syscall/zsysnum_dragonfly_amd64.go
@@ -1,6 +1,8 @@
 // mksysnum_dragonfly.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build amd64,dragonfly
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_freebsd_386.go b/src/syscall/zsysnum_freebsd_386.go
index dfca558bb5..5e47217957 100644
--- a/src/syscall/zsysnum_freebsd_386.go
+++ b/src/syscall/zsysnum_freebsd_386.go
@@ -1,6 +1,8 @@
 // mksysnum_freebsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build 386,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_freebsd_amd64.go b/src/syscall/zsysnum_freebsd_amd64.go
index dfca558bb5..df8928cc68 100644
--- a/src/syscall/zsysnum_freebsd_amd64.go
+++ b/src/syscall/zsysnum_freebsd_amd64.go
@@ -1,6 +1,8 @@
 // mksysnum_freebsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build amd64,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_freebsd_arm.go b/src/syscall/zsysnum_freebsd_arm.go
index dfca558bb5..f670a59179 100644
--- a/src/syscall/zsysnum_freebsd_arm.go
+++ b/src/syscall/zsysnum_freebsd_arm.go
@@ -1,6 +1,8 @@
 // mksysnum_freebsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build arm,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_linux_386.go b/src/syscall/zsysnum_linux_386.go
index c40b5f1ace..c277ed9a25 100644
--- a/src/syscall/zsysnum_linux_386.go
+++ b/src/syscall/zsysnum_linux_386.go
@@ -1,6 +1,8 @@
 // mksysnum_linux.pl /usr/include/asm/unistd_32.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build 386,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_linux_amd64.go b/src/syscall/zsysnum_linux_amd64.go
index 7cf70a4d86..978a4d3c3d 100644
--- a/src/syscall/zsysnum_linux_amd64.go
+++ b/src/syscall/zsysnum_linux_amd64.go
@@ -1,6 +1,8 @@
 // mksysnum_linux.pl /usr/include/asm/unistd_64.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build amd64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_linux_arm.go b/src/syscall/zsysnum_linux_arm.go
index 7068e4e210..5061cbab2b 100644
--- a/src/syscall/zsysnum_linux_arm.go
+++ b/src/syscall/zsysnum_linux_arm.go
@@ -1,6 +1,8 @@
 // mksysnum_linux.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build arm,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_linux_arm64.go b/src/syscall/zsysnum_linux_arm64.go
index 4bcf0573e7..d53712c681 100644
--- a/src/syscall/zsysnum_linux_arm64.go
+++ b/src/syscall/zsysnum_linux_arm64.go
@@ -1,6 +1,8 @@
 // mksysnum_linux.pl /usr/include/asm-generic/unistd.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build arm64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_linux_ppc64.go b/src/syscall/zsysnum_linux_ppc64.go
index 0567fd0ea8..82d253a1ab 100644
--- a/src/syscall/zsysnum_linux_ppc64.go
+++ b/src/syscall/zsysnum_linux_ppc64.go
@@ -1,6 +1,8 @@
 // mksysnum_linux.pl /usr/include/asm/unistd.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build ppc64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_linux_ppc64le.go b/src/syscall/zsysnum_linux_ppc64le.go
index 52c63e3d3e..3af4e83822 100644
--- a/src/syscall/zsysnum_linux_ppc64le.go
+++ b/src/syscall/zsysnum_linux_ppc64le.go
@@ -1,6 +1,8 @@
 // mksysnum_linux.pl /usr/include/powerpc64le-linux-gnu/asm/unistd.h
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build ppc64le,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_netbsd_386.go b/src/syscall/zsysnum_netbsd_386.go
index c570965237..c8af210082 100644
--- a/src/syscall/zsysnum_netbsd_386.go
+++ b/src/syscall/zsysnum_netbsd_386.go
@@ -1,6 +1,8 @@
 // mksysnum_netbsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build 386,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_netbsd_amd64.go b/src/syscall/zsysnum_netbsd_amd64.go
index c570965237..e342a3ce58 100644
--- a/src/syscall/zsysnum_netbsd_amd64.go
+++ b/src/syscall/zsysnum_netbsd_amd64.go
@@ -1,6 +1,8 @@
 // mksysnum_netbsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build amd64,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_netbsd_arm.go b/src/syscall/zsysnum_netbsd_arm.go
index c570965237..1f5b569b8f 100644
--- a/src/syscall/zsysnum_netbsd_arm.go
+++ b/src/syscall/zsysnum_netbsd_arm.go
@@ -1,6 +1,8 @@
 // mksysnum_netbsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build arm,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_openbsd_386.go b/src/syscall/zsysnum_openbsd_386.go
index 3b9ac4c941..c19f6de649 100644
--- a/src/syscall/zsysnum_openbsd_386.go
+++ b/src/syscall/zsysnum_openbsd_386.go
@@ -1,6 +1,8 @@
 // mksysnum_openbsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build 386,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_openbsd_amd64.go b/src/syscall/zsysnum_openbsd_amd64.go
index 3b9ac4c941..86e04cd47e 100644
--- a/src/syscall/zsysnum_openbsd_amd64.go
+++ b/src/syscall/zsysnum_openbsd_amd64.go
@@ -1,6 +1,8 @@
 // mksysnum_openbsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build amd64,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_openbsd_arm.go b/src/syscall/zsysnum_openbsd_arm.go
index 8457c14732..38b43caba6 100644
--- a/src/syscall/zsysnum_openbsd_arm.go
+++ b/src/syscall/zsysnum_openbsd_arm.go
@@ -1,6 +1,8 @@
 // mksysnum_openbsd.pl
 // MACHINE GENERATED BY THE ABOVE COMMAND; DO NOT EDIT
 
+// +build arm,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/zsysnum_solaris_amd64.go b/src/syscall/zsysnum_solaris_amd64.go
index 43b3d8b40d..be198f899b 100644
--- a/src/syscall/zsysnum_solaris_amd64.go
+++ b/src/syscall/zsysnum_solaris_amd64.go
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build amd64,solaris
+
 package syscall
 
 // TODO(aram): remove these before Go 1.3.
diff --git a/src/syscall/ztypes_darwin_386.go b/src/syscall/ztypes_darwin_386.go
index 13724c3cc6..7298d0243d 100644
--- a/src/syscall/ztypes_darwin_386.go
+++ b/src/syscall/ztypes_darwin_386.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_darwin.go
 
+// +build 386,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_darwin_amd64.go b/src/syscall/ztypes_darwin_amd64.go
index 65b02ae4f5..ec95d51f91 100644
--- a/src/syscall/ztypes_darwin_amd64.go
+++ b/src/syscall/ztypes_darwin_amd64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_darwin.go
 
+// +build amd64,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_darwin_arm.go b/src/syscall/ztypes_darwin_arm.go
index ec87f54fb2..91c4470e7c 100644
--- a/src/syscall/ztypes_darwin_arm.go
+++ b/src/syscall/ztypes_darwin_arm.go
@@ -2,6 +2,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_darwin.go
 
+// +build arm,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_darwin_arm64.go b/src/syscall/ztypes_darwin_arm64.go
index 65b02ae4f5..1d65cfde9d 100644
--- a/src/syscall/ztypes_darwin_arm64.go
+++ b/src/syscall/ztypes_darwin_arm64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_darwin.go
 
+// +build arm64,darwin
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_dragonfly_amd64.go b/src/syscall/ztypes_dragonfly_amd64.go
index 954ffd7ab2..00120d0a27 100644
--- a/src/syscall/ztypes_dragonfly_amd64.go
+++ b/src/syscall/ztypes_dragonfly_amd64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_dragonfly.go
 
+// +build amd64,dragonfly
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_freebsd_386.go b/src/syscall/ztypes_freebsd_386.go
index b809eea37a..d972fb6bdf 100644
--- a/src/syscall/ztypes_freebsd_386.go
+++ b/src/syscall/ztypes_freebsd_386.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_freebsd.go
 
+// +build 386,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_freebsd_amd64.go b/src/syscall/ztypes_freebsd_amd64.go
index a05908aed1..0a5a10bf7d 100644
--- a/src/syscall/ztypes_freebsd_amd64.go
+++ b/src/syscall/ztypes_freebsd_amd64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_freebsd.go
 
+// +build amd64,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_freebsd_arm.go b/src/syscall/ztypes_freebsd_arm.go
index 9303816f91..5d7acd547b 100644
--- a/src/syscall/ztypes_freebsd_arm.go
+++ b/src/syscall/ztypes_freebsd_arm.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -fsigned-char types_freebsd.go
 
+// +build arm,freebsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_linux_386.go b/src/syscall/ztypes_linux_386.go
index a887f31427..dd198cb394 100644
--- a/src/syscall/ztypes_linux_386.go
+++ b/src/syscall/ztypes_linux_386.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_linux.go
 
+// +build 386,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_linux_amd64.go b/src/syscall/ztypes_linux_amd64.go
index adf95caee7..a39489e4e5 100644
--- a/src/syscall/ztypes_linux_amd64.go
+++ b/src/syscall/ztypes_linux_amd64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_linux.go
 
+// +build amd64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_linux_arm.go b/src/syscall/ztypes_linux_arm.go
index 1ae9718945..f446e41be0 100644
--- a/src/syscall/ztypes_linux_arm.go
+++ b/src/syscall/ztypes_linux_arm.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_linux.go
 
+// +build arm,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_linux_arm64.go b/src/syscall/ztypes_linux_arm64.go
index 1cb1d435d8..dcb1178dc4 100644
--- a/src/syscall/ztypes_linux_arm64.go
+++ b/src/syscall/ztypes_linux_arm64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs -- -fsigned-char types_linux.go
 
+// +build arm64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_linux_ppc64.go b/src/syscall/ztypes_linux_ppc64.go
index bce0350b86..33d1b7f3e5 100644
--- a/src/syscall/ztypes_linux_ppc64.go
+++ b/src/syscall/ztypes_linux_ppc64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_linux.go
 
+// +build ppc64,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_linux_ppc64le.go b/src/syscall/ztypes_linux_ppc64le.go
index 0de1770f7f..27ca004834 100644
--- a/src/syscall/ztypes_linux_ppc64le.go
+++ b/src/syscall/ztypes_linux_ppc64le.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_linux.go
 
+// +build ppc64le,linux
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_netbsd_386.go b/src/syscall/ztypes_netbsd_386.go
index 6add325a37..1752c6c229 100644
--- a/src/syscall/ztypes_netbsd_386.go
+++ b/src/syscall/ztypes_netbsd_386.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_netbsd.go
 
+// +build 386,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_netbsd_amd64.go b/src/syscall/ztypes_netbsd_amd64.go
index 4451fc1f02..b8d4b0b02a 100644
--- a/src/syscall/ztypes_netbsd_amd64.go
+++ b/src/syscall/ztypes_netbsd_amd64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_netbsd.go
 
+// +build amd64,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_netbsd_arm.go b/src/syscall/ztypes_netbsd_arm.go
index 4e853eaa23..c21d875a93 100644
--- a/src/syscall/ztypes_netbsd_arm.go
+++ b/src/syscall/ztypes_netbsd_arm.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_netbsd.go
 
+// +build arm,netbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_openbsd_386.go b/src/syscall/ztypes_openbsd_386.go
index 2e4d9dd174..0376d3acab 100644
--- a/src/syscall/ztypes_openbsd_386.go
+++ b/src/syscall/ztypes_openbsd_386.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_openbsd.go
 
+// +build 386,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_openbsd_amd64.go b/src/syscall/ztypes_openbsd_amd64.go
index f07bc714e9..bf23626ff2 100644
--- a/src/syscall/ztypes_openbsd_amd64.go
+++ b/src/syscall/ztypes_openbsd_amd64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_openbsd.go
 
+// +build amd64,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_openbsd_arm.go b/src/syscall/ztypes_openbsd_arm.go
index 5f8fb1262f..e1d8938f0e 100644
--- a/src/syscall/ztypes_openbsd_arm.go
+++ b/src/syscall/ztypes_openbsd_arm.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_openbsd.go
 
+// +build arm,openbsd
+
 package syscall
 
 const (
diff --git a/src/syscall/ztypes_solaris_amd64.go b/src/syscall/ztypes_solaris_amd64.go
index 77275a54e3..2471519ade 100644
--- a/src/syscall/ztypes_solaris_amd64.go
+++ b/src/syscall/ztypes_solaris_amd64.go
@@ -1,6 +1,8 @@
 // Created by cgo -godefs - DO NOT EDIT
 // cgo -godefs types_solaris.go
 
+// +build amd64,solaris
+
 package syscall
 
 const (

From ed8ae79282f1ad8d06f926b366725c1be798289c Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Tue, 12 May 2015 01:13:09 -0400
Subject: [PATCH 097/232] syscall: add test for Flock_t roundtrip

See CL 9962 for the rationale.

Change-Id: I73c714fce258430eea1e61d3835f5c8e9014ca1f
Signed-off-by: Shenghou Ma 
Reviewed-on: https://go-review.googlesource.com/9925
Reviewed-by: Ian Lance Taylor 
---
 src/syscall/syscall_unix_test.go | 59 +++++++++++++++++++++++++-------
 1 file changed, 47 insertions(+), 12 deletions(-)

diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go
index 01fc670aba..90fd276f82 100644
--- a/src/syscall/syscall_unix_test.go
+++ b/src/syscall/syscall_unix_test.go
@@ -60,20 +60,55 @@ func _() {
 
 // TestFcntlFlock tests whether the file locking structure matches
 // the calling convention of each kernel.
+// On some Linux systems, glibc uses another set of values for the
+// commands and translates them to the correct value that the kernel
+// expects just before the actual fcntl syscall. As Go uses raw
+// syscalls directly, it must use the real value, not the glibc value.
+// Thus this test also verifies that the Flock_t structure can be
+// roundtripped with F_SETLK and F_GETLK.
 func TestFcntlFlock(t *testing.T) {
-	name := filepath.Join(os.TempDir(), "TestFcntlFlock")
-	fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0)
-	if err != nil {
-		t.Fatalf("Open failed: %v", err)
-	}
-	defer syscall.Unlink(name)
-	defer syscall.Close(fd)
 	flock := syscall.Flock_t{
-		Type:  syscall.F_RDLCK,
-		Start: 0, Len: 0, Whence: 1,
+		Type:  syscall.F_WRLCK,
+		Start: 31415, Len: 271828, Whence: 1,
 	}
-	if err := syscall.FcntlFlock(uintptr(fd), syscall.F_GETLK, &flock); err != nil {
-		t.Fatalf("FcntlFlock failed: %v", err)
+	if os.Getenv("GO_WANT_HELPER_PROCESS") == "" {
+		// parent
+		name := filepath.Join(os.TempDir(), "TestFcntlFlock")
+		fd, err := syscall.Open(name, syscall.O_CREAT|syscall.O_RDWR|syscall.O_CLOEXEC, 0)
+		if err != nil {
+			t.Fatalf("Open failed: %v", err)
+		}
+		defer syscall.Unlink(name)
+		defer syscall.Close(fd)
+		if err := syscall.Ftruncate(fd, 1<<20); err != nil {
+			t.Fatalf("Ftruncate(1<<20) failed: %v", err)
+		}
+		if err := syscall.FcntlFlock(uintptr(fd), syscall.F_SETLK, &flock); err != nil {
+			t.Fatalf("FcntlFlock(F_SETLK) failed: %v", err)
+		}
+		cmd := exec.Command(os.Args[0], "-test.run=^TestFcntlFlock$")
+		cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
+		cmd.ExtraFiles = []*os.File{os.NewFile(uintptr(fd), name)}
+		out, err := cmd.CombinedOutput()
+		if len(out) > 0 || err != nil {
+			t.Fatalf("child process: %q, %v", out, err)
+		}
+	} else {
+		// child
+		got := flock
+		// make sure the child lock is conflicting with the parent lock
+		got.Start--
+		got.Len++
+		if err := syscall.FcntlFlock(3, syscall.F_GETLK, &got); err != nil {
+			t.Fatalf("FcntlFlock(F_GETLK) failed: %v", err)
+		}
+		flock.Pid = int32(syscall.Getppid())
+		// Linux kernel always set Whence to 0
+		flock.Whence = 0
+		if got.Type == flock.Type && got.Start == flock.Start && got.Len == flock.Len && got.Pid == flock.Pid && got.Whence == flock.Whence {
+			os.Exit(0)
+		}
+		t.Fatalf("FcntlFlock got %v, want %v", got, flock)
 	}
 }
 
@@ -121,7 +156,7 @@ func TestPassFD(t *testing.T) {
 	defer readFile.Close()
 
 	cmd := exec.Command(os.Args[0], "-test.run=^TestPassFD$", "--", tempDir)
-	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
+	cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
 	cmd.ExtraFiles = []*os.File{writeFile}
 
 	out, err := cmd.CombinedOutput()

From 335e44d265e7b7741b00237f4fcd97a1b80bfd9a Mon Sep 17 00:00:00 2001
From: Patrick Mezard 
Date: Sat, 9 May 2015 15:51:45 +0200
Subject: [PATCH 098/232] internal/syscall/windows/registry: fix read overrun
 in GetStringsValue

According to MSDN RegQueryValueEx page:

  If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the
  string may not have been stored with the proper terminating null
  characters. Therefore, even if the function returns ERROR_SUCCESS, the
  application should ensure that the string is properly terminated before
  using it; otherwise, it may overwrite a buffer. (Note that REG_MULTI_SZ
  strings should have two terminating null characters.)

Test written by Alex Brainman 

Change-Id: I8c0852e0527e27ceed949134ed5e6de944189986
Reviewed-on: https://go-review.googlesource.com/9806
Reviewed-by: Alex Brainman 
Run-TryBot: Alex Brainman 
---
 .../syscall/windows/registry/export_test.go   | 11 ++++
 .../syscall/windows/registry/registry_test.go | 65 +++++++++++++++++++
 .../syscall/windows/registry/value.go         | 12 +++-
 3 files changed, 86 insertions(+), 2 deletions(-)
 create mode 100644 src/internal/syscall/windows/registry/export_test.go

diff --git a/src/internal/syscall/windows/registry/export_test.go b/src/internal/syscall/windows/registry/export_test.go
new file mode 100644
index 0000000000..8badf6fdcf
--- /dev/null
+++ b/src/internal/syscall/windows/registry/export_test.go
@@ -0,0 +1,11 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build windows
+
+package registry
+
+func (k Key) SetValue(name string, valtype uint32, data []byte) error {
+	return k.setValue(name, valtype, data)
+}
diff --git a/src/internal/syscall/windows/registry/registry_test.go b/src/internal/syscall/windows/registry/registry_test.go
index 5f75febd27..07eccb23d8 100644
--- a/src/internal/syscall/windows/registry/registry_test.go
+++ b/src/internal/syscall/windows/registry/registry_test.go
@@ -611,3 +611,68 @@ func TestExpandString(t *testing.T) {
 		t.Errorf("want %q string expanded, got %q", want, got)
 	}
 }
+
+func TestInvalidValues(t *testing.T) {
+	softwareK, err := registry.OpenKey(registry.CURRENT_USER, "Software", registry.QUERY_VALUE)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer softwareK.Close()
+
+	testKName := randKeyName("TestInvalidValues_")
+
+	k, exist, err := registry.CreateKey(softwareK, testKName, registry.CREATE_SUB_KEY|registry.QUERY_VALUE|registry.SET_VALUE)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer k.Close()
+
+	if exist {
+		t.Fatalf("key %q already exists", testKName)
+	}
+
+	defer registry.DeleteKey(softwareK, testKName)
+
+	var tests = []struct {
+		Type uint32
+		Name string
+		Data []byte
+	}{
+		{registry.DWORD, "Dword1", nil},
+		{registry.DWORD, "Dword2", []byte{1, 2, 3}},
+		{registry.QWORD, "Qword1", nil},
+		{registry.QWORD, "Qword2", []byte{1, 2, 3}},
+		{registry.QWORD, "Qword3", []byte{1, 2, 3, 4, 5, 6, 7}},
+		{registry.MULTI_SZ, "MultiString1", nil},
+		{registry.MULTI_SZ, "MultiString2", []byte{0}},
+		{registry.MULTI_SZ, "MultiString3", []byte{'a', 'b', 0}},
+		{registry.MULTI_SZ, "MultiString4", []byte{'a', 0, 0, 'b', 0}},
+		{registry.MULTI_SZ, "MultiString5", []byte{'a', 0, 0}},
+	}
+
+	for _, test := range tests {
+		err := k.SetValue(test.Name, test.Type, test.Data)
+		if err != nil {
+			t.Fatalf("SetValue for %q failed: %v", test.Name, err)
+		}
+	}
+
+	for _, test := range tests {
+		switch test.Type {
+		case registry.DWORD, registry.QWORD:
+			value, valType, err := k.GetIntegerValue(test.Name)
+			if err == nil {
+				t.Errorf("GetIntegerValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
+			}
+		case registry.MULTI_SZ:
+			value, valType, err := k.GetStringsValue(test.Name)
+			if err == nil {
+				if len(value) != 0 {
+					t.Errorf("GetStringsValue(%q) succeeded. Returns type=%d value=%v", test.Name, valType, value)
+				}
+			}
+		default:
+			t.Errorf("unsupported type %d for %s value", test.Type, test.Name)
+		}
+	}
+}
diff --git a/src/internal/syscall/windows/registry/value.go b/src/internal/syscall/windows/registry/value.go
index 814fe445b9..bb45a23643 100644
--- a/src/internal/syscall/windows/registry/value.go
+++ b/src/internal/syscall/windows/registry/value.go
@@ -150,9 +150,17 @@ func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err err
 	if typ != MULTI_SZ {
 		return nil, typ, ErrUnexpectedType
 	}
-	val = make([]string, 0, 5)
+	if len(data) == 0 {
+		return nil, typ, nil
+	}
 	p := (*[1 << 24]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2]
-	p = p[:len(p)-1] // remove terminating nil
+	if len(p) == 0 {
+		return nil, typ, nil
+	}
+	if p[len(p)-1] == 0 {
+		p = p[:len(p)-1] // remove terminating null
+	}
+	val = make([]string, 0, 5)
 	from := 0
 	for i, c := range p {
 		if c == 0 {

From 3b214175bcf1e1441f0411f4691ca9a0963c4564 Mon Sep 17 00:00:00 2001
From: David Symonds 
Date: Fri, 15 May 2015 15:37:40 +1000
Subject: [PATCH 099/232] cmd/go: fix count of number of reserved names (doc
 change).

Change-Id: I2784f831453d929df64c66febb4982cdf1f08e06
Reviewed-on: https://go-review.googlesource.com/10133
Reviewed-by: Minux Ma 
---
 src/cmd/go/alldocs.go | 3 ++-
 src/cmd/go/help.go    | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/cmd/go/alldocs.go b/src/cmd/go/alldocs.go
index e0d4a6c0fe..2b1cbf98ec 100644
--- a/src/cmd/go/alldocs.go
+++ b/src/cmd/go/alldocs.go
@@ -503,6 +503,7 @@ syntax of package template.  The default output is equivalent to -f
         Name          string // package name
         Doc           string // package documentation string
         Target        string // install path
+        Shlib         string // the shared library that contains this package (only set when -linkshared)
         Goroot        bool   // is this package in the Go root?
         Standard      bool   // is this package part of the standard Go library?
         Stale         bool   // would 'go install' do anything for this package?
@@ -1053,7 +1054,7 @@ environment variable (see 'go help gopath').
 If no import paths are given, the action applies to the
 package in the current directory.
 
-There are three reserved names for paths that should not be used
+There are four reserved names for paths that should not be used
 for packages to be built with the go tool:
 
 - "main" denotes the top-level package in a stand-alone executable.
diff --git a/src/cmd/go/help.go b/src/cmd/go/help.go
index 56e8493e1a..2062f0c4ee 100644
--- a/src/cmd/go/help.go
+++ b/src/cmd/go/help.go
@@ -47,7 +47,7 @@ environment variable (see 'go help gopath').
 If no import paths are given, the action applies to the
 package in the current directory.
 
-There are three reserved names for paths that should not be used
+There are four reserved names for paths that should not be used
 for packages to be built with the go tool:
 
 - "main" denotes the top-level package in a stand-alone executable.

From 497970f4213367d8c6980dd46c43ea522f412ef9 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Wed, 13 May 2015 10:48:05 -0400
Subject: [PATCH 100/232] runtime: use memmove during slice append
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The effect of this CL:

name                   old mean              new mean              delta
BinaryTree17            5.97s × (0.96,1.04)   5.95s × (0.98,1.02)    ~    (p=0.697)
Fannkuch11              4.39s × (1.00,1.01)   4.41s × (1.00,1.01)  +0.52% (p=0.015)
FmtFprintfEmpty        90.8ns × (0.97,1.05)  89.4ns × (0.94,1.13)    ~    (p=0.571)
FmtFprintfString        305ns × (0.99,1.01)   292ns × (0.98,1.05)  -4.35% (p=0.000)
FmtFprintfInt           278ns × (0.96,1.03)   279ns × (0.98,1.04)    ~    (p=0.741)
FmtFprintfIntInt        489ns × (0.99,1.02)   482ns × (0.98,1.03)  -1.43% (p=0.024)
FmtFprintfPrefixedInt   402ns × (0.98,1.02)   395ns × (0.98,1.03)  -1.67% (p=0.014)
FmtFprintfFloat         578ns × (1.00,1.00)   569ns × (0.99,1.01)  -1.48% (p=0.000)
FmtManyArgs            1.88µs × (0.99,1.01)  1.88µs × (1.00,1.01)    ~    (p=0.055)
GobDecode              15.3ms × (0.99,1.01)  15.2ms × (1.00,1.01)  -0.61% (p=0.007)
GobEncode              11.8ms × (0.98,1.05)  11.6ms × (0.99,1.01)    ~    (p=0.075)
Gzip                    647ms × (0.99,1.01)   647ms × (1.00,1.00)    ~    (p=0.790)
Gunzip                  143ms × (1.00,1.00)   142ms × (1.00,1.00)    ~    (p=0.370)
HTTPClientServer       91.2µs × (0.99,1.01)  91.7µs × (0.99,1.02)    ~    (p=0.233)
JSONEncode             31.5ms × (0.98,1.01)  31.8ms × (0.99,1.02)  +1.09% (p=0.015)
JSONDecode              110ms × (0.99,1.01)   110ms × (0.99,1.02)    ~    (p=0.577)
Mandelbrot200          6.00ms × (1.00,1.00)  6.02ms × (1.00,1.00)  +0.24% (p=0.001)
GoParse                6.68ms × (0.98,1.02)  6.61ms × (0.99,1.01)  -1.10% (p=0.027)
RegexpMatchEasy0_32     162ns × (1.00,1.00)   161ns × (1.00,1.01)  -0.66% (p=0.001)
RegexpMatchEasy0_1K     539ns × (1.00,1.00)   539ns × (0.99,1.01)    ~    (p=0.509)
RegexpMatchEasy1_32     140ns × (0.99,1.02)   139ns × (0.99,1.02)    ~    (p=0.163)
RegexpMatchEasy1_1K     886ns × (1.00,1.00)   887ns × (1.00,1.00)    ~    (p=0.408)
RegexpMatchMedium_32    252ns × (1.00,1.00)   255ns × (0.99,1.01)  +1.01% (p=0.000)
RegexpMatchMedium_1K   72.6µs × (1.00,1.00)  72.6µs × (1.00,1.00)    ~    (p=0.176)
RegexpMatchHard_32     3.84µs × (1.00,1.00)  3.84µs × (1.00,1.00)    ~    (p=0.403)
RegexpMatchHard_1K      117µs × (1.00,1.00)   117µs × (1.00,1.00)    ~    (p=0.351)
Revcomp                 926ms × (0.99,1.01)   925ms × (0.99,1.01)    ~    (p=0.541)
Template                126ms × (0.99,1.02)   130ms × (0.99,1.01)  +3.42% (p=0.000)
TimeParse               632ns × (0.99,1.01)   626ns × (1.00,1.00)  -0.88% (p=0.000)
TimeFormat              658ns × (0.99,1.01)   662ns × (0.99,1.02)    ~    (p=0.111)

The effect of this CL combined with CL 9886:

name                   old mean              new mean              delta
BinaryTree17            5.90s × (0.98,1.03)   5.95s × (0.98,1.02)    ~    (p=0.175)
Fannkuch11              4.34s × (1.00,1.00)   4.41s × (1.00,1.01)  +1.69% (p=0.000)
FmtFprintfEmpty        87.3ns × (0.97,1.17)  89.4ns × (0.94,1.13)    ~    (p=0.499)
FmtFprintfString        288ns × (0.98,1.04)   292ns × (0.98,1.05)    ~    (p=0.292)
FmtFprintfInt           290ns × (0.98,1.05)   279ns × (0.98,1.04)  -3.76% (p=0.001)
FmtFprintfIntInt        493ns × (0.98,1.04)   482ns × (0.98,1.03)  -2.27% (p=0.017)
FmtFprintfPrefixedInt   399ns × (0.98,1.02)   395ns × (0.98,1.03)    ~    (p=0.159)
FmtFprintfFloat         569ns × (1.00,1.00)   569ns × (0.99,1.01)    ~    (p=0.847)
FmtManyArgs            1.90µs × (0.99,1.03)  1.88µs × (1.00,1.01)  -1.14% (p=0.009)
GobDecode              15.2ms × (1.00,1.01)  15.2ms × (1.00,1.01)    ~    (p=0.170)
GobEncode              11.8ms × (0.99,1.02)  11.6ms × (0.99,1.01)  -1.47% (p=0.003)
Gzip                    649ms × (0.99,1.00)   647ms × (1.00,1.00)    ~    (p=0.200)
Gunzip                  144ms × (0.99,1.01)   142ms × (1.00,1.00)  -1.04% (p=0.000)
HTTPClientServer       91.1µs × (0.98,1.03)  91.7µs × (0.99,1.02)    ~    (p=0.345)
JSONEncode             31.5ms × (0.99,1.01)  31.8ms × (0.99,1.02)  +0.98% (p=0.021)
JSONDecode              110ms × (1.00,1.01)   110ms × (0.99,1.02)    ~    (p=0.259)
Mandelbrot200          6.02ms × (1.00,1.01)  6.02ms × (1.00,1.00)    ~    (p=0.500)
GoParse                6.68ms × (1.00,1.01)  6.61ms × (0.99,1.01)  -1.17% (p=0.001)
RegexpMatchEasy0_32     161ns × (1.00,1.00)   161ns × (1.00,1.01)  -0.39% (p=0.033)
RegexpMatchEasy0_1K     539ns × (1.00,1.00)   539ns × (0.99,1.01)    ~    (p=0.445)
RegexpMatchEasy1_32     138ns × (1.00,1.01)   139ns × (0.99,1.02)    ~    (p=0.281)
RegexpMatchEasy1_1K     887ns × (1.00,1.01)   887ns × (1.00,1.00)    ~    (p=0.610)
RegexpMatchMedium_32    251ns × (1.00,1.02)   255ns × (0.99,1.01)  +1.42% (p=0.000)
RegexpMatchMedium_1K   72.7µs × (1.00,1.00)  72.6µs × (1.00,1.00)    ~    (p=0.097)
RegexpMatchHard_32     3.85µs × (1.00,1.00)  3.84µs × (1.00,1.00)  -0.31% (p=0.000)
RegexpMatchHard_1K      117µs × (1.00,1.00)   117µs × (1.00,1.00)    ~    (p=0.704)
Revcomp                 923ms × (0.98,1.02)   925ms × (0.99,1.01)    ~    (p=0.574)
Template                126ms × (0.98,1.03)   130ms × (0.99,1.01)  +3.28% (p=0.000)
TimeParse               631ns × (0.99,1.02)   626ns × (1.00,1.00)    ~    (p=0.053)
TimeFormat              660ns × (0.99,1.01)   662ns × (0.99,1.02)    ~    (p=0.398)

Change-Id: I59c03d329fe7bc178a31477c6f1f01062b881041
Reviewed-on: https://go-review.googlesource.com/9993
Reviewed-by: Austin Clements 
---
 src/runtime/mbarrier.go | 14 ++++++++++++++
 src/runtime/slice.go    |  9 ++++++---
 2 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index 409c1948c6..d3e4809737 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -200,6 +200,8 @@ func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uin
 
 //go:nosplit
 func typedslicecopy(typ *_type, dst, src slice) int {
+	// TODO(rsc): If typedslicecopy becomes faster than calling
+	// typedmemmove repeatedly, consider using during func growslice.
 	n := dst.len
 	if n > src.len {
 		n = src.len
@@ -217,6 +219,10 @@ func typedslicecopy(typ *_type, dst, src slice) int {
 		racereadrangepc(srcp, uintptr(n)*typ.size, callerpc, pc)
 	}
 
+	// Note: No point in checking typ.kind&kindNoPointers here:
+	// compiler only emits calls to typedslicecopy for types with pointers,
+	// and growslice and reflect_typedslicecopy check for pointers
+	// before calling typedslicecopy.
 	if !writeBarrierEnabled {
 		memmove(dstp, srcp, uintptr(n)*typ.size)
 		return n
@@ -257,5 +263,13 @@ func typedslicecopy(typ *_type, dst, src slice) int {
 
 //go:linkname reflect_typedslicecopy reflect.typedslicecopy
 func reflect_typedslicecopy(elemType *_type, dst, src slice) int {
+	if elemType.kind&kindNoPointers != 0 {
+		n := dst.len
+		if n > src.len {
+			n = src.len
+		}
+		memmove(dst.array, src.array, uintptr(n)*elemType.size)
+		return n
+	}
 	return typedslicecopy(elemType, dst, src)
 }
diff --git a/src/runtime/slice.go b/src/runtime/slice.go
index 5ccc6592bf..79b611839d 100644
--- a/src/runtime/slice.go
+++ b/src/runtime/slice.go
@@ -84,10 +84,13 @@ func growslice(t *slicetype, old slice, n int) slice {
 		memclr(add(p, lenmem), capmem-lenmem)
 	} else {
 		// Note: can't use rawmem (which avoids zeroing of memory), because then GC can scan unitialized memory.
-		// TODO(rsc): Use memmove when !writeBarrierEnabled.
 		p = newarray(et, uintptr(newcap))
-		for i := 0; i < old.len; i++ {
-			typedmemmove(et, add(p, uintptr(i)*et.size), add(old.array, uintptr(i)*et.size))
+		if !writeBarrierEnabled {
+			memmove(p, old.array, lenmem)
+		} else {
+			for i := uintptr(0); i < lenmem; i += et.size {
+				typedmemmove(et, add(p, i), add(old.array, i))
+			}
 		}
 	}
 

From 65c4d7beabd4a49aa77a9dddf1b7cdde55c47bb4 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Wed, 13 May 2015 14:44:48 -0400
Subject: [PATCH 101/232] runtime: optimize heapBitsBulkBarrier a tiny amount
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This may be mostly noise but:

name                   old mean              new mean              delta
BinaryTree17            6.03s × (0.98,1.02)   5.98s × (0.97,1.03)    ~    (p=0.306)
Fannkuch11              4.42s × (0.99,1.01)   4.34s × (0.99,1.02)  -1.83% (p=0.000)
FmtFprintfEmpty        84.7ns × (0.99,1.01)  84.4ns × (1.00,1.00)    ~    (p=0.138)
FmtFprintfString        289ns × (0.98,1.02)   289ns × (1.00,1.01)    ~    (p=0.509)
FmtFprintfInt           280ns × (0.97,1.03)   272ns × (0.98,1.03)  -2.64% (p=0.003)
FmtFprintfIntInt        484ns × (0.98,1.02)   482ns × (0.98,1.03)    ~    (p=0.606)
FmtFprintfPrefixedInt   397ns × (0.98,1.03)   393ns × (0.99,1.02)    ~    (p=0.064)
FmtFprintfFloat         573ns × (0.99,1.01)   569ns × (0.99,1.01)  -0.69% (p=0.023)
FmtManyArgs            1.89µs × (0.99,1.02)  1.91µs × (0.98,1.02)    ~    (p=0.219)
GobDecode              15.4ms × (0.99,1.02)  15.1ms × (0.99,1.01)  -2.05% (p=0.000)
GobEncode              12.0ms × (0.97,1.04)  11.9ms × (0.97,1.03)    ~    (p=0.458)
Gzip                    652ms × (0.99,1.01)   653ms × (0.99,1.01)    ~    (p=0.743)
Gunzip                  144ms × (0.99,1.01)   143ms × (0.99,1.01)    ~    (p=0.134)
HTTPClientServer       91.6µs × (0.99,1.01)  91.8µs × (0.99,1.03)    ~    (p=0.678)
JSONEncode             31.9ms × (1.00,1.00)  32.0ms × (0.99,1.01)    ~    (p=0.334)
JSONDecode              110ms × (0.99,1.01)   110ms × (0.99,1.01)    ~    (p=0.315)
Mandelbrot200          6.04ms × (0.99,1.01)  6.04ms × (1.00,1.01)    ~    (p=0.596)
GoParse                6.72ms × (0.98,1.03)  6.74ms × (0.99,1.03)    ~    (p=0.577)
RegexpMatchEasy0_32     161ns × (0.99,1.01)   160ns × (1.00,1.00)  -0.83% (p=0.002)
RegexpMatchEasy0_1K     542ns × (0.99,1.02)   541ns × (0.99,1.01)    ~    (p=0.396)
RegexpMatchEasy1_32     140ns × (0.98,1.01)   137ns × (1.00,1.00)  -2.12% (p=0.000)
RegexpMatchEasy1_1K     892ns × (0.99,1.01)   891ns × (1.00,1.01)    ~    (p=0.631)
RegexpMatchMedium_32    255ns × (0.99,1.01)   253ns × (0.99,1.01)  -0.76% (p=0.008)
RegexpMatchMedium_1K   73.1µs × (1.00,1.01)  72.9µs × (1.00,1.00)    ~    (p=0.229)
RegexpMatchHard_32     3.86µs × (1.00,1.01)  3.85µs × (1.00,1.00)    ~    (p=0.341)
RegexpMatchHard_1K      117µs × (1.00,1.01)   117µs × (0.99,1.00)    ~    (p=0.955)
Revcomp                 954ms × (0.97,1.03)   955ms × (0.98,1.02)    ~    (p=0.894)
Template                133ms × (0.97,1.05)   129ms × (0.99,1.02)  -2.50% (p=0.014)
TimeParse               629ns × (0.99,1.01)   626ns × (0.99,1.01)    ~    (p=0.106)
TimeFormat              663ns × (0.99,1.01)   660ns × (0.99,1.02)    ~    (p=0.231)

Change-Id: I580e03ed01b0629cb5eae4c4637618f20127f924
Reviewed-on: https://go-review.googlesource.com/9994
Reviewed-by: Austin Clements 
---
 src/runtime/mbitmap.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 2d2abca643..fcfcc7261c 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -365,11 +365,13 @@ func heapBitsBulkBarrier(p, size uintptr) {
 		return
 	}
 
+	h := heapBitsForAddr(p)
 	for i := uintptr(0); i < size; i += ptrSize {
-		if heapBitsForAddr(p + i).isPointer() {
+		if h.isPointer() {
 			x := (*uintptr)(unsafe.Pointer(p + i))
 			writebarrierptr_nostore(x, *x)
 		}
+		h = h.next()
 	}
 }
 

From 7e26a2d9a80b825d019c2cdaf6437d89001506a9 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 14 May 2015 17:27:04 -0400
Subject: [PATCH 102/232] runtime: allocate map element zero values for
 reflect-created types on demand

Preallocating them in reflect means that
(1) if you say _ = PtrTo(ArrayOf(1000000000, reflect.TypeOf(byte(0)))), you just allocated 1GB of data
(2) if you say it again, that's *another* GB of data.

The only use of t.zero in the runtime is for map elements.
Delay the allocation until the creation of a map with that element type,
and share the zeros.

The one downside of the shared zero is that it's not garbage collected,
but it's also never written, so the OS should be able to handle it fairly
efficiently.

Change-Id: I56b098a091abf3ac0945de28ebef9a6c08e76614
Reviewed-on: https://go-review.googlesource.com/10111
Reviewed-by: Keith Randall 
---
 src/reflect/type.go    |  9 --------
 src/runtime/hashmap.go | 48 ++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 9 deletions(-)

diff --git a/src/reflect/type.go b/src/reflect/type.go
index 5a43805626..bffe2595dd 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1087,7 +1087,6 @@ func (t *rtype) ptrTo() *rtype {
 
 	p.uncommonType = nil
 	p.ptrToThis = nil
-	p.zero = unsafe.Pointer(&make([]byte, p.size)[0])
 	p.elem = t
 
 	ptrMap.m[t] = p
@@ -1467,7 +1466,6 @@ func ChanOf(dir ChanDir, t Type) Type {
 	ch.elem = typ
 	ch.uncommonType = nil
 	ch.ptrToThis = nil
-	ch.zero = unsafe.Pointer(&make([]byte, ch.size)[0])
 
 	return cachePut(ckey, &ch.rtype)
 }
@@ -1530,7 +1528,6 @@ func MapOf(key, elem Type) Type {
 	mt.reflexivekey = isReflexive(ktyp)
 	mt.uncommonType = nil
 	mt.ptrToThis = nil
-	mt.zero = unsafe.Pointer(&make([]byte, mt.size)[0])
 
 	return cachePut(ckey, &mt.rtype)
 }
@@ -1610,7 +1607,6 @@ func FuncOf(in, out []Type, variadic bool) Type {
 	ft.string = &str
 	ft.uncommonType = nil
 	ft.ptrToThis = nil
-	ft.zero = unsafe.Pointer(&make([]byte, ft.size)[0])
 	funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype)
 
 	return ft
@@ -1857,7 +1853,6 @@ func SliceOf(t Type) Type {
 	slice.elem = typ
 	slice.uncommonType = nil
 	slice.ptrToThis = nil
-	slice.zero = unsafe.Pointer(&make([]byte, slice.size)[0])
 
 	return cachePut(ckey, &slice.rtype)
 }
@@ -1913,10 +1908,6 @@ func ArrayOf(count int, elem Type) Type {
 	array.fieldAlign = typ.fieldAlign
 	array.uncommonType = nil
 	array.ptrToThis = nil
-	if array.size > 0 {
-		zero := make([]byte, array.size)
-		array.zero = unsafe.Pointer(&zero[0])
-	}
 	array.len = uintptr(count)
 	array.slice = slice.(*rtype)
 
diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index 9ca33992bb..2b3af301b3 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -233,6 +233,9 @@ func makemap(t *maptype, hint int64, h *hmap, bucket unsafe.Pointer) *hmap {
 		throw("need padding in bucket (value)")
 	}
 
+	// make sure zero of element type is available.
+	mapzero(t.elem)
+
 	// find size parameter which will hold the requested # of elements
 	B := uint8(0)
 	for ; hint > bucketCnt && float32(hint) > loadFactor*float32(uintptr(1)<2GB zero on 32-bit machine
+				throw("map element too large")
+			}
+		}
+		zerobuf.p = (*byte)(persistentalloc(zerobuf.size, 64, &memstats.other_sys))
+	}
+	atomicstorep(unsafe.Pointer(&t.zero), unsafe.Pointer(zerobuf.p))
+	unlock(&zerobuf.lock)
+}

From 0112f6f6b605b17194175aa7cfe15b4054f862fc Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Wed, 22 Apr 2015 20:08:03 -0700
Subject: [PATCH 103/232] cmd/5g, etc: prepare to unexport gc.Mp*

Remove all uses of Mp* outside of the gc package.

A subsequent, automated commit in the Go 1.6
cycle will unexport all Mp* functions and types.

No functional changes. Passes toolstash -cmp.

Change-Id: Ie1604cb5b84ffb30b47f4777d4235570f2c62709
Reviewed-on: https://go-review.googlesource.com/9263
Reviewed-by: Russ Cox 
---
 src/cmd/5g/cgen64.go         |  6 ++---
 src/cmd/5g/ggen.go           |  8 +++---
 src/cmd/5g/gsubr.go          |  6 ++---
 src/cmd/6g/ggen.go           |  6 ++---
 src/cmd/6g/gsubr.go          | 40 +++++++++++++---------------
 src/cmd/7g/ggen.go           |  6 ++---
 src/cmd/7g/gsubr.go          |  4 +--
 src/cmd/8g/cgen64.go         | 11 ++++----
 src/cmd/8g/ggen.go           |  6 ++---
 src/cmd/8g/gsubr.go          | 51 ++++++++++++++++++------------------
 src/cmd/9g/ggen.go           |  6 ++---
 src/cmd/9g/gsubr.go          | 37 +++++++++++++-------------
 src/cmd/internal/gc/const.go | 28 ++++++++++++++++++++
 13 files changed, 119 insertions(+), 96 deletions(-)

diff --git a/src/cmd/5g/cgen64.go b/src/cmd/5g/cgen64.go
index 699e555f71..c55e000adc 100644
--- a/src/cmd/5g/cgen64.go
+++ b/src/cmd/5g/cgen64.go
@@ -237,7 +237,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
 	//	shld hi:lo, c
 	//	shld lo:t, c
 	case gc.OLROT:
-		v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+		v := uint64(r.Int())
 
 		var bl gc.Node
 		gc.Regalloc(&bl, lo1.Type, nil)
@@ -291,7 +291,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
 		var p4 *obj.Prog
 		var p5 *obj.Prog
 		if r.Op == gc.OLITERAL {
-			v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+			v := uint64(r.Int())
 			if v >= 64 {
 				// TODO(kaib): replace with gins(AMOVW, nodintconst(0), &al)
 				// here and below (verify it optimizes to EOR)
@@ -452,7 +452,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
 		var creg gc.Node
 		var p3 *obj.Prog
 		if r.Op == gc.OLITERAL {
-			v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+			v := uint64(r.Int())
 			if v >= 64 {
 				if bh.Type.Etype == gc.TINT32 {
 					//	MOVW	bh->31, al
diff --git a/src/cmd/5g/ggen.go b/src/cmd/5g/ggen.go
index c2bd6dda0a..e4612362a2 100644
--- a/src/cmd/5g/ggen.go
+++ b/src/cmd/5g/ggen.go
@@ -183,7 +183,7 @@ func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
 	w := int(nl.Type.Width * 8)
 
 	if op == gc.OLROT {
-		v := int(gc.Mpgetfix(nr.Val.U.Xval))
+		v := nr.Int()
 		var n1 gc.Node
 		gc.Regalloc(&n1, nl.Type, res)
 		if w == 32 {
@@ -210,7 +210,7 @@ func cgen_shift(op int, bounded bool, nl *gc.Node, nr *gc.Node, res *gc.Node) {
 		var n1 gc.Node
 		gc.Regalloc(&n1, nl.Type, res)
 		gc.Cgen(nl, &n1)
-		sc := uint64(gc.Mpgetfix(nr.Val.U.Xval))
+		sc := uint64(nr.Int())
 		if sc == 0 {
 		} else // nothing to do
 		if sc >= uint64(nl.Type.Width*8) {
@@ -480,7 +480,7 @@ func ginscon(as int, c int64, n *gc.Node) {
 }
 
 func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
-	if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && gc.Mpgetfix(n1.Val.U.Xval) == 0 && n2.Op != gc.OLITERAL {
+	if gc.Isint[t.Etype] && n1.Op == gc.OLITERAL && n1.Int() == 0 && n2.Op != gc.OLITERAL {
 		op = gc.Brrev(op)
 		n1, n2 = n2, n1
 	}
@@ -489,7 +489,7 @@ func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
 	gc.Regalloc(&g1, n1.Type, &r1)
 	gc.Cgen(n1, &g1)
 	gmove(&g1, &r1)
-	if gc.Isint[t.Etype] && n2.Op == gc.OLITERAL && gc.Mpgetfix(n2.Val.U.Xval) == 0 {
+	if gc.Isint[t.Etype] && n2.Op == gc.OLITERAL && n2.Int() == 0 {
 		gins(arm.ACMP, &r1, n2)
 	} else {
 		gc.Regalloc(&r2, t, n2)
diff --git a/src/cmd/5g/gsubr.go b/src/cmd/5g/gsubr.go
index 57d511e6f6..db46d6e9ee 100644
--- a/src/cmd/5g/gsubr.go
+++ b/src/cmd/5g/gsubr.go
@@ -53,7 +53,7 @@ func ncon(i uint32) *gc.Node {
 	if ncon_n.Type == nil {
 		gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0)
 	}
-	gc.Mpmovecfix(ncon_n.Val.U.Xval, int64(i))
+	ncon_n.SetInt(int64(i))
 	return &ncon_n
 }
 
@@ -112,7 +112,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
 	case gc.OLITERAL:
 		var n1 gc.Node
 		gc.Convconst(&n1, n.Type, &n.Val)
-		i := gc.Mpgetfix(n1.Val.U.Xval)
+		i := n1.Int()
 		gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
 		i >>= 32
 		if n.Type.Etype == gc.TINT64 {
@@ -1118,7 +1118,7 @@ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool {
 		if !gc.Isconst(n, gc.CTINT) {
 			break
 		}
-		v := gc.Mpgetfix(n.Val.U.Xval)
+		v := n.Int()
 		if v >= 32000 || v <= -32000 {
 			break
 		}
diff --git a/src/cmd/6g/ggen.go b/src/cmd/6g/ggen.go
index 6e5e6bc4ca..12198d7187 100644
--- a/src/cmd/6g/ggen.go
+++ b/src/cmd/6g/ggen.go
@@ -190,9 +190,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
 	check := 0
 	if gc.Issigned[t.Etype] {
 		check = 1
-		if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<= uint64(nl.Type.Width*8) {
 			// large shift gets 2 shifts by width-1
 			var n3 gc.Node
diff --git a/src/cmd/6g/gsubr.go b/src/cmd/6g/gsubr.go
index 14e1a57cbd..4e54bc8de5 100644
--- a/src/cmd/6g/gsubr.go
+++ b/src/cmd/6g/gsubr.go
@@ -32,6 +32,7 @@ package main
 
 import (
 	"cmd/internal/gc"
+	"cmd/internal/gc/big"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 	"fmt"
@@ -139,29 +140,27 @@ func ginsboolval(a int, n *gc.Node) {
 	gins(jmptoset(a), nil, n)
 }
 
-/*
- * set up nodes representing 2^63
- */
-var bigi gc.Node
-
-var bigf gc.Node
-
-var bignodes_did int
+// set up nodes representing 2^63
+var (
+	bigi         gc.Node
+	bigf         gc.Node
+	bignodes_did bool
+)
 
 func bignodes() {
-	if bignodes_did != 0 {
+	if bignodes_did {
 		return
 	}
-	bignodes_did = 1
+	bignodes_did = true
 
-	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1)
-	gc.Mpshiftfix(bigi.Val.U.Xval, 63)
+	var i big.Int
+	i.SetInt64(1)
+	i.Lsh(&i, 63)
 
-	bigf = bigi
-	bigf.Type = gc.Types[gc.TFLOAT64]
-	bigf.Val.Ctype = gc.CTFLT
-	bigf.Val.U.Fval = new(gc.Mpflt)
-	gc.Mpmovefixflt(bigf.Val.U.Fval, bigi.Val.U.Xval)
+	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+	bigi.SetBigInt(&i)
+
+	gc.Convconst(&bigf, gc.Types[gc.TFLOAT64], &bigi.Val)
 }
 
 /*
@@ -206,10 +205,7 @@ func gmove(f *gc.Node, t *gc.Node) {
 			// 64-bit immediates are really 32-bit sign-extended
 			// unless moving into a register.
 			if gc.Isint[tt] {
-				if gc.Mpcmpfixfix(con.Val.U.Xval, gc.Minintval[gc.TINT32]) < 0 {
-					goto hard
-				}
-				if gc.Mpcmpfixfix(con.Val.U.Xval, gc.Maxintval[gc.TINT32]) > 0 {
+				if i := con.Int(); int64(int32(i)) != i {
 					goto hard
 				}
 			}
@@ -1273,7 +1269,7 @@ func sudoaddable(as int, n *gc.Node, a *obj.Addr) bool {
 		if !gc.Isconst(n, gc.CTINT) {
 			break
 		}
-		v := gc.Mpgetfix(n.Val.U.Xval)
+		v := n.Int()
 		if v >= 32000 || v <= -32000 {
 			break
 		}
diff --git a/src/cmd/7g/ggen.go b/src/cmd/7g/ggen.go
index b824a3a18c..af51c31648 100644
--- a/src/cmd/7g/ggen.go
+++ b/src/cmd/7g/ggen.go
@@ -147,9 +147,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
 	check := 0
 	if gc.Issigned[t.Etype] {
 		check = 1
-		if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<= uint64(nl.Type.Width*8) {
 			// large shift gets 2 shifts by width-1
 			var n3 gc.Node
diff --git a/src/cmd/7g/gsubr.go b/src/cmd/7g/gsubr.go
index 60c3a7ad44..2f03b121b4 100644
--- a/src/cmd/7g/gsubr.go
+++ b/src/cmd/7g/gsubr.go
@@ -115,7 +115,7 @@ func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
 	gc.Cgen(n1, &g1)
 	gmove(&g1, &r1)
 	if gc.Isint[t.Etype] && gc.Isconst(n2, gc.CTINT) {
-		ginscon2(optoas(gc.OCMP, t), &r1, gc.Mpgetfix(n2.Val.U.Xval))
+		ginscon2(optoas(gc.OCMP, t), &r1, n2.Int())
 	} else {
 		gc.Regalloc(&r2, t, n2)
 		gc.Regalloc(&g2, n1.Type, &r2)
@@ -473,7 +473,7 @@ func intLiteral(n *gc.Node) (x int64, ok bool) {
 	}
 	switch n.Val.Ctype {
 	case gc.CTINT, gc.CTRUNE:
-		return gc.Mpgetfix(n.Val.U.Xval), true
+		return n.Int(), true
 	case gc.CTBOOL:
 		return int64(obj.Bool2int(n.Val.U.Bval)), true
 	}
diff --git a/src/cmd/8g/cgen64.go b/src/cmd/8g/cgen64.go
index a682e2fb44..80a9642f75 100644
--- a/src/cmd/8g/cgen64.go
+++ b/src/cmd/8g/cgen64.go
@@ -162,7 +162,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
 	//	shld hi:lo, c
 	//	shld lo:t, c
 	case gc.OLROT:
-		v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+		v := uint64(r.Int())
 
 		if v >= 32 {
 			// reverse during load to do the first 32 bits of rotate
@@ -189,7 +189,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
 
 	case gc.OLSH:
 		if r.Op == gc.OLITERAL {
-			v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+			v := uint64(r.Int())
 			if v >= 64 {
 				if gc.Is64(r.Type) {
 					splitclean()
@@ -278,7 +278,7 @@ func cgen64(n *gc.Node, res *gc.Node) {
 
 	case gc.ORSH:
 		if r.Op == gc.OLITERAL {
-			v := uint64(gc.Mpgetfix(r.Val.U.Xval))
+			v := uint64(r.Int())
 			if v >= 64 {
 				if gc.Is64(r.Type) {
 					splitclean()
@@ -400,9 +400,8 @@ func cgen64(n *gc.Node, res *gc.Node) {
 
 		if lo2.Op == gc.OLITERAL {
 			// special cases for constants.
-			lv := uint32(gc.Mpgetfix(lo2.Val.U.Xval))
-
-			hv := uint32(gc.Mpgetfix(hi2.Val.U.Xval))
+			lv := uint32(lo2.Int())
+			hv := uint32(hi2.Int())
 			splitclean() // right side
 			split64(res, &lo2, &hi2)
 			switch n.Op {
diff --git a/src/cmd/8g/ggen.go b/src/cmd/8g/ggen.go
index 59025525fa..baa1b64d1e 100644
--- a/src/cmd/8g/ggen.go
+++ b/src/cmd/8g/ggen.go
@@ -216,9 +216,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node, ax *gc.Node, dx *gc.N
 	check := 0
 	if gc.Issigned[t.Etype] {
 		check = 1
-		if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -1<= uint64(nl.Type.Width*8) {
 			// large shift gets 2 shifts by width-1
 			gins(a, ncon(uint32(w)-1), &n1)
diff --git a/src/cmd/8g/gsubr.go b/src/cmd/8g/gsubr.go
index d1134d2c74..6878883b28 100644
--- a/src/cmd/8g/gsubr.go
+++ b/src/cmd/8g/gsubr.go
@@ -32,6 +32,7 @@ package main
 
 import (
 	"cmd/internal/gc"
+	"cmd/internal/gc/big"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 	"fmt"
@@ -641,7 +642,7 @@ func ncon(i uint32) *gc.Node {
 	if ncon_n.Type == nil {
 		gc.Nodconst(&ncon_n, gc.Types[gc.TUINT32], 0)
 	}
-	gc.Mpmovecfix(ncon_n.Val.U.Xval, int64(i))
+	ncon_n.SetInt(int64(i))
 	return &ncon_n
 }
 
@@ -700,7 +701,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
 	case gc.OLITERAL:
 		var n1 gc.Node
 		gc.Convconst(&n1, n.Type, &n.Val)
-		i := gc.Mpgetfix(n1.Val.U.Xval)
+		i := n1.Int()
 		gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
 		i >>= 32
 		if n.Type.Etype == gc.TINT64 {
@@ -721,36 +722,36 @@ func splitclean() {
 	}
 }
 
-/*
- * set up nodes representing fp constants
- */
-var zerof gc.Node
-
-var two64f gc.Node
-
-var two63f gc.Node
-
-var bignodes_did int
+// set up nodes representing fp constants
+var (
+	zerof        gc.Node
+	two63f       gc.Node
+	two64f       gc.Node
+	bignodes_did bool
+)
 
 func bignodes() {
-	if bignodes_did != 0 {
+	if bignodes_did {
 		return
 	}
-	bignodes_did = 1
+	bignodes_did = true
 
-	two64f = *ncon(0)
-	two64f.Type = gc.Types[gc.TFLOAT64]
-	two64f.Val.Ctype = gc.CTFLT
-	two64f.Val.U.Fval = new(gc.Mpflt)
-	gc.Mpmovecflt(two64f.Val.U.Fval, 18446744073709551616.)
+	gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0)
+	gc.Convconst(&zerof, gc.Types[gc.TFLOAT64], &zerof.Val)
 
-	two63f = two64f
-	two63f.Val.U.Fval = new(gc.Mpflt)
-	gc.Mpmovecflt(two63f.Val.U.Fval, 9223372036854775808.)
+	var i big.Int
+	i.SetInt64(1)
+	i.Lsh(&i, 63)
+	var bigi gc.Node
 
-	zerof = two64f
-	zerof.Val.U.Fval = new(gc.Mpflt)
-	gc.Mpmovecflt(zerof.Val.U.Fval, 0)
+	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+	bigi.SetBigInt(&i)
+	gc.Convconst(&two63f, gc.Types[gc.TFLOAT64], &bigi.Val)
+
+	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+	i.Lsh(&i, 1)
+	bigi.SetBigInt(&i)
+	gc.Convconst(&two64f, gc.Types[gc.TFLOAT64], &bigi.Val)
 }
 
 func memname(n *gc.Node, t *gc.Type) {
diff --git a/src/cmd/9g/ggen.go b/src/cmd/9g/ggen.go
index 28ebd9cc01..265536921a 100644
--- a/src/cmd/9g/ggen.go
+++ b/src/cmd/9g/ggen.go
@@ -141,9 +141,9 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
 	check := 0
 	if gc.Issigned[t.Etype] {
 		check = 1
-		if gc.Isconst(nl, gc.CTINT) && gc.Mpgetfix(nl.Val.U.Xval) != -(1<= uint64(nl.Type.Width*8) {
 			// large shift gets 2 shifts by width-1
 			var n3 gc.Node
diff --git a/src/cmd/9g/gsubr.go b/src/cmd/9g/gsubr.go
index 61ba87ee3e..f14f93734c 100644
--- a/src/cmd/9g/gsubr.go
+++ b/src/cmd/9g/gsubr.go
@@ -32,6 +32,7 @@ package main
 
 import (
 	"cmd/internal/gc"
+	"cmd/internal/gc/big"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
 	"fmt"
@@ -129,7 +130,7 @@ func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
 	gc.Cgen(n1, &g1)
 	gmove(&g1, &r1)
 	if gc.Isint[t.Etype] && gc.Isconst(n2, gc.CTINT) {
-		ginscon2(optoas(gc.OCMP, t), &r1, gc.Mpgetfix(n2.Val.U.Xval))
+		ginscon2(optoas(gc.OCMP, t), &r1, n2.Int())
 	} else {
 		gc.Regalloc(&r2, t, n2)
 		gc.Regalloc(&g2, n1.Type, &r2)
@@ -144,29 +145,27 @@ func ginscmp(op int, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
 	return gc.Gbranch(optoas(op, t), nil, likely)
 }
 
-/*
- * set up nodes representing 2^63
- */
-var bigi gc.Node
-
-var bigf gc.Node
-
-var bignodes_did int
+// set up nodes representing 2^63
+var (
+	bigi         gc.Node
+	bigf         gc.Node
+	bignodes_did bool
+)
 
 func bignodes() {
-	if bignodes_did != 0 {
+	if bignodes_did {
 		return
 	}
-	bignodes_did = 1
+	bignodes_did = true
 
-	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 1)
-	gc.Mpshiftfix(bigi.Val.U.Xval, 63)
+	var i big.Int
+	i.SetInt64(1)
+	i.Lsh(&i, 63)
 
-	bigf = bigi
-	bigf.Type = gc.Types[gc.TFLOAT64]
-	bigf.Val.Ctype = gc.CTFLT
-	bigf.Val.U.Fval = new(gc.Mpflt)
-	gc.Mpmovefixflt(bigf.Val.U.Fval, bigi.Val.U.Xval)
+	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
+	bigi.SetBigInt(&i)
+
+	gc.Convconst(&bigf, gc.Types[gc.TFLOAT64], &bigi.Val)
 }
 
 /*
@@ -552,7 +551,7 @@ func intLiteral(n *gc.Node) (x int64, ok bool) {
 	}
 	switch n.Val.Ctype {
 	case gc.CTINT, gc.CTRUNE:
-		return gc.Mpgetfix(n.Val.U.Xval), true
+		return n.Int(), true
 	case gc.CTBOOL:
 		return int64(obj.Bool2int(n.Val.U.Bval)), true
 	}
diff --git a/src/cmd/internal/gc/const.go b/src/cmd/internal/gc/const.go
index 84b133769c..748752679b 100644
--- a/src/cmd/internal/gc/const.go
+++ b/src/cmd/internal/gc/const.go
@@ -5,10 +5,38 @@
 package gc
 
 import (
+	"cmd/internal/gc/big"
 	"cmd/internal/obj"
 	"strings"
 )
 
+// Int returns n as an int.
+// n must be an integer constant.
+func (n *Node) Int() int64 {
+	if !Isconst(n, CTINT) {
+		Fatal("Int(%v)", n)
+	}
+	return Mpgetfix(n.Val.U.Xval)
+}
+
+// SetInt sets n's value to i.
+// n must be an integer constant.
+func (n *Node) SetInt(i int64) {
+	if !Isconst(n, CTINT) {
+		Fatal("SetInt(%v)", n)
+	}
+	Mpmovecfix(n.Val.U.Xval, i)
+}
+
+// SetBigInt sets n's value to x.
+// n must be an integer constant.
+func (n *Node) SetBigInt(x *big.Int) {
+	if !Isconst(n, CTINT) {
+		Fatal("SetBigInt(%v)", n)
+	}
+	n.Val.U.Xval.Val.Set(x)
+}
+
 /*
  * truncate float literal fv to 32-bit or 64-bit precision
  * according to type; return truncated value.

From ba57781181b18187958756b7ad7ccb126728de2d Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Thu, 7 May 2015 18:43:03 -0700
Subject: [PATCH 104/232] cmd/5g, etc: prepare Node.Val to be unexported

Remove all uses of Node.Val outside of the gc package.

A subsequent, automated commit in the Go 1.6 cycle
will unexport Node.Val.

No functional changes. Passes toolstash -cmp.

Change-Id: Ia92ae6a7766c83ab3e45c69edab24a9581c824f9
Reviewed-on: https://go-review.googlesource.com/9267
Reviewed-by: Russ Cox 
---
 src/cmd/5g/gsubr.go          |  8 ++++----
 src/cmd/6g/gsubr.go          |  4 ++--
 src/cmd/7g/gsubr.go          | 17 ++++++++---------
 src/cmd/8g/gsubr.go          | 12 ++++++------
 src/cmd/9g/gsubr.go          | 19 +++++++++----------
 src/cmd/internal/gc/const.go | 28 +++++++++++++++++-----------
 6 files changed, 46 insertions(+), 42 deletions(-)

diff --git a/src/cmd/5g/gsubr.go b/src/cmd/5g/gsubr.go
index db46d6e9ee..2f70bfd468 100644
--- a/src/cmd/5g/gsubr.go
+++ b/src/cmd/5g/gsubr.go
@@ -111,7 +111,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
 
 	case gc.OLITERAL:
 		var n1 gc.Node
-		gc.Convconst(&n1, n.Type, &n.Val)
+		n.Convconst(&n1, n.Type)
 		i := n1.Int()
 		gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
 		i >>= 32
@@ -160,12 +160,12 @@ func gmove(f *gc.Node, t *gc.Node) {
 		var con gc.Node
 		switch tt {
 		default:
-			gc.Convconst(&con, t.Type, &f.Val)
+			f.Convconst(&con, t.Type)
 
 		case gc.TINT16,
 			gc.TINT8:
 			var con gc.Node
-			gc.Convconst(&con, gc.Types[gc.TINT32], &f.Val)
+			f.Convconst(&con, gc.Types[gc.TINT32])
 			var r1 gc.Node
 			gc.Regalloc(&r1, con.Type, t)
 			gins(arm.AMOVW, &con, &r1)
@@ -176,7 +176,7 @@ func gmove(f *gc.Node, t *gc.Node) {
 		case gc.TUINT16,
 			gc.TUINT8:
 			var con gc.Node
-			gc.Convconst(&con, gc.Types[gc.TUINT32], &f.Val)
+			f.Convconst(&con, gc.Types[gc.TUINT32])
 			var r1 gc.Node
 			gc.Regalloc(&r1, con.Type, t)
 			gins(arm.AMOVW, &con, &r1)
diff --git a/src/cmd/6g/gsubr.go b/src/cmd/6g/gsubr.go
index 4e54bc8de5..9b9141468e 100644
--- a/src/cmd/6g/gsubr.go
+++ b/src/cmd/6g/gsubr.go
@@ -160,7 +160,7 @@ func bignodes() {
 	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
 	bigi.SetBigInt(&i)
 
-	gc.Convconst(&bigf, gc.Types[gc.TFLOAT64], &bigi.Val)
+	bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64])
 }
 
 /*
@@ -191,7 +191,7 @@ func gmove(f *gc.Node, t *gc.Node) {
 	// convert constant to desired type
 	if f.Op == gc.OLITERAL {
 		var con gc.Node
-		gc.Convconst(&con, t.Type, &f.Val)
+		f.Convconst(&con, t.Type)
 		f = &con
 		ft = tt // so big switch will choose a simple mov
 
diff --git a/src/cmd/7g/gsubr.go b/src/cmd/7g/gsubr.go
index 2f03b121b4..0f617079ad 100644
--- a/src/cmd/7g/gsubr.go
+++ b/src/cmd/7g/gsubr.go
@@ -161,13 +161,13 @@ func gmove(f *gc.Node, t *gc.Node) {
 		var con gc.Node
 		switch tt {
 		default:
-			gc.Convconst(&con, t.Type, &f.Val)
+			f.Convconst(&con, t.Type)
 
 		case gc.TINT32,
 			gc.TINT16,
 			gc.TINT8:
 			var con gc.Node
-			gc.Convconst(&con, gc.Types[gc.TINT64], &f.Val)
+			f.Convconst(&con, gc.Types[gc.TINT64])
 			var r1 gc.Node
 			gc.Regalloc(&r1, con.Type, t)
 			gins(arm64.AMOVD, &con, &r1)
@@ -179,7 +179,7 @@ func gmove(f *gc.Node, t *gc.Node) {
 			gc.TUINT16,
 			gc.TUINT8:
 			var con gc.Node
-			gc.Convconst(&con, gc.Types[gc.TUINT64], &f.Val)
+			f.Convconst(&con, gc.Types[gc.TUINT64])
 			var r1 gc.Node
 			gc.Regalloc(&r1, con.Type, t)
 			gins(arm64.AMOVD, &con, &r1)
@@ -468,14 +468,13 @@ hard:
 }
 
 func intLiteral(n *gc.Node) (x int64, ok bool) {
-	if n == nil || n.Op != gc.OLITERAL {
+	switch {
+	case n == nil:
 		return
-	}
-	switch n.Val.Ctype {
-	case gc.CTINT, gc.CTRUNE:
+	case gc.Isconst(n, gc.CTINT):
 		return n.Int(), true
-	case gc.CTBOOL:
-		return int64(obj.Bool2int(n.Val.U.Bval)), true
+	case gc.Isconst(n, gc.CTBOOL):
+		return int64(obj.Bool2int(n.Bool())), true
 	}
 	return
 }
diff --git a/src/cmd/8g/gsubr.go b/src/cmd/8g/gsubr.go
index 6878883b28..b0b0aedabc 100644
--- a/src/cmd/8g/gsubr.go
+++ b/src/cmd/8g/gsubr.go
@@ -700,7 +700,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
 
 	case gc.OLITERAL:
 		var n1 gc.Node
-		gc.Convconst(&n1, n.Type, &n.Val)
+		n.Convconst(&n1, n.Type)
 		i := n1.Int()
 		gc.Nodconst(lo, gc.Types[gc.TUINT32], int64(uint32(i)))
 		i >>= 32
@@ -737,7 +737,7 @@ func bignodes() {
 	bignodes_did = true
 
 	gc.Nodconst(&zerof, gc.Types[gc.TINT64], 0)
-	gc.Convconst(&zerof, gc.Types[gc.TFLOAT64], &zerof.Val)
+	zerof.Convconst(&zerof, gc.Types[gc.TFLOAT64])
 
 	var i big.Int
 	i.SetInt64(1)
@@ -746,12 +746,12 @@ func bignodes() {
 
 	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
 	bigi.SetBigInt(&i)
-	gc.Convconst(&two63f, gc.Types[gc.TFLOAT64], &bigi.Val)
+	bigi.Convconst(&two63f, gc.Types[gc.TFLOAT64])
 
 	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
 	i.Lsh(&i, 1)
 	bigi.SetBigInt(&i)
-	gc.Convconst(&two64f, gc.Types[gc.TFLOAT64], &bigi.Val)
+	bigi.Convconst(&two64f, gc.Types[gc.TFLOAT64])
 }
 
 func memname(n *gc.Node, t *gc.Type) {
@@ -790,7 +790,7 @@ func gmove(f *gc.Node, t *gc.Node) {
 	// convert constant to desired type
 	if f.Op == gc.OLITERAL {
 		var con gc.Node
-		gc.Convconst(&con, t.Type, &f.Val)
+		f.Convconst(&con, t.Type)
 		f = &con
 		ft = gc.Simsimtype(con.Type)
 	}
@@ -1061,7 +1061,7 @@ func floatmove(f *gc.Node, t *gc.Node) {
 	// convert constant to desired type
 	if f.Op == gc.OLITERAL {
 		var con gc.Node
-		gc.Convconst(&con, t.Type, &f.Val)
+		f.Convconst(&con, t.Type)
 		f = &con
 		ft = gc.Simsimtype(con.Type)
 
diff --git a/src/cmd/9g/gsubr.go b/src/cmd/9g/gsubr.go
index f14f93734c..3a7c884fd1 100644
--- a/src/cmd/9g/gsubr.go
+++ b/src/cmd/9g/gsubr.go
@@ -165,7 +165,7 @@ func bignodes() {
 	gc.Nodconst(&bigi, gc.Types[gc.TUINT64], 0)
 	bigi.SetBigInt(&i)
 
-	gc.Convconst(&bigf, gc.Types[gc.TFLOAT64], &bigi.Val)
+	bigi.Convconst(&bigf, gc.Types[gc.TFLOAT64])
 }
 
 /*
@@ -200,13 +200,13 @@ func gmove(f *gc.Node, t *gc.Node) {
 		var con gc.Node
 		switch tt {
 		default:
-			gc.Convconst(&con, t.Type, &f.Val)
+			f.Convconst(&con, t.Type)
 
 		case gc.TINT32,
 			gc.TINT16,
 			gc.TINT8:
 			var con gc.Node
-			gc.Convconst(&con, gc.Types[gc.TINT64], &f.Val)
+			f.Convconst(&con, gc.Types[gc.TINT64])
 			var r1 gc.Node
 			gc.Regalloc(&r1, con.Type, t)
 			gins(ppc64.AMOVD, &con, &r1)
@@ -218,7 +218,7 @@ func gmove(f *gc.Node, t *gc.Node) {
 			gc.TUINT16,
 			gc.TUINT8:
 			var con gc.Node
-			gc.Convconst(&con, gc.Types[gc.TUINT64], &f.Val)
+			f.Convconst(&con, gc.Types[gc.TUINT64])
 			var r1 gc.Node
 			gc.Regalloc(&r1, con.Type, t)
 			gins(ppc64.AMOVD, &con, &r1)
@@ -546,14 +546,13 @@ hard:
 }
 
 func intLiteral(n *gc.Node) (x int64, ok bool) {
-	if n == nil || n.Op != gc.OLITERAL {
+	switch {
+	case n == nil:
 		return
-	}
-	switch n.Val.Ctype {
-	case gc.CTINT, gc.CTRUNE:
+	case gc.Isconst(n, gc.CTINT):
 		return n.Int(), true
-	case gc.CTBOOL:
-		return int64(obj.Bool2int(n.Val.U.Bval)), true
+	case gc.Isconst(n, gc.CTBOOL):
+		return int64(obj.Bool2int(n.Bool())), true
 	}
 	return
 }
diff --git a/src/cmd/internal/gc/const.go b/src/cmd/internal/gc/const.go
index 748752679b..69f2e5c904 100644
--- a/src/cmd/internal/gc/const.go
+++ b/src/cmd/internal/gc/const.go
@@ -37,6 +37,15 @@ func (n *Node) SetBigInt(x *big.Int) {
 	n.Val.U.Xval.Val.Set(x)
 }
 
+// Bool returns n as an bool.
+// n must be an boolean constant.
+func (n *Node) Bool() bool {
+	if !Isconst(n, CTBOOL) {
+		Fatal("Int(%v)", n)
+	}
+	return n.Val.U.Bval
+}
+
 /*
  * truncate float literal fv to 32-bit or 64-bit precision
  * according to type; return truncated value.
@@ -1426,32 +1435,30 @@ func iconv(x int64, et int) int64 {
 	return x
 }
 
-/*
- * convert constant val to type t; leave in con.
- * for back end.
- */
-func Convconst(con *Node, t *Type, val *Val) {
+// Convconst converts constant node n to type t and
+// places the result in con.
+func (n *Node) Convconst(con *Node, t *Type) {
 	tt := Simsimtype(t)
 
 	// copy the constant for conversion
 	Nodconst(con, Types[TINT8], 0)
 
 	con.Type = t
-	con.Val = *val
+	con.Val = n.Val
 
 	if Isint[tt] {
 		con.Val.Ctype = CTINT
 		con.Val.U.Xval = new(Mpint)
 		var i int64
-		switch val.Ctype {
+		switch n.Val.Ctype {
 		default:
-			Fatal("convconst ctype=%d %v", val.Ctype, Tconv(t, obj.FmtLong))
+			Fatal("convconst ctype=%d %v", n.Val.Ctype, Tconv(t, obj.FmtLong))
 
 		case CTINT, CTRUNE:
-			i = Mpgetfix(val.U.Xval)
+			i = Mpgetfix(n.Val.U.Xval)
 
 		case CTBOOL:
-			i = int64(obj.Bool2int(val.U.Bval))
+			i = int64(obj.Bool2int(n.Val.U.Bval))
 
 		case CTNIL:
 			i = 0
@@ -1479,7 +1486,6 @@ func Convconst(con *Node, t *Type, val *Val) {
 			con.Val.U.Cval.Real = *truncfltlit(&con.Val.U.Cval.Real, Types[TFLOAT32])
 			con.Val.U.Cval.Imag = *truncfltlit(&con.Val.U.Cval.Imag, Types[TFLOAT32])
 		}
-
 		return
 	}
 

From 3c06cff7d1d7382b74ab39bd3bf0e46264ac845d Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Thu, 14 May 2015 17:32:07 -0700
Subject: [PATCH 105/232] cmd/internal/gc: explicitly set zero bool Val

This trivial change is a prerequisite to
converting Val.U to an interface{}.

No functional changes. Passes toolstash -cmp.

Change-Id: I17ff036f68d29a9ed0097a8b23ae1c91e6ce8c21
Reviewed-on: https://go-review.googlesource.com/10058
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/gen.go | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go
index fcb2499d3b..8f6a43c121 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/internal/gc/gen.go
@@ -348,6 +348,7 @@ func Clearslim(n *Node) {
 
 	case TBOOL:
 		z.Val.Ctype = CTBOOL
+		z.Val.U.Bval = false
 
 	case TINT8,
 		TINT16,

From 13485be939d73d6d2a5a8407c74c5fd502e35a6f Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Thu, 14 May 2015 17:57:42 -0700
Subject: [PATCH 106/232] cmd/internal/gc: convert Val.U to interface{}

This CL was generated by updating Val in go.go
and then running:

sed -i "" 's/\.U\.[SBXFC]val = /.U = /' *.go
sed -i "" 's/\.U\.Sval/.U.\(string\)/g' *.go *.y
sed -i "" 's/\.U\.Bval/.U.\(bool\)/g' *.go *.y
sed -i "" 's/\.U\.Xval/.U.\(\*Mpint\)/g' *.go *.y
sed -i "" 's/\.U\.Fval/.U.\(\*Mpflt\)/g' *.go *.y
sed -i "" 's/\.U\.Cval/.U.\(\*Mpcplx\)/g' *.go *.y

No functional changes. Passes toolstash -cmp.

This reduces the size of gc.Node from 424 to 392 bytes.
This in turn reduces the permanent (pprof -inuse_space)
memory usage while compiling the test/rotate?.go tests:

test	old(MB)	new(MB)	change
rotate0	379.49	364.78	-3.87%
rotate1	373.42	359.07	-3.84%
rotate2	381.17	366.24	-3.91%
rotate3	374.30	359.95	-3.83%

CL 8445 was similar to this; gri asked that Val's implementation
be hidden first. CLs 8912, 9263, and 9267 have at least
isolated the changes to the cmd/internal/gc package.

Updates #9933.

Change-Id: I83ddfe003d48e0a73c92e819edd3b5e620023084
Reviewed-on: https://go-review.googlesource.com/10059
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/cgen.go      |  60 ++++----
 src/cmd/internal/gc/const.go     | 228 +++++++++++++++----------------
 src/cmd/internal/gc/cplx.go      |   6 +-
 src/cmd/internal/gc/dcl.go       |   2 +-
 src/cmd/internal/gc/fmt.go       |  32 ++---
 src/cmd/internal/gc/gen.go       |  18 +--
 src/cmd/internal/gc/go.go        |  14 +-
 src/cmd/internal/gc/go.y         |  28 ++--
 src/cmd/internal/gc/gsubr.go     |   8 +-
 src/cmd/internal/gc/lex.go       |  54 ++++----
 src/cmd/internal/gc/obj.go       |   4 +-
 src/cmd/internal/gc/order.go     |   2 +-
 src/cmd/internal/gc/sinit.go     |  24 ++--
 src/cmd/internal/gc/subr.go      |  24 ++--
 src/cmd/internal/gc/swt.go       |  10 +-
 src/cmd/internal/gc/typecheck.go |  54 ++++----
 src/cmd/internal/gc/unsafe.go    |   4 +-
 src/cmd/internal/gc/walk.go      |  48 +++----
 src/cmd/internal/gc/y.go         |  28 ++--
 19 files changed, 324 insertions(+), 324 deletions(-)

diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go
index a4b4f0b61b..7237e863ca 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/internal/gc/cgen.go
@@ -542,7 +542,7 @@ func cgen_wb(n, res *Node, wb bool) {
 			var n1 Node
 			Regalloc(&n1, Types[Tptr], res)
 			p1 := Thearch.Gins(Thearch.Optoas(OAS, n1.Type), nil, &n1)
-			Datastring(nl.Val.U.Sval, &p1.From)
+			Datastring(nl.Val.U.(string), &p1.From)
 			p1.From.Type = obj.TYPE_ADDR
 			Thearch.Gmove(&n1, res)
 			Regfree(&n1)
@@ -1030,7 +1030,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 				if Isconst(nl, CTSTR) {
 					Fatal("constant string constant index")
 				}
-				v := uint64(Mpgetfix(nr.Val.U.Xval))
+				v := uint64(Mpgetfix(nr.Val.U.(*Mpint)))
 				var n2 Node
 				if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
 					if Debug['B'] == 0 && !n.Bounded {
@@ -1066,7 +1066,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 			if Debug['B'] == 0 && !n.Bounded {
 				// check bounds
 				if Isconst(nl, CTSTR) {
-					Nodconst(&n4, Types[TUINT32], int64(len(nl.Val.U.Sval)))
+					Nodconst(&n4, Types[TUINT32], int64(len(nl.Val.U.(string))))
 				} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
 					n1 = n3
 					n1.Op = OINDREG
@@ -1091,7 +1091,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 			if Isconst(nl, CTSTR) {
 				Regalloc(&n3, Types[Tptr], res)
 				p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3)
-				Datastring(nl.Val.U.Sval, &p1.From)
+				Datastring(nl.Val.U.(string), &p1.From)
 				p1.From.Type = obj.TYPE_ADDR
 			} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
 				n1 = n3
@@ -1182,7 +1182,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 				if Isconst(nl, CTSTR) {
 					Fatal("constant string constant index") // front end should handle
 				}
-				v := uint64(Mpgetfix(nr.Val.U.Xval))
+				v := uint64(Mpgetfix(nr.Val.U.(*Mpint)))
 				if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
 					if Debug['B'] == 0 && !n.Bounded {
 						nlen := n3
@@ -1227,7 +1227,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 
 				var nlen Node
 				if Isconst(nl, CTSTR) {
-					Nodconst(&nlen, t, int64(len(nl.Val.U.Sval)))
+					Nodconst(&nlen, t, int64(len(nl.Val.U.(string))))
 				} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
 					nlen = n3
 					nlen.Type = t
@@ -1247,7 +1247,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 			if Isconst(nl, CTSTR) {
 				Regalloc(&n3, Types[Tptr], res)
 				p1 := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &n3)
-				Datastring(nl.Val.U.Sval, &p1.From)
+				Datastring(nl.Val.U.(string), &p1.From)
 				p1.From.Type = obj.TYPE_ADDR
 				Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3)
 				goto indexdone1
@@ -1372,7 +1372,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 			if Isconst(nl, CTSTR) {
 				Fatal("constant string constant index") // front end should handle
 			}
-			v := uint64(Mpgetfix(nr.Val.U.Xval))
+			v := uint64(Mpgetfix(nr.Val.U.(*Mpint)))
 			if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
 				if Debug['B'] == 0 && !n.Bounded {
 					p1 := Thearch.Ginscmp(OGT, Types[Simtype[TUINT]], &nlen, Nodintconst(int64(v)), +1)
@@ -1410,7 +1410,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 				t = Types[TUINT64]
 			}
 			if Isconst(nl, CTSTR) {
-				Nodconst(&nlen, t, int64(len(nl.Val.U.Sval)))
+				Nodconst(&nlen, t, int64(len(nl.Val.U.(string))))
 			} else if Isslice(nl.Type) || nl.Type.Etype == TSTRING {
 				// nlen already initialized
 			} else {
@@ -1425,7 +1425,7 @@ func Agenr(n *Node, a *Node, res *Node) {
 		if Isconst(nl, CTSTR) {
 			Regalloc(&n3, Types[Tptr], res)
 			p1 := Thearch.Gins(Thearch.Optoas(OAS, n3.Type), nil, &n3) // XXX was LEAQ!
-			Datastring(nl.Val.U.Sval, &p1.From)
+			Datastring(nl.Val.U.(string), &p1.From)
 			p1.From.Type = obj.TYPE_ADDR
 			Thearch.Gins(Thearch.Optoas(OADD, n3.Type), &n2, &n3)
 			goto indexdone
@@ -1712,7 +1712,7 @@ func Igen(n *Node, a *Node, res *Node) {
 				// Compute &a[i] as &a + i*width.
 				a.Type = n.Type
 
-				a.Xoffset += Mpgetfix(n.Right.Val.U.Xval) * n.Type.Width
+				a.Xoffset += Mpgetfix(n.Right.Val.U.(*Mpint)) * n.Type.Width
 				Fixlargeoffset(a)
 				return
 			}
@@ -1862,11 +1862,11 @@ func bgenx(n, res *Node, wantTrue bool, likely int, to *obj.Prog) {
 			Fatal("bgen: non-bool const %v\n", Nconv(n, obj.FmtLong))
 		}
 		if genval {
-			Cgen(Nodbool(wantTrue == n.Val.U.Bval), res)
+			Cgen(Nodbool(wantTrue == n.Val.U.(bool)), res)
 			return
 		}
 		// If n == wantTrue, jump; otherwise do nothing.
-		if wantTrue == n.Val.U.Bval {
+		if wantTrue == n.Val.U.(bool) {
 			Patch(Gbranch(obj.AJMP, nil, likely), to)
 		}
 		return
@@ -2187,7 +2187,7 @@ func stkof(n *Node) int64 {
 			return off
 		}
 		if Isconst(n.Right, CTINT) {
-			return off + t.Type.Width*Mpgetfix(n.Right.Val.U.Xval)
+			return off + t.Type.Width*Mpgetfix(n.Right.Val.U.(*Mpint))
 		}
 		return 1000
 
@@ -2642,7 +2642,7 @@ func cgen_div(op int, nl *Node, nr *Node, res *Node) {
 	case TUINT64:
 		var m Magic
 		m.W = w
-		m.Ud = uint64(Mpgetfix(nr.Val.U.Xval))
+		m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint)))
 		Umagic(&m)
 		if m.Bad != 0 {
 			break
@@ -2680,7 +2680,7 @@ func cgen_div(op int, nl *Node, nr *Node, res *Node) {
 	case TINT64:
 		var m Magic
 		m.W = w
-		m.Sd = Mpgetfix(nr.Val.U.Xval)
+		m.Sd = Mpgetfix(nr.Val.U.(*Mpint))
 		Smagic(&m)
 		if m.Bad != 0 {
 			break
@@ -3024,7 +3024,7 @@ func cgen_slice(n, res *Node, wb bool) {
 			return
 		}
 		if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
-			Nodconst(&xlen, indexRegType, int64(len(n.Left.Val.U.Sval)))
+			Nodconst(&xlen, indexRegType, int64(len(n.Left.Val.U.(string))))
 			return
 		}
 		regalloc(&xlen, indexRegType, nil)
@@ -3180,20 +3180,20 @@ func cgen_slice(n, res *Node, wb bool) {
 	if n.Op == OSLICEARR || n.Op == OSLICE3ARR {
 		bound = n.Left.Type.Type.Bound
 	} else if n.Op == OSLICESTR && Isconst(n.Left, CTSTR) {
-		bound = int64(len(n.Left.Val.U.Sval))
+		bound = int64(len(n.Left.Val.U.(string)))
 	}
 	if Isconst(&i, CTINT) {
-		if mpcmpfixc(i.Val.U.Xval, 0) < 0 || bound >= 0 && mpcmpfixc(i.Val.U.Xval, bound) > 0 {
+		if mpcmpfixc(i.Val.U.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(i.Val.U.(*Mpint), bound) > 0 {
 			Yyerror("slice index out of bounds")
 		}
 	}
 	if Isconst(&j, CTINT) {
-		if mpcmpfixc(j.Val.U.Xval, 0) < 0 || bound >= 0 && mpcmpfixc(j.Val.U.Xval, bound) > 0 {
+		if mpcmpfixc(j.Val.U.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(j.Val.U.(*Mpint), bound) > 0 {
 			Yyerror("slice index out of bounds")
 		}
 	}
 	if Isconst(&k, CTINT) {
-		if mpcmpfixc(k.Val.U.Xval, 0) < 0 || bound >= 0 && mpcmpfixc(k.Val.U.Xval, bound) > 0 {
+		if mpcmpfixc(k.Val.U.(*Mpint), 0) < 0 || bound >= 0 && mpcmpfixc(k.Val.U.(*Mpint), bound) > 0 {
 			Yyerror("slice index out of bounds")
 		}
 	}
@@ -3202,7 +3202,7 @@ func cgen_slice(n, res *Node, wb bool) {
 	same := func(n1, n2 *Node) bool {
 		return n1.Op == OREGISTER && n2.Op == OREGISTER && n1.Reg == n2.Reg ||
 			n1.Op == ONAME && n2.Op == ONAME && n1.Orig == n2.Orig && n1.Type == n2.Type && n1.Xoffset == n2.Xoffset ||
-			n1.Op == OLITERAL && n2.Op == OLITERAL && Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval) == 0
+			n1.Op == OLITERAL && n2.Op == OLITERAL && Mpcmpfixfix(n1.Val.U.(*Mpint), n2.Val.U.(*Mpint)) == 0
 	}
 
 	// obvious reports whether n1 <= n2 is obviously true,
@@ -3221,7 +3221,7 @@ func cgen_slice(n, res *Node, wb bool) {
 			return true // len(x) <= cap(x) always true
 		}
 		if Isconst(n1, CTINT) && Isconst(n2, CTINT) {
-			if Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval) <= 0 {
+			if Mpcmpfixfix(n1.Val.U.(*Mpint), n2.Val.U.(*Mpint)) <= 0 {
 				return true // n1, n2 constants such that n1 <= n2
 			}
 			Yyerror("slice index out of bounds")
@@ -3312,9 +3312,9 @@ func cgen_slice(n, res *Node, wb bool) {
 			switch j.Op {
 			case OLITERAL:
 				if Isconst(&i, CTINT) {
-					Nodconst(&j, indexRegType, Mpgetfix(j.Val.U.Xval)-Mpgetfix(i.Val.U.Xval))
+					Nodconst(&j, indexRegType, Mpgetfix(j.Val.U.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint)))
 					if Debug_slice > 0 {
-						Warn("slice: result len == %d", Mpgetfix(j.Val.U.Xval))
+						Warn("slice: result len == %d", Mpgetfix(j.Val.U.(*Mpint)))
 					}
 					break
 				}
@@ -3329,7 +3329,7 @@ func cgen_slice(n, res *Node, wb bool) {
 				fallthrough
 			case OREGISTER:
 				if i.Op == OLITERAL {
-					v := Mpgetfix(i.Val.U.Xval)
+					v := Mpgetfix(i.Val.U.(*Mpint))
 					if v != 0 {
 						ginscon(Thearch.Optoas(OSUB, indexRegType), v, &j)
 					}
@@ -3372,9 +3372,9 @@ func cgen_slice(n, res *Node, wb bool) {
 			switch k.Op {
 			case OLITERAL:
 				if Isconst(&i, CTINT) {
-					Nodconst(&k, indexRegType, Mpgetfix(k.Val.U.Xval)-Mpgetfix(i.Val.U.Xval))
+					Nodconst(&k, indexRegType, Mpgetfix(k.Val.U.(*Mpint))-Mpgetfix(i.Val.U.(*Mpint)))
 					if Debug_slice > 0 {
-						Warn("slice: result cap == %d", Mpgetfix(k.Val.U.Xval))
+						Warn("slice: result cap == %d", Mpgetfix(k.Val.U.(*Mpint)))
 					}
 					break
 				}
@@ -3395,7 +3395,7 @@ func cgen_slice(n, res *Node, wb bool) {
 						Warn("slice: result cap == 0")
 					}
 				} else if i.Op == OLITERAL {
-					v := Mpgetfix(i.Val.U.Xval)
+					v := Mpgetfix(i.Val.U.(*Mpint))
 					if v != 0 {
 						ginscon(Thearch.Optoas(OSUB, indexRegType), v, &k)
 					}
@@ -3478,7 +3478,7 @@ func cgen_slice(n, res *Node, wb bool) {
 				w = res.Type.Type.Width // res is []T, elem size is T.width
 			}
 			if Isconst(&i, CTINT) {
-				ginscon(Thearch.Optoas(OADD, xbase.Type), Mpgetfix(i.Val.U.Xval)*w, &xbase)
+				ginscon(Thearch.Optoas(OADD, xbase.Type), Mpgetfix(i.Val.U.(*Mpint))*w, &xbase)
 			} else if Thearch.AddIndex != nil && Thearch.AddIndex(&i, w, &xbase) {
 				// done by back end
 			} else if w == 1 {
diff --git a/src/cmd/internal/gc/const.go b/src/cmd/internal/gc/const.go
index 69f2e5c904..986e2c3337 100644
--- a/src/cmd/internal/gc/const.go
+++ b/src/cmd/internal/gc/const.go
@@ -16,7 +16,7 @@ func (n *Node) Int() int64 {
 	if !Isconst(n, CTINT) {
 		Fatal("Int(%v)", n)
 	}
-	return Mpgetfix(n.Val.U.Xval)
+	return Mpgetfix(n.Val.U.(*Mpint))
 }
 
 // SetInt sets n's value to i.
@@ -25,7 +25,7 @@ func (n *Node) SetInt(i int64) {
 	if !Isconst(n, CTINT) {
 		Fatal("SetInt(%v)", n)
 	}
-	Mpmovecfix(n.Val.U.Xval, i)
+	Mpmovecfix(n.Val.U.(*Mpint), i)
 }
 
 // SetBigInt sets n's value to x.
@@ -34,7 +34,7 @@ func (n *Node) SetBigInt(x *big.Int) {
 	if !Isconst(n, CTINT) {
 		Fatal("SetBigInt(%v)", n)
 	}
-	n.Val.U.Xval.Val.Set(x)
+	n.Val.U.(*Mpint).Val.Set(x)
 }
 
 // Bool returns n as an bool.
@@ -43,7 +43,7 @@ func (n *Node) Bool() bool {
 	if !Isconst(n, CTBOOL) {
 		Fatal("Int(%v)", n)
 	}
-	return n.Val.U.Bval
+	return n.Val.U.(bool)
 }
 
 /*
@@ -57,7 +57,7 @@ func truncfltlit(oldv *Mpflt, t *Type) *Mpflt {
 
 	var v Val
 	v.Ctype = CTFLT
-	v.U.Fval = oldv
+	v.U = oldv
 	overflow(v, t)
 
 	fv := newMpflt()
@@ -227,8 +227,8 @@ func convlit1(np **Node, t *Type, explicit bool) {
 		// if it is an unsafe.Pointer
 		case TUINTPTR:
 			if n.Type.Etype == TUNSAFEPTR {
-				n.Val.U.Xval = new(Mpint)
-				Mpmovecfix(n.Val.U.Xval, 0)
+				n.Val.U = new(Mpint)
+				Mpmovecfix(n.Val.U.(*Mpint), 0)
 				n.Val.Ctype = CTINT
 			} else {
 				goto bad
@@ -269,7 +269,7 @@ func convlit1(np **Node, t *Type, explicit bool) {
 
 				// flowthrough
 			case CTFLT:
-				n.Val.U.Fval = truncfltlit(n.Val.U.Fval, t)
+				n.Val.U = truncfltlit(n.Val.U.(*Mpflt), t)
 			}
 		} else if Iscomplex[et] {
 			switch ct {
@@ -310,19 +310,19 @@ func copyval(v Val) Val {
 	switch v.Ctype {
 	case CTINT, CTRUNE:
 		i := new(Mpint)
-		mpmovefixfix(i, v.U.Xval)
-		v.U.Xval = i
+		mpmovefixfix(i, v.U.(*Mpint))
+		v.U = i
 
 	case CTFLT:
 		f := newMpflt()
-		mpmovefltflt(f, v.U.Fval)
-		v.U.Fval = f
+		mpmovefltflt(f, v.U.(*Mpflt))
+		v.U = f
 
 	case CTCPLX:
 		c := new(Mpcplx)
-		mpmovefltflt(&c.Real, &v.U.Cval.Real)
-		mpmovefltflt(&c.Imag, &v.U.Cval.Imag)
-		v.U.Cval = c
+		mpmovefltflt(&c.Real, &v.U.(*Mpcplx).Real)
+		mpmovefltflt(&c.Imag, &v.U.(*Mpcplx).Imag)
+		v.U = c
 	}
 
 	return v
@@ -332,17 +332,17 @@ func tocplx(v Val) Val {
 	switch v.Ctype {
 	case CTINT, CTRUNE:
 		c := new(Mpcplx)
-		Mpmovefixflt(&c.Real, v.U.Xval)
+		Mpmovefixflt(&c.Real, v.U.(*Mpint))
 		Mpmovecflt(&c.Imag, 0.0)
 		v.Ctype = CTCPLX
-		v.U.Cval = c
+		v.U = c
 
 	case CTFLT:
 		c := new(Mpcplx)
-		mpmovefltflt(&c.Real, v.U.Fval)
+		mpmovefltflt(&c.Real, v.U.(*Mpflt))
 		Mpmovecflt(&c.Imag, 0.0)
 		v.Ctype = CTCPLX
-		v.U.Cval = c
+		v.U = c
 	}
 
 	return v
@@ -352,18 +352,18 @@ func toflt(v Val) Val {
 	switch v.Ctype {
 	case CTINT, CTRUNE:
 		f := newMpflt()
-		Mpmovefixflt(f, v.U.Xval)
+		Mpmovefixflt(f, v.U.(*Mpint))
 		v.Ctype = CTFLT
-		v.U.Fval = f
+		v.U = f
 
 	case CTCPLX:
 		f := newMpflt()
-		mpmovefltflt(f, &v.U.Cval.Real)
-		if mpcmpfltc(&v.U.Cval.Imag, 0) != 0 {
-			Yyerror("constant %v%vi truncated to real", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp|obj.FmtSign))
+		mpmovefltflt(f, &v.U.(*Mpcplx).Real)
+		if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) != 0 {
+			Yyerror("constant %v%vi truncated to real", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp|obj.FmtSign))
 		}
 		v.Ctype = CTFLT
-		v.U.Fval = f
+		v.U = f
 	}
 
 	return v
@@ -376,22 +376,22 @@ func toint(v Val) Val {
 
 	case CTFLT:
 		i := new(Mpint)
-		if mpmovefltfix(i, v.U.Fval) < 0 {
-			Yyerror("constant %v truncated to integer", Fconv(v.U.Fval, obj.FmtSharp))
+		if mpmovefltfix(i, v.U.(*Mpflt)) < 0 {
+			Yyerror("constant %v truncated to integer", Fconv(v.U.(*Mpflt), obj.FmtSharp))
 		}
 		v.Ctype = CTINT
-		v.U.Xval = i
+		v.U = i
 
 	case CTCPLX:
 		i := new(Mpint)
-		if mpmovefltfix(i, &v.U.Cval.Real) < 0 {
-			Yyerror("constant %v%vi truncated to integer", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp|obj.FmtSign))
+		if mpmovefltfix(i, &v.U.(*Mpcplx).Real) < 0 {
+			Yyerror("constant %v%vi truncated to integer", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp|obj.FmtSign))
 		}
-		if mpcmpfltc(&v.U.Cval.Imag, 0) != 0 {
-			Yyerror("constant %v%vi truncated to real", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp|obj.FmtSign))
+		if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) != 0 {
+			Yyerror("constant %v%vi truncated to real", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp|obj.FmtSign))
 		}
 		v.Ctype = CTINT
-		v.U.Xval = i
+		v.U = i
 	}
 
 	return v
@@ -403,7 +403,7 @@ func doesoverflow(v Val, t *Type) bool {
 		if !Isint[t.Etype] {
 			Fatal("overflow: %v integer constant", t)
 		}
-		if Mpcmpfixfix(v.U.Xval, Minintval[t.Etype]) < 0 || Mpcmpfixfix(v.U.Xval, Maxintval[t.Etype]) > 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), Minintval[t.Etype]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[t.Etype]) > 0 {
 			return true
 		}
 
@@ -411,7 +411,7 @@ func doesoverflow(v Val, t *Type) bool {
 		if !Isfloat[t.Etype] {
 			Fatal("overflow: %v floating-point constant", t)
 		}
-		if mpcmpfltflt(v.U.Fval, minfltval[t.Etype]) <= 0 || mpcmpfltflt(v.U.Fval, maxfltval[t.Etype]) >= 0 {
+		if mpcmpfltflt(v.U.(*Mpflt), minfltval[t.Etype]) <= 0 || mpcmpfltflt(v.U.(*Mpflt), maxfltval[t.Etype]) >= 0 {
 			return true
 		}
 
@@ -419,7 +419,7 @@ func doesoverflow(v Val, t *Type) bool {
 		if !Iscomplex[t.Etype] {
 			Fatal("overflow: %v complex constant", t)
 		}
-		if mpcmpfltflt(&v.U.Cval.Real, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.Cval.Real, maxfltval[t.Etype]) >= 0 || mpcmpfltflt(&v.U.Cval.Imag, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.Cval.Imag, maxfltval[t.Etype]) >= 0 {
+		if mpcmpfltflt(&v.U.(*Mpcplx).Real, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.(*Mpcplx).Real, maxfltval[t.Etype]) >= 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, minfltval[t.Etype]) <= 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, maxfltval[t.Etype]) >= 0 {
 			return true
 		}
 	}
@@ -445,26 +445,26 @@ func overflow(v Val, t *Type) {
 
 	switch v.Ctype {
 	case CTINT, CTRUNE:
-		Yyerror("constant %v overflows %v", v.U.Xval, t)
+		Yyerror("constant %v overflows %v", v.U.(*Mpint), t)
 
 	case CTFLT:
-		Yyerror("constant %v overflows %v", Fconv(v.U.Fval, obj.FmtSharp), t)
+		Yyerror("constant %v overflows %v", Fconv(v.U.(*Mpflt), obj.FmtSharp), t)
 
 	case CTCPLX:
-		Yyerror("constant %v overflows %v", Fconv(v.U.Fval, obj.FmtSharp), t)
+		Yyerror("constant %v overflows %v", Fconv(v.U.(*Mpflt), obj.FmtSharp), t)
 	}
 }
 
 func tostr(v Val) Val {
 	switch v.Ctype {
 	case CTINT, CTRUNE:
-		if Mpcmpfixfix(v.U.Xval, Minintval[TINT]) < 0 || Mpcmpfixfix(v.U.Xval, Maxintval[TINT]) > 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), Minintval[TINT]) < 0 || Mpcmpfixfix(v.U.(*Mpint), Maxintval[TINT]) > 0 {
 			Yyerror("overflow in int -> string")
 		}
-		r := uint(Mpgetfix(v.U.Xval))
+		r := uint(Mpgetfix(v.U.(*Mpint)))
 		v = Val{}
 		v.Ctype = CTSTR
-		v.U.Sval = string(r)
+		v.U = string(r)
 
 	case CTFLT:
 		Yyerror("no float -> string")
@@ -473,7 +473,7 @@ func tostr(v Val) Val {
 	case CTNIL:
 		v = Val{}
 		v.Ctype = CTSTR
-		v.U.Sval = ""
+		v.U = ""
 	}
 
 	return v
@@ -562,7 +562,7 @@ func evconst(n *Node) {
 				l2 = l1
 				for l2 != nil && Isconst(l2.N, CTSTR) {
 					nr = l2.N
-					strs = append(strs, nr.Val.U.Sval)
+					strs = append(strs, nr.Val.U.(string))
 					l2 = l2.Next
 				}
 
@@ -570,7 +570,7 @@ func evconst(n *Node) {
 				*nl = *l1.N
 				nl.Orig = nl
 				nl.Val.Ctype = CTSTR
-				nl.Val.U.Sval = strings.Join(strs, "")
+				nl.Val.U = strings.Join(strs, "")
 				l1.N = nl
 				l1.Next = l2
 			}
@@ -650,7 +650,7 @@ func evconst(n *Node) {
 
 		case OMINUS<<16 | CTINT,
 			OMINUS<<16 | CTRUNE:
-			mpnegfix(v.U.Xval)
+			mpnegfix(v.U.(*Mpint))
 
 		case OCOM<<16 | CTINT,
 			OCOM<<16 | CTRUNE:
@@ -677,23 +677,23 @@ func evconst(n *Node) {
 				mpmovefixfix(&b, Maxintval[et])
 			}
 
-			mpxorfixfix(v.U.Xval, &b)
+			mpxorfixfix(v.U.(*Mpint), &b)
 
 		case OPLUS<<16 | CTFLT:
 			break
 
 		case OMINUS<<16 | CTFLT:
-			mpnegflt(v.U.Fval)
+			mpnegflt(v.U.(*Mpflt))
 
 		case OPLUS<<16 | CTCPLX:
 			break
 
 		case OMINUS<<16 | CTCPLX:
-			mpnegflt(&v.U.Cval.Real)
-			mpnegflt(&v.U.Cval.Imag)
+			mpnegflt(&v.U.(*Mpcplx).Real)
+			mpnegflt(&v.U.(*Mpcplx).Imag)
 
 		case ONOT<<16 | CTBOOL:
-			if !v.U.Bval {
+			if !v.U.(bool) {
 				goto settrue
 			}
 			goto setfalse
@@ -797,77 +797,77 @@ func evconst(n *Node) {
 
 	case OADD<<16 | CTINT,
 		OADD<<16 | CTRUNE:
-		mpaddfixfix(v.U.Xval, rv.U.Xval, 0)
+		mpaddfixfix(v.U.(*Mpint), rv.U.(*Mpint), 0)
 
 	case OSUB<<16 | CTINT,
 		OSUB<<16 | CTRUNE:
-		mpsubfixfix(v.U.Xval, rv.U.Xval)
+		mpsubfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OMUL<<16 | CTINT,
 		OMUL<<16 | CTRUNE:
-		mpmulfixfix(v.U.Xval, rv.U.Xval)
+		mpmulfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case ODIV<<16 | CTINT,
 		ODIV<<16 | CTRUNE:
-		if mpcmpfixc(rv.U.Xval, 0) == 0 {
+		if mpcmpfixc(rv.U.(*Mpint), 0) == 0 {
 			Yyerror("division by zero")
-			mpsetovf(v.U.Xval)
+			mpsetovf(v.U.(*Mpint))
 			break
 		}
 
-		mpdivfixfix(v.U.Xval, rv.U.Xval)
+		mpdivfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OMOD<<16 | CTINT,
 		OMOD<<16 | CTRUNE:
-		if mpcmpfixc(rv.U.Xval, 0) == 0 {
+		if mpcmpfixc(rv.U.(*Mpint), 0) == 0 {
 			Yyerror("division by zero")
-			mpsetovf(v.U.Xval)
+			mpsetovf(v.U.(*Mpint))
 			break
 		}
 
-		mpmodfixfix(v.U.Xval, rv.U.Xval)
+		mpmodfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OLSH<<16 | CTINT,
 		OLSH<<16 | CTRUNE:
-		mplshfixfix(v.U.Xval, rv.U.Xval)
+		mplshfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case ORSH<<16 | CTINT,
 		ORSH<<16 | CTRUNE:
-		mprshfixfix(v.U.Xval, rv.U.Xval)
+		mprshfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OOR<<16 | CTINT,
 		OOR<<16 | CTRUNE:
-		mporfixfix(v.U.Xval, rv.U.Xval)
+		mporfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OAND<<16 | CTINT,
 		OAND<<16 | CTRUNE:
-		mpandfixfix(v.U.Xval, rv.U.Xval)
+		mpandfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OANDNOT<<16 | CTINT,
 		OANDNOT<<16 | CTRUNE:
-		mpandnotfixfix(v.U.Xval, rv.U.Xval)
+		mpandnotfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OXOR<<16 | CTINT,
 		OXOR<<16 | CTRUNE:
-		mpxorfixfix(v.U.Xval, rv.U.Xval)
+		mpxorfixfix(v.U.(*Mpint), rv.U.(*Mpint))
 
 	case OADD<<16 | CTFLT:
-		mpaddfltflt(v.U.Fval, rv.U.Fval)
+		mpaddfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
 
 	case OSUB<<16 | CTFLT:
-		mpsubfltflt(v.U.Fval, rv.U.Fval)
+		mpsubfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
 
 	case OMUL<<16 | CTFLT:
-		mpmulfltflt(v.U.Fval, rv.U.Fval)
+		mpmulfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
 
 	case ODIV<<16 | CTFLT:
-		if mpcmpfltc(rv.U.Fval, 0) == 0 {
+		if mpcmpfltc(rv.U.(*Mpflt), 0) == 0 {
 			Yyerror("division by zero")
-			Mpmovecflt(v.U.Fval, 1.0)
+			Mpmovecflt(v.U.(*Mpflt), 1.0)
 			break
 		}
 
-		mpdivfltflt(v.U.Fval, rv.U.Fval)
+		mpdivfltflt(v.U.(*Mpflt), rv.U.(*Mpflt))
 
 		// The default case above would print 'ideal % ideal',
 	// which is not quite an ideal error.
@@ -880,25 +880,25 @@ func evconst(n *Node) {
 		return
 
 	case OADD<<16 | CTCPLX:
-		mpaddfltflt(&v.U.Cval.Real, &rv.U.Cval.Real)
-		mpaddfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag)
+		mpaddfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real)
+		mpaddfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag)
 
 	case OSUB<<16 | CTCPLX:
-		mpsubfltflt(&v.U.Cval.Real, &rv.U.Cval.Real)
-		mpsubfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag)
+		mpsubfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real)
+		mpsubfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag)
 
 	case OMUL<<16 | CTCPLX:
-		cmplxmpy(v.U.Cval, rv.U.Cval)
+		cmplxmpy(v.U.(*Mpcplx), rv.U.(*Mpcplx))
 
 	case ODIV<<16 | CTCPLX:
-		if mpcmpfltc(&rv.U.Cval.Real, 0) == 0 && mpcmpfltc(&rv.U.Cval.Imag, 0) == 0 {
+		if mpcmpfltc(&rv.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&rv.U.(*Mpcplx).Imag, 0) == 0 {
 			Yyerror("complex division by zero")
-			Mpmovecflt(&rv.U.Cval.Real, 1.0)
-			Mpmovecflt(&rv.U.Cval.Imag, 0.0)
+			Mpmovecflt(&rv.U.(*Mpcplx).Real, 1.0)
+			Mpmovecflt(&rv.U.(*Mpcplx).Imag, 0.0)
 			break
 		}
 
-		cmplxdiv(v.U.Cval, rv.U.Cval)
+		cmplxdiv(v.U.(*Mpcplx), rv.U.(*Mpcplx))
 
 	case OEQ<<16 | CTNIL:
 		goto settrue
@@ -908,90 +908,90 @@ func evconst(n *Node) {
 
 	case OEQ<<16 | CTINT,
 		OEQ<<16 | CTRUNE:
-		if Mpcmpfixfix(v.U.Xval, rv.U.Xval) == 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) == 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case ONE<<16 | CTINT,
 		ONE<<16 | CTRUNE:
-		if Mpcmpfixfix(v.U.Xval, rv.U.Xval) != 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) != 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OLT<<16 | CTINT,
 		OLT<<16 | CTRUNE:
-		if Mpcmpfixfix(v.U.Xval, rv.U.Xval) < 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) < 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OLE<<16 | CTINT,
 		OLE<<16 | CTRUNE:
-		if Mpcmpfixfix(v.U.Xval, rv.U.Xval) <= 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) <= 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OGE<<16 | CTINT,
 		OGE<<16 | CTRUNE:
-		if Mpcmpfixfix(v.U.Xval, rv.U.Xval) >= 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) >= 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OGT<<16 | CTINT,
 		OGT<<16 | CTRUNE:
-		if Mpcmpfixfix(v.U.Xval, rv.U.Xval) > 0 {
+		if Mpcmpfixfix(v.U.(*Mpint), rv.U.(*Mpint)) > 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OEQ<<16 | CTFLT:
-		if mpcmpfltflt(v.U.Fval, rv.U.Fval) == 0 {
+		if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) == 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case ONE<<16 | CTFLT:
-		if mpcmpfltflt(v.U.Fval, rv.U.Fval) != 0 {
+		if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) != 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OLT<<16 | CTFLT:
-		if mpcmpfltflt(v.U.Fval, rv.U.Fval) < 0 {
+		if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) < 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OLE<<16 | CTFLT:
-		if mpcmpfltflt(v.U.Fval, rv.U.Fval) <= 0 {
+		if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) <= 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OGE<<16 | CTFLT:
-		if mpcmpfltflt(v.U.Fval, rv.U.Fval) >= 0 {
+		if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) >= 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OGT<<16 | CTFLT:
-		if mpcmpfltflt(v.U.Fval, rv.U.Fval) > 0 {
+		if mpcmpfltflt(v.U.(*Mpflt), rv.U.(*Mpflt)) > 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case OEQ<<16 | CTCPLX:
-		if mpcmpfltflt(&v.U.Cval.Real, &rv.U.Cval.Real) == 0 && mpcmpfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag) == 0 {
+		if mpcmpfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) == 0 && mpcmpfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) == 0 {
 			goto settrue
 		}
 		goto setfalse
 
 	case ONE<<16 | CTCPLX:
-		if mpcmpfltflt(&v.U.Cval.Real, &rv.U.Cval.Real) != 0 || mpcmpfltflt(&v.U.Cval.Imag, &rv.U.Cval.Imag) != 0 {
+		if mpcmpfltflt(&v.U.(*Mpcplx).Real, &rv.U.(*Mpcplx).Real) != 0 || mpcmpfltflt(&v.U.(*Mpcplx).Imag, &rv.U.(*Mpcplx).Imag) != 0 {
 			goto settrue
 		}
 		goto setfalse
@@ -1033,25 +1033,25 @@ func evconst(n *Node) {
 		goto setfalse
 
 	case OOROR<<16 | CTBOOL:
-		if v.U.Bval || rv.U.Bval {
+		if v.U.(bool) || rv.U.(bool) {
 			goto settrue
 		}
 		goto setfalse
 
 	case OANDAND<<16 | CTBOOL:
-		if v.U.Bval && rv.U.Bval {
+		if v.U.(bool) && rv.U.(bool) {
 			goto settrue
 		}
 		goto setfalse
 
 	case OEQ<<16 | CTBOOL:
-		if v.U.Bval == rv.U.Bval {
+		if v.U.(bool) == rv.U.(bool) {
 			goto settrue
 		}
 		goto setfalse
 
 	case ONE<<16 | CTBOOL:
-		if v.U.Bval != rv.U.Bval {
+		if v.U.(bool) != rv.U.(bool) {
 			goto settrue
 		}
 		goto setfalse
@@ -1076,7 +1076,7 @@ ret:
 
 	// truncate precision for non-ideal float.
 	if v.Ctype == CTFLT && n.Type.Etype != TIDEAL {
-		n.Val.U.Fval = truncfltlit(v.U.Fval, n.Type)
+		n.Val.U = truncfltlit(v.U.(*Mpflt), n.Type)
 	}
 	return
 
@@ -1131,15 +1131,15 @@ func nodcplxlit(r Val, i Val) *Node {
 	c := new(Mpcplx)
 	n := Nod(OLITERAL, nil, nil)
 	n.Type = Types[TIDEAL]
-	n.Val.U.Cval = c
+	n.Val.U = c
 	n.Val.Ctype = CTCPLX
 
 	if r.Ctype != CTFLT || i.Ctype != CTFLT {
 		Fatal("nodcplxlit ctype %d/%d", r.Ctype, i.Ctype)
 	}
 
-	mpmovefltflt(&c.Real, r.U.Fval)
-	mpmovefltflt(&c.Imag, i.U.Fval)
+	mpmovefltflt(&c.Real, r.U.(*Mpflt))
+	mpmovefltflt(&c.Imag, i.U.(*Mpflt))
 	return n
 }
 
@@ -1354,7 +1354,7 @@ func defaultlit2(lp **Node, rp **Node, force int) {
 }
 
 func cmpslit(l, r *Node) int {
-	return stringsCompare(l.Val.U.Sval, r.Val.U.Sval)
+	return stringsCompare(l.Val.U.(string), r.Val.U.(string))
 }
 
 func Smallintconst(n *Node) bool {
@@ -1371,7 +1371,7 @@ func Smallintconst(n *Node) bool {
 			return true
 
 		case TIDEAL, TINT64, TUINT64, TPTR64:
-			if Mpcmpfixfix(n.Val.U.Xval, Minintval[TINT32]) < 0 || Mpcmpfixfix(n.Val.U.Xval, Maxintval[TINT32]) > 0 {
+			if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 {
 				break
 			}
 			return true
@@ -1394,10 +1394,10 @@ func nonnegconst(n *Node) int {
 			TINT64,
 			TUINT64,
 			TIDEAL:
-			if Mpcmpfixfix(n.Val.U.Xval, Minintval[TUINT32]) < 0 || Mpcmpfixfix(n.Val.U.Xval, Maxintval[TINT32]) > 0 {
+			if Mpcmpfixfix(n.Val.U.(*Mpint), Minintval[TUINT32]) < 0 || Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT32]) > 0 {
 				break
 			}
-			return int(Mpgetfix(n.Val.U.Xval))
+			return int(Mpgetfix(n.Val.U.(*Mpint)))
 		}
 	}
 
@@ -1448,24 +1448,24 @@ func (n *Node) Convconst(con *Node, t *Type) {
 
 	if Isint[tt] {
 		con.Val.Ctype = CTINT
-		con.Val.U.Xval = new(Mpint)
+		con.Val.U = new(Mpint)
 		var i int64
 		switch n.Val.Ctype {
 		default:
 			Fatal("convconst ctype=%d %v", n.Val.Ctype, Tconv(t, obj.FmtLong))
 
 		case CTINT, CTRUNE:
-			i = Mpgetfix(n.Val.U.Xval)
+			i = Mpgetfix(n.Val.U.(*Mpint))
 
 		case CTBOOL:
-			i = int64(obj.Bool2int(n.Val.U.Bval))
+			i = int64(obj.Bool2int(n.Val.U.(bool)))
 
 		case CTNIL:
 			i = 0
 		}
 
 		i = iconv(i, tt)
-		Mpmovecfix(con.Val.U.Xval, i)
+		Mpmovecfix(con.Val.U.(*Mpint), i)
 		return
 	}
 
@@ -1475,7 +1475,7 @@ func (n *Node) Convconst(con *Node, t *Type) {
 			Fatal("convconst ctype=%d %v", con.Val.Ctype, t)
 		}
 		if tt == TFLOAT32 {
-			con.Val.U.Fval = truncfltlit(con.Val.U.Fval, t)
+			con.Val.U = truncfltlit(con.Val.U.(*Mpflt), t)
 		}
 		return
 	}
@@ -1483,8 +1483,8 @@ func (n *Node) Convconst(con *Node, t *Type) {
 	if Iscomplex[tt] {
 		con.Val = tocplx(con.Val)
 		if tt == TCOMPLEX64 {
-			con.Val.U.Cval.Real = *truncfltlit(&con.Val.U.Cval.Real, Types[TFLOAT32])
-			con.Val.U.Cval.Imag = *truncfltlit(&con.Val.U.Cval.Imag, Types[TFLOAT32])
+			con.Val.U.(*Mpcplx).Real = *truncfltlit(&con.Val.U.(*Mpcplx).Real, Types[TFLOAT32])
+			con.Val.U.(*Mpcplx).Imag = *truncfltlit(&con.Val.U.(*Mpcplx).Imag, Types[TFLOAT32])
 		}
 		return
 	}
diff --git a/src/cmd/internal/gc/cplx.go b/src/cmd/internal/gc/cplx.go
index cf48c922d7..56a4892636 100644
--- a/src/cmd/internal/gc/cplx.go
+++ b/src/cmd/internal/gc/cplx.go
@@ -89,8 +89,8 @@ func subnode(nr *Node, ni *Node, nc *Node) {
 	t := Types[tc]
 
 	if nc.Op == OLITERAL {
-		nodfconst(nr, t, &nc.Val.U.Cval.Real)
-		nodfconst(ni, t, &nc.Val.U.Cval.Imag)
+		nodfconst(nr, t, &nc.Val.U.(*Mpcplx).Real)
+		nodfconst(ni, t, &nc.Val.U.(*Mpcplx).Imag)
 		return
 	}
 
@@ -226,7 +226,7 @@ func nodfconst(n *Node, t *Type, fval *Mpflt) {
 	n.Op = OLITERAL
 	n.Addable = true
 	ullmancalc(n)
-	n.Val.U.Fval = fval
+	n.Val.U = fval
 	n.Val.Ctype = CTFLT
 	n.Type = t
 
diff --git a/src/cmd/internal/gc/dcl.go b/src/cmd/internal/gc/dcl.go
index 627556eeef..85a33bec3f 100644
--- a/src/cmd/internal/gc/dcl.go
+++ b/src/cmd/internal/gc/dcl.go
@@ -830,7 +830,7 @@ func structfield(n *Node) *Type {
 	switch n.Val.Ctype {
 	case CTSTR:
 		f.Note = new(string)
-		*f.Note = n.Val.U.Sval
+		*f.Note = n.Val.U.(string)
 
 	default:
 		Yyerror("field annotation must be string")
diff --git a/src/cmd/internal/gc/fmt.go b/src/cmd/internal/gc/fmt.go
index 4e3045a929..9d8482bf76 100644
--- a/src/cmd/internal/gc/fmt.go
+++ b/src/cmd/internal/gc/fmt.go
@@ -302,12 +302,12 @@ func Vconv(v *Val, flag int) string {
 	switch v.Ctype {
 	case CTINT:
 		if (flag&obj.FmtSharp != 0) || fmtmode == FExp {
-			return Bconv(v.U.Xval, obj.FmtSharp)
+			return Bconv(v.U.(*Mpint), obj.FmtSharp)
 		}
-		return Bconv(v.U.Xval, 0)
+		return Bconv(v.U.(*Mpint), 0)
 
 	case CTRUNE:
-		x := Mpgetfix(v.U.Xval)
+		x := Mpgetfix(v.U.(*Mpint))
 		if ' ' <= x && x < 0x80 && x != '\\' && x != '\'' {
 			return fmt.Sprintf("'%c'", int(x))
 		}
@@ -317,34 +317,34 @@ func Vconv(v *Val, flag int) string {
 		if 0 <= x && x <= utf8.MaxRune {
 			return fmt.Sprintf("'\\U%08x'", uint64(x))
 		}
-		return fmt.Sprintf("('\\x00' + %v)", v.U.Xval)
+		return fmt.Sprintf("('\\x00' + %v)", v.U.(*Mpint))
 
 	case CTFLT:
 		if (flag&obj.FmtSharp != 0) || fmtmode == FExp {
-			return Fconv(v.U.Fval, 0)
+			return Fconv(v.U.(*Mpflt), 0)
 		}
-		return Fconv(v.U.Fval, obj.FmtSharp)
+		return Fconv(v.U.(*Mpflt), obj.FmtSharp)
 
 	case CTCPLX:
 		if (flag&obj.FmtSharp != 0) || fmtmode == FExp {
-			return fmt.Sprintf("(%v+%vi)", &v.U.Cval.Real, &v.U.Cval.Imag)
+			return fmt.Sprintf("(%v+%vi)", &v.U.(*Mpcplx).Real, &v.U.(*Mpcplx).Imag)
 		}
-		if mpcmpfltc(&v.U.Cval.Real, 0) == 0 {
-			return fmt.Sprintf("%vi", Fconv(&v.U.Cval.Imag, obj.FmtSharp))
+		if mpcmpfltc(&v.U.(*Mpcplx).Real, 0) == 0 {
+			return fmt.Sprintf("%vi", Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp))
 		}
-		if mpcmpfltc(&v.U.Cval.Imag, 0) == 0 {
-			return Fconv(&v.U.Cval.Real, obj.FmtSharp)
+		if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) == 0 {
+			return Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp)
 		}
-		if mpcmpfltc(&v.U.Cval.Imag, 0) < 0 {
-			return fmt.Sprintf("(%v%vi)", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp))
+		if mpcmpfltc(&v.U.(*Mpcplx).Imag, 0) < 0 {
+			return fmt.Sprintf("(%v%vi)", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp))
 		}
-		return fmt.Sprintf("(%v+%vi)", Fconv(&v.U.Cval.Real, obj.FmtSharp), Fconv(&v.U.Cval.Imag, obj.FmtSharp))
+		return fmt.Sprintf("(%v+%vi)", Fconv(&v.U.(*Mpcplx).Real, obj.FmtSharp), Fconv(&v.U.(*Mpcplx).Imag, obj.FmtSharp))
 
 	case CTSTR:
-		return strconv.Quote(v.U.Sval)
+		return strconv.Quote(v.U.(string))
 
 	case CTBOOL:
-		if v.U.Bval {
+		if v.U.(bool) {
 			return "true"
 		}
 		return "false"
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go
index 8f6a43c121..cd0e650ca9 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/internal/gc/gen.go
@@ -333,22 +333,22 @@ func Clearslim(n *Node) {
 
 	switch Simtype[n.Type.Etype] {
 	case TCOMPLEX64, TCOMPLEX128:
-		z.Val.U.Cval = new(Mpcplx)
-		Mpmovecflt(&z.Val.U.Cval.Real, 0.0)
-		Mpmovecflt(&z.Val.U.Cval.Imag, 0.0)
+		z.Val.U = new(Mpcplx)
+		Mpmovecflt(&z.Val.U.(*Mpcplx).Real, 0.0)
+		Mpmovecflt(&z.Val.U.(*Mpcplx).Imag, 0.0)
 
 	case TFLOAT32, TFLOAT64:
 		var zero Mpflt
 		Mpmovecflt(&zero, 0.0)
 		z.Val.Ctype = CTFLT
-		z.Val.U.Fval = &zero
+		z.Val.U = &zero
 
 	case TPTR32, TPTR64, TCHAN, TMAP:
 		z.Val.Ctype = CTNIL
 
 	case TBOOL:
 		z.Val.Ctype = CTBOOL
-		z.Val.U.Bval = false
+		z.Val.U = false
 
 	case TINT8,
 		TINT16,
@@ -359,8 +359,8 @@ func Clearslim(n *Node) {
 		TUINT32,
 		TUINT64:
 		z.Val.Ctype = CTINT
-		z.Val.U.Xval = new(Mpint)
-		Mpmovecfix(z.Val.U.Xval, 0)
+		z.Val.U = new(Mpint)
+		Mpmovecfix(z.Val.U.(*Mpint), 0)
 
 	default:
 		Fatal("clearslim called on type %v", n.Type)
@@ -1122,7 +1122,7 @@ func componentgen_wb(nr, nl *Node, wb bool) bool {
 		nodl.Type = Ptrto(Types[TUINT8])
 		Regalloc(&nodr, Types[Tptr], nil)
 		p := Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), nil, &nodr)
-		Datastring(nr.Val.U.Sval, &p.From)
+		Datastring(nr.Val.U.(string), &p.From)
 		p.From.Type = obj.TYPE_ADDR
 		Thearch.Gmove(&nodr, &nodl)
 		Regfree(&nodr)
@@ -1130,7 +1130,7 @@ func componentgen_wb(nr, nl *Node, wb bool) bool {
 		// length
 		nodl.Type = Types[Simtype[TUINT]]
 		nodl.Xoffset += int64(Array_nel) - int64(Array_array)
-		Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.Sval)))
+		Nodconst(&nodr, nodl.Type, int64(len(nr.Val.U.(string))))
 		Thearch.Gmove(&nodr, &nodl)
 		return true
 	}
diff --git a/src/cmd/internal/gc/go.go b/src/cmd/internal/gc/go.go
index 404dcbb4ff..31692bdf00 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/internal/gc/go.go
@@ -83,13 +83,13 @@ type Mpcplx struct {
 
 type Val struct {
 	Ctype int16
-	U     struct {
-		Bval bool    // bool value CTBOOL
-		Xval *Mpint  // int CTINT, rune CTRUNE
-		Fval *Mpflt  // float CTFLT
-		Cval *Mpcplx // float CTCPLX
-		Sval string  // string CTSTR
-	}
+	// U contains one of:
+	// bool     bool when Ctype == CTBOOL
+	// *Mpint   int when Ctype == CTINT, rune when Ctype == CTRUNE
+	// *Mpflt   float when Ctype == CTFLT
+	// *Mpcplx  pair of floats when Ctype == CTCPLX
+	// string   string when Ctype == CTSTR
+	U interface{}
 }
 
 type Pkg struct {
diff --git a/src/cmd/internal/gc/go.y b/src/cmd/internal/gc/go.y
index bbee200889..e2e4331a77 100644
--- a/src/cmd/internal/gc/go.y
+++ b/src/cmd/internal/gc/go.y
@@ -1131,13 +1131,13 @@ hidden_importsym:
 	{
 		var p *Pkg
 
-		if $2.U.Sval == "" {
+		if $2.U.(string) == "" {
 			p = importpkg;
 		} else {
-			if isbadimport($2.U.Sval) {
+			if isbadimport($2.U.(string)) {
 				errorexit();
 			}
-			p = mkpkg($2.U.Sval);
+			p = mkpkg($2.U.(string));
 		}
 		$$ = Pkglookup($4.Name, p);
 	}
@@ -1145,13 +1145,13 @@ hidden_importsym:
 	{
 		var p *Pkg
 
-		if $2.U.Sval == "" {
+		if $2.U.(string) == "" {
 			p = importpkg;
 		} else {
-			if isbadimport($2.U.Sval) {
+			if isbadimport($2.U.(string)) {
 				errorexit();
 			}
-			p = mkpkg($2.U.Sval);
+			p = mkpkg($2.U.(string));
 		}
 		$$ = Pkglookup("?", p);
 	}
@@ -1945,7 +1945,7 @@ oliteral:
 hidden_import:
 	LIMPORT LNAME LLITERAL ';'
 	{
-		importimport($2, $3.U.Sval);
+		importimport($2, $3.U.(string));
 	}
 |	LVAR hidden_pkg_importsym hidden_type ';'
 	{
@@ -2172,14 +2172,14 @@ hidden_literal:
 		$$ = nodlit($2);
 		switch($$.Val.Ctype){
 		case CTINT, CTRUNE:
-			mpnegfix($$.Val.U.Xval);
+			mpnegfix($$.Val.U.(*Mpint));
 			break;
 		case CTFLT:
-			mpnegflt($$.Val.U.Fval);
+			mpnegflt($$.Val.U.(*Mpflt));
 			break;
 		case CTCPLX:
-			mpnegflt(&$$.Val.U.Cval.Real);
-			mpnegflt(&$$.Val.U.Cval.Imag);
+			mpnegflt(&$$.Val.U.(*Mpcplx).Real);
+			mpnegflt(&$$.Val.U.(*Mpcplx).Imag);
 			break;
 		default:
 			Yyerror("bad negated constant");
@@ -2199,11 +2199,11 @@ hidden_constant:
 	{
 		if $2.Val.Ctype == CTRUNE && $4.Val.Ctype == CTINT {
 			$$ = $2;
-			mpaddfixfix($2.Val.U.Xval, $4.Val.U.Xval, 0);
+			mpaddfixfix($2.Val.U.(*Mpint), $4.Val.U.(*Mpint), 0);
 			break;
 		}
-		$4.Val.U.Cval.Real = $4.Val.U.Cval.Imag;
-		Mpmovecflt(&$4.Val.U.Cval.Imag, 0.0);
+		$4.Val.U.(*Mpcplx).Real = $4.Val.U.(*Mpcplx).Imag;
+		Mpmovecflt(&$4.Val.U.(*Mpcplx).Imag, 0.0);
 		$$ = nodcplxlit($2.Val, $4.Val);
 	}
 
diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/internal/gc/gsubr.go
index 93e4852b6f..98d6346e2a 100644
--- a/src/cmd/internal/gc/gsubr.go
+++ b/src/cmd/internal/gc/gsubr.go
@@ -412,20 +412,20 @@ func Naddr(a *obj.Addr, n *Node) {
 
 		case CTFLT:
 			a.Type = obj.TYPE_FCONST
-			a.Val = mpgetflt(n.Val.U.Fval)
+			a.Val = mpgetflt(n.Val.U.(*Mpflt))
 
 		case CTINT, CTRUNE:
 			a.Sym = nil
 			a.Type = obj.TYPE_CONST
-			a.Offset = Mpgetfix(n.Val.U.Xval)
+			a.Offset = Mpgetfix(n.Val.U.(*Mpint))
 
 		case CTSTR:
-			datagostring(n.Val.U.Sval, a)
+			datagostring(n.Val.U.(string), a)
 
 		case CTBOOL:
 			a.Sym = nil
 			a.Type = obj.TYPE_CONST
-			a.Offset = int64(obj.Bool2int(n.Val.U.Bval))
+			a.Offset = int64(obj.Bool2int(n.Val.U.(bool)))
 
 		case CTNIL:
 			a.Sym = nil
diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/internal/gc/lex.go
index 96a1a01aaa..1bf0758000 100644
--- a/src/cmd/internal/gc/lex.go
+++ b/src/cmd/internal/gc/lex.go
@@ -647,13 +647,13 @@ func importfile(f *Val, line int) {
 		return
 	}
 
-	if len(f.U.Sval) == 0 {
+	if len(f.U.(string)) == 0 {
 		Yyerror("import path is empty")
 		fakeimport()
 		return
 	}
 
-	if isbadimport(f.U.Sval) {
+	if isbadimport(f.U.(string)) {
 		fakeimport()
 		return
 	}
@@ -662,29 +662,29 @@ func importfile(f *Val, line int) {
 	// but we reserve the import path "main" to identify
 	// the main package, just as we reserve the import
 	// path "math" to identify the standard math package.
-	if f.U.Sval == "main" {
+	if f.U.(string) == "main" {
 		Yyerror("cannot import \"main\"")
 		errorexit()
 	}
 
-	if myimportpath != "" && f.U.Sval == myimportpath {
-		Yyerror("import %q while compiling that package (import cycle)", f.U.Sval)
+	if myimportpath != "" && f.U.(string) == myimportpath {
+		Yyerror("import %q while compiling that package (import cycle)", f.U.(string))
 		errorexit()
 	}
 
-	if f.U.Sval == "unsafe" {
+	if f.U.(string) == "unsafe" {
 		if safemode != 0 {
 			Yyerror("cannot import package unsafe")
 			errorexit()
 		}
 
-		importpkg = mkpkg(f.U.Sval)
+		importpkg = mkpkg(f.U.(string))
 		cannedimports("unsafe.6", unsafeimport)
 		imported_unsafe = 1
 		return
 	}
 
-	path_ := f.U.Sval
+	path_ := f.U.(string)
 	if islocalname(path_) {
 		if path_[0] == '/' {
 			Yyerror("import path cannot be absolute path")
@@ -710,7 +710,7 @@ func importfile(f *Val, line int) {
 
 	file, found := findpkg(path_)
 	if !found {
-		Yyerror("can't find import: %q", f.U.Sval)
+		Yyerror("can't find import: %q", f.U.(string))
 		errorexit()
 	}
 
@@ -735,7 +735,7 @@ func importfile(f *Val, line int) {
 	var imp *obj.Biobuf
 	imp, err = obj.Bopenr(file)
 	if err != nil {
-		Yyerror("can't open import: %q: %v", f.U.Sval, err)
+		Yyerror("can't open import: %q: %v", f.U.(string), err)
 		errorexit()
 	}
 
@@ -798,7 +798,7 @@ func importfile(f *Val, line int) {
 		return
 	}
 
-	Yyerror("no import in %q", f.U.Sval)
+	Yyerror("no import in %q", f.U.(string))
 	unimportfile()
 }
 
@@ -1066,8 +1066,8 @@ l0:
 			ungetc(int(v))
 		}
 
-		yylval.val.U.Xval = new(Mpint)
-		Mpmovecfix(yylval.val.U.Xval, v)
+		yylval.val.U = new(Mpint)
+		Mpmovecfix(yylval.val.U.(*Mpint), v)
 		yylval.val.Ctype = CTRUNE
 		if Debug['x'] != 0 {
 			fmt.Printf("lex: codepoint literal\n")
@@ -1405,11 +1405,11 @@ ncu:
 	ungetc(c)
 
 	str = lexbuf.String()
-	yylval.val.U.Xval = new(Mpint)
-	mpatofix(yylval.val.U.Xval, str)
-	if yylval.val.U.Xval.Ovf {
+	yylval.val.U = new(Mpint)
+	mpatofix(yylval.val.U.(*Mpint), str)
+	if yylval.val.U.(*Mpint).Ovf {
 		Yyerror("overflow in constant")
-		Mpmovecfix(yylval.val.U.Xval, 0)
+		Mpmovecfix(yylval.val.U.(*Mpint), 0)
 	}
 
 	yylval.val.Ctype = CTINT
@@ -1461,12 +1461,12 @@ casei:
 	cp = nil
 
 	str = lexbuf.String()
-	yylval.val.U.Cval = new(Mpcplx)
-	Mpmovecflt(&yylval.val.U.Cval.Real, 0.0)
-	mpatoflt(&yylval.val.U.Cval.Imag, str)
-	if yylval.val.U.Cval.Imag.Val.IsInf() {
+	yylval.val.U = new(Mpcplx)
+	Mpmovecflt(&yylval.val.U.(*Mpcplx).Real, 0.0)
+	mpatoflt(&yylval.val.U.(*Mpcplx).Imag, str)
+	if yylval.val.U.(*Mpcplx).Imag.Val.IsInf() {
 		Yyerror("overflow in imaginary constant")
-		Mpmovecflt(&yylval.val.U.Cval.Real, 0.0)
+		Mpmovecflt(&yylval.val.U.(*Mpcplx).Real, 0.0)
 	}
 
 	yylval.val.Ctype = CTCPLX
@@ -1481,11 +1481,11 @@ caseout:
 	ungetc(c)
 
 	str = lexbuf.String()
-	yylval.val.U.Fval = newMpflt()
-	mpatoflt(yylval.val.U.Fval, str)
-	if yylval.val.U.Fval.Val.IsInf() {
+	yylval.val.U = newMpflt()
+	mpatoflt(yylval.val.U.(*Mpflt), str)
+	if yylval.val.U.(*Mpflt).Val.IsInf() {
 		Yyerror("overflow in float constant")
-		Mpmovecflt(yylval.val.U.Fval, 0.0)
+		Mpmovecflt(yylval.val.U.(*Mpflt), 0.0)
 	}
 
 	yylval.val.Ctype = CTFLT
@@ -1496,7 +1496,7 @@ caseout:
 	return LLITERAL
 
 strlit:
-	yylval.val.U.Sval = internString(cp.Bytes())
+	yylval.val.U = internString(cp.Bytes())
 	yylval.val.Ctype = CTSTR
 	if Debug['x'] != 0 {
 		fmt.Printf("lex: string literal\n")
diff --git a/src/cmd/internal/gc/obj.go b/src/cmd/internal/gc/obj.go
index 05c5b1a811..2afd786dc1 100644
--- a/src/cmd/internal/gc/obj.go
+++ b/src/cmd/internal/gc/obj.go
@@ -382,11 +382,11 @@ func gdata(nam *Node, nr *Node, wid int) {
 	if nr.Op == OLITERAL {
 		switch nr.Val.Ctype {
 		case CTCPLX:
-			gdatacomplex(nam, nr.Val.U.Cval)
+			gdatacomplex(nam, nr.Val.U.(*Mpcplx))
 			return
 
 		case CTSTR:
-			gdatastring(nam, nr.Val.U.Sval)
+			gdatastring(nam, nr.Val.U.(string))
 			return
 		}
 	}
diff --git a/src/cmd/internal/gc/order.go b/src/cmd/internal/gc/order.go
index 06a1490be4..b3fd282a68 100644
--- a/src/cmd/internal/gc/order.go
+++ b/src/cmd/internal/gc/order.go
@@ -995,7 +995,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) {
 		haslit := false
 		for l := n.List; l != nil; l = l.Next {
 			hasbyte = hasbyte || l.N.Op == OARRAYBYTESTR
-			haslit = haslit || l.N.Op == OLITERAL && len(l.N.Val.U.Sval) != 0
+			haslit = haslit || l.N.Op == OLITERAL && len(l.N.Val.U.(string)) != 0
 		}
 
 		if haslit && hasbyte {
diff --git a/src/cmd/internal/gc/sinit.go b/src/cmd/internal/gc/sinit.go
index a9af9450ae..4fdb2e9223 100644
--- a/src/cmd/internal/gc/sinit.go
+++ b/src/cmd/internal/gc/sinit.go
@@ -437,7 +437,7 @@ func staticassign(l *Node, r *Node, out **NodeList) bool {
 
 	case OSTRARRAYBYTE:
 		if l.Class == PEXTERN && r.Left.Op == OLITERAL {
-			sval := r.Left.Val.U.Sval
+			sval := r.Left.Val.U.(string)
 			slicebytes(l, sval, len(sval))
 			return true
 		}
@@ -449,7 +449,7 @@ func staticassign(l *Node, r *Node, out **NodeList) bool {
 			ta := typ(TARRAY)
 
 			ta.Type = r.Type.Type
-			ta.Bound = Mpgetfix(r.Right.Val.U.Xval)
+			ta.Bound = Mpgetfix(r.Right.Val.U.(*Mpint))
 			a := staticname(ta, 1)
 			r.Nname = a
 			n1 = *l
@@ -722,7 +722,7 @@ func slicelit(ctxt int, n *Node, var_ *Node, init **NodeList) {
 	// make an array type
 	t := shallow(n.Type)
 
-	t.Bound = Mpgetfix(n.Right.Val.U.Xval)
+	t.Bound = Mpgetfix(n.Right.Val.U.(*Mpint))
 	t.Width = 0
 	t.Sym = nil
 	t.Haspointers = 0
@@ -1226,7 +1226,7 @@ func oaslit(n *Node, init **NodeList) bool {
 
 func getlit(lit *Node) int {
 	if Smallintconst(lit) {
-		return int(Mpgetfix(lit.Val.U.Xval))
+		return int(Mpgetfix(lit.Val.U.(*Mpint)))
 	}
 	return -1
 }
@@ -1290,7 +1290,7 @@ func initplan(n *Node) {
 			if a.Op != OKEY || !Smallintconst(a.Left) {
 				Fatal("initplan arraylit")
 			}
-			addvalue(p, n.Type.Type.Width*Mpgetfix(a.Left.Val.U.Xval), nil, a.Right)
+			addvalue(p, n.Type.Type.Width*Mpgetfix(a.Left.Val.U.(*Mpint)), nil, a.Right)
 		}
 
 	case OSTRUCTLIT:
@@ -1360,19 +1360,19 @@ func iszero(n *Node) bool {
 			return true
 
 		case CTSTR:
-			return n.Val.U.Sval == ""
+			return n.Val.U.(string) == ""
 
 		case CTBOOL:
-			return !n.Val.U.Bval
+			return !n.Val.U.(bool)
 
 		case CTINT, CTRUNE:
-			return mpcmpfixc(n.Val.U.Xval, 0) == 0
+			return mpcmpfixc(n.Val.U.(*Mpint), 0) == 0
 
 		case CTFLT:
-			return mpcmpfltc(n.Val.U.Fval, 0) == 0
+			return mpcmpfltc(n.Val.U.(*Mpflt), 0) == 0
 
 		case CTCPLX:
-			return mpcmpfltc(&n.Val.U.Cval.Real, 0) == 0 && mpcmpfltc(&n.Val.U.Cval.Imag, 0) == 0
+			return mpcmpfltc(&n.Val.U.(*Mpcplx).Real, 0) == 0 && mpcmpfltc(&n.Val.U.(*Mpcplx).Imag, 0) == 0
 		}
 
 	case OARRAYLIT:
@@ -1510,10 +1510,10 @@ func gen_as_init(n *Node) bool {
 		gdata(&nam, nr, int(nr.Type.Width))
 
 	case TCOMPLEX64, TCOMPLEX128:
-		gdatacomplex(&nam, nr.Val.U.Cval)
+		gdatacomplex(&nam, nr.Val.U.(*Mpcplx))
 
 	case TSTRING:
-		gdatastring(&nam, nr.Val.U.Sval)
+		gdatastring(&nam, nr.Val.U.(string))
 	}
 
 	return true
diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/internal/gc/subr.go
index dd84214dc8..b09f4232aa 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/internal/gc/subr.go
@@ -666,8 +666,8 @@ func sortinter(t *Type) *Type {
 func Nodintconst(v int64) *Node {
 	c := Nod(OLITERAL, nil, nil)
 	c.Addable = true
-	c.Val.U.Xval = new(Mpint)
-	Mpmovecfix(c.Val.U.Xval, v)
+	c.Val.U = new(Mpint)
+	Mpmovecfix(c.Val.U.(*Mpint), v)
 	c.Val.Ctype = CTINT
 	c.Type = Types[TIDEAL]
 	ullmancalc(c)
@@ -677,8 +677,8 @@ func Nodintconst(v int64) *Node {
 func nodfltconst(v *Mpflt) *Node {
 	c := Nod(OLITERAL, nil, nil)
 	c.Addable = true
-	c.Val.U.Fval = newMpflt()
-	mpmovefltflt(c.Val.U.Fval, v)
+	c.Val.U = newMpflt()
+	mpmovefltflt(c.Val.U.(*Mpflt), v)
 	c.Val.Ctype = CTFLT
 	c.Type = Types[TIDEAL]
 	ullmancalc(c)
@@ -690,8 +690,8 @@ func Nodconst(n *Node, t *Type, v int64) {
 	n.Op = OLITERAL
 	n.Addable = true
 	ullmancalc(n)
-	n.Val.U.Xval = new(Mpint)
-	Mpmovecfix(n.Val.U.Xval, v)
+	n.Val.U = new(Mpint)
+	Mpmovecfix(n.Val.U.(*Mpint), v)
 	n.Val.Ctype = CTINT
 	n.Type = t
 
@@ -710,7 +710,7 @@ func nodnil() *Node {
 func Nodbool(b bool) *Node {
 	c := Nodintconst(0)
 	c.Val.Ctype = CTBOOL
-	c.Val.U.Bval = b
+	c.Val.U = b
 	c.Type = idealbool
 	return c
 }
@@ -724,7 +724,7 @@ func aindex(b *Node, t *Type) *Type {
 			Yyerror("array bound must be an integer expression")
 
 		case CTINT, CTRUNE:
-			bound = Mpgetfix(b.Val.U.Xval)
+			bound = Mpgetfix(b.Val.U.(*Mpint))
 			if bound < 0 {
 				Yyerror("array bound must be non negative")
 			}
@@ -2422,11 +2422,11 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
 
 		var v Val
 		v.Ctype = CTSTR
-		v.U.Sval = rcvr.Type.Sym.Pkg.Name // package name
+		v.U = rcvr.Type.Sym.Pkg.Name // package name
 		l = list(l, nodlit(v))
-		v.U.Sval = rcvr.Type.Sym.Name // type name
+		v.U = rcvr.Type.Sym.Name // type name
 		l = list(l, nodlit(v))
-		v.U.Sval = method.Sym.Name
+		v.U = method.Sym.Name
 		l = list(l, nodlit(v)) // method name
 		call := Nod(OCALL, syslook("panicwrap", 0), nil)
 		call.List = l
@@ -3139,7 +3139,7 @@ func powtwo(n *Node) int {
 		return -1
 	}
 
-	v := uint64(Mpgetfix(n.Val.U.Xval))
+	v := uint64(Mpgetfix(n.Val.U.(*Mpint)))
 	b := uint64(1)
 	for i := 0; i < 64; i++ {
 		if b == v {
diff --git a/src/cmd/internal/gc/swt.go b/src/cmd/internal/gc/swt.go
index 7cb632cebe..e8f15a5ce5 100644
--- a/src/cmd/internal/gc/swt.go
+++ b/src/cmd/internal/gc/swt.go
@@ -218,7 +218,7 @@ func (s *exprSwitch) walk(sw *Node) {
 	s.kind = switchKindExpr
 	if Isconst(sw.Ntest, CTBOOL) {
 		s.kind = switchKindTrue
-		if !sw.Ntest.Val.U.Bval {
+		if !sw.Ntest.Val.U.(bool) {
 			s.kind = switchKindFalse
 		}
 	}
@@ -755,16 +755,16 @@ func exprcmp(c1, c2 *caseClause) int {
 	// sort by constant value to enable binary search
 	switch ct {
 	case CTFLT:
-		return mpcmpfltflt(n1.Val.U.Fval, n2.Val.U.Fval)
+		return mpcmpfltflt(n1.Val.U.(*Mpflt), n2.Val.U.(*Mpflt))
 	case CTINT, CTRUNE:
-		return Mpcmpfixfix(n1.Val.U.Xval, n2.Val.U.Xval)
+		return Mpcmpfixfix(n1.Val.U.(*Mpint), n2.Val.U.(*Mpint))
 	case CTSTR:
 		// Sort strings by length and then by value.
 		// It is much cheaper to compare lengths than values,
 		// and all we need here is consistency.
 		// We respect this sorting in exprSwitch.walkCases.
-		a := n1.Val.U.Sval
-		b := n2.Val.U.Sval
+		a := n1.Val.U.(string)
+		b := n2.Val.U.(string)
 		if len(a) < len(b) {
 			return -1
 		}
diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go
index 3061275da3..bc6bbdb7e3 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/internal/gc/typecheck.go
@@ -392,7 +392,7 @@ OpSwitch:
 				return
 			}
 
-			t.Bound = Mpgetfix(v.U.Xval)
+			t.Bound = Mpgetfix(v.U.(*Mpint))
 			if doesoverflow(v, Types[TINT]) {
 				Yyerror("array bound is too large")
 				n.Type = nil
@@ -770,7 +770,7 @@ OpSwitch:
 		}
 
 		if (op == ODIV || op == OMOD) && Isconst(r, CTINT) {
-			if mpcmpfixc(r.Val.U.Xval, 0) == 0 {
+			if mpcmpfixc(r.Val.U.(*Mpint), 0) == 0 {
 				Yyerror("division by zero")
 				n.Type = nil
 				return
@@ -1043,14 +1043,14 @@ OpSwitch:
 			}
 
 			if Isconst(n.Right, CTINT) {
-				x := Mpgetfix(n.Right.Val.U.Xval)
+				x := Mpgetfix(n.Right.Val.U.(*Mpint))
 				if x < 0 {
 					Yyerror("invalid %s index %v (index must be non-negative)", why, n.Right)
 				} else if Isfixedarray(t) && t.Bound > 0 && x >= t.Bound {
 					Yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.Bound)
-				} else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.Val.U.Sval)) {
-					Yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.Val.U.Sval))
-				} else if Mpcmpfixfix(n.Right.Val.U.Xval, Maxintval[TINT]) > 0 {
+				} else if Isconst(n.Left, CTSTR) && x >= int64(len(n.Left.Val.U.(string))) {
+					Yyerror("invalid string index %v (out of bounds for %d-byte string)", n.Right, len(n.Left.Val.U.(string)))
+				} else if Mpcmpfixfix(n.Right.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
 					Yyerror("invalid %s index %v (index too large)", why, n.Right)
 				}
 			}
@@ -1435,9 +1435,9 @@ OpSwitch:
 			if Isconst(l, CTCPLX) {
 				r := n
 				if n.Op == OREAL {
-					n = nodfltconst(&l.Val.U.Cval.Real)
+					n = nodfltconst(&l.Val.U.(*Mpcplx).Real)
 				} else {
-					n = nodfltconst(&l.Val.U.Cval.Imag)
+					n = nodfltconst(&l.Val.U.(*Mpcplx).Imag)
 				}
 				n.Orig = r
 			}
@@ -1451,7 +1451,7 @@ OpSwitch:
 		case TSTRING:
 			if Isconst(l, CTSTR) {
 				r := Nod(OXXX, nil, nil)
-				Nodconst(r, Types[TINT], int64(len(l.Val.U.Sval)))
+				Nodconst(r, Types[TINT], int64(len(l.Val.U.(string))))
 				r.Orig = n
 				n = r
 			}
@@ -1859,7 +1859,7 @@ OpSwitch:
 				n.Type = nil
 				return
 			}
-			if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && Mpcmpfixfix(l.Val.U.Xval, r.Val.U.Xval) > 0 {
+			if Isconst(l, CTINT) && r != nil && Isconst(r, CTINT) && Mpcmpfixfix(l.Val.U.(*Mpint), r.Val.U.(*Mpint)) > 0 {
 				Yyerror("len larger than cap in make(%v)", t)
 				n.Type = nil
 				return
@@ -2255,16 +2255,16 @@ func checksliceindex(l *Node, r *Node, tp *Type) int {
 	}
 
 	if r.Op == OLITERAL {
-		if Mpgetfix(r.Val.U.Xval) < 0 {
+		if Mpgetfix(r.Val.U.(*Mpint)) < 0 {
 			Yyerror("invalid slice index %v (index must be non-negative)", r)
 			return -1
-		} else if tp != nil && tp.Bound > 0 && Mpgetfix(r.Val.U.Xval) > tp.Bound {
+		} else if tp != nil && tp.Bound > 0 && Mpgetfix(r.Val.U.(*Mpint)) > tp.Bound {
 			Yyerror("invalid slice index %v (out of bounds for %d-element array)", r, tp.Bound)
 			return -1
-		} else if Isconst(l, CTSTR) && Mpgetfix(r.Val.U.Xval) > int64(len(l.Val.U.Sval)) {
-			Yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.Val.U.Sval))
+		} else if Isconst(l, CTSTR) && Mpgetfix(r.Val.U.(*Mpint)) > int64(len(l.Val.U.(string))) {
+			Yyerror("invalid slice index %v (out of bounds for %d-byte string)", r, len(l.Val.U.(string)))
 			return -1
-		} else if Mpcmpfixfix(r.Val.U.Xval, Maxintval[TINT]) > 0 {
+		} else if Mpcmpfixfix(r.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
 			Yyerror("invalid slice index %v (index too large)", r)
 			return -1
 		}
@@ -2274,7 +2274,7 @@ func checksliceindex(l *Node, r *Node, tp *Type) int {
 }
 
 func checksliceconst(lo *Node, hi *Node) int {
-	if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && Mpcmpfixfix(lo.Val.U.Xval, hi.Val.U.Xval) > 0 {
+	if lo != nil && hi != nil && lo.Op == OLITERAL && hi.Op == OLITERAL && Mpcmpfixfix(lo.Val.U.(*Mpint), hi.Val.U.(*Mpint)) > 0 {
 		Yyerror("invalid slice index: %v > %v", lo, hi)
 		return -1
 	}
@@ -2824,10 +2824,10 @@ func keydup(n *Node, hash []*Node) {
 		b = 23
 
 	case CTINT, CTRUNE:
-		b = uint32(Mpgetfix(n.Val.U.Xval))
+		b = uint32(Mpgetfix(n.Val.U.(*Mpint)))
 
 	case CTFLT:
-		d := mpgetflt(n.Val.U.Fval)
+		d := mpgetflt(n.Val.U.(*Mpflt))
 		x := math.Float64bits(d)
 		for i := 0; i < 8; i++ {
 			b = b*PRIME1 + uint32(x&0xFF)
@@ -2836,8 +2836,8 @@ func keydup(n *Node, hash []*Node) {
 
 	case CTSTR:
 		b = 0
-		s := n.Val.U.Sval
-		for i := len(n.Val.U.Sval); i > 0; i-- {
+		s := n.Val.U.(string)
+		for i := len(n.Val.U.(string)); i > 0; i-- {
 			b = b*PRIME1 + uint32(s[0])
 			s = s[1:]
 		}
@@ -2853,12 +2853,12 @@ func keydup(n *Node, hash []*Node) {
 			if Eqtype(a.Left.Type, n.Type) {
 				cmp.Right = a.Left
 				evconst(&cmp)
-				b = uint32(obj.Bool2int(cmp.Val.U.Bval))
+				b = uint32(obj.Bool2int(cmp.Val.U.(bool)))
 			}
 		} else if Eqtype(a.Type, n.Type) {
 			cmp.Right = a
 			evconst(&cmp)
-			b = uint32(obj.Bool2int(cmp.Val.U.Bval))
+			b = uint32(obj.Bool2int(cmp.Val.U.(bool)))
 		}
 
 		if b != 0 {
@@ -2876,11 +2876,11 @@ func indexdup(n *Node, hash []*Node) {
 		Fatal("indexdup: not OLITERAL")
 	}
 
-	b := uint32(Mpgetfix(n.Val.U.Xval))
+	b := uint32(Mpgetfix(n.Val.U.(*Mpint)))
 	h := uint(b % uint32(len(hash)))
 	var c uint32
 	for a := hash[h]; a != nil; a = a.Ntest {
-		c = uint32(Mpgetfix(a.Val.U.Xval))
+		c = uint32(Mpgetfix(a.Val.U.(*Mpint)))
 		if b == c {
 			Yyerror("duplicate index in array literal: %d", b)
 			return
@@ -3529,7 +3529,7 @@ func stringtoarraylit(np **Node) {
 		Fatal("stringtoarraylit %v", n)
 	}
 
-	s := n.Left.Val.U.Sval
+	s := n.Left.Val.U.(string)
 	var l *NodeList
 	if n.Type.Type.Etype == TUINT8 {
 		// []byte
@@ -3886,12 +3886,12 @@ func checkmake(t *Type, arg string, n *Node) int {
 		switch n.Val.Ctype {
 		case CTINT, CTRUNE, CTFLT, CTCPLX:
 			n.Val = toint(n.Val)
-			if mpcmpfixc(n.Val.U.Xval, 0) < 0 {
+			if mpcmpfixc(n.Val.U.(*Mpint), 0) < 0 {
 				Yyerror("negative %s argument in make(%v)", arg, t)
 				return -1
 			}
 
-			if Mpcmpfixfix(n.Val.U.Xval, Maxintval[TINT]) > 0 {
+			if Mpcmpfixfix(n.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
 				Yyerror("%s argument too large in make(%v)", arg, t)
 				return -1
 			}
diff --git a/src/cmd/internal/gc/unsafe.go b/src/cmd/internal/gc/unsafe.go
index aa90a19308..824ecd0339 100644
--- a/src/cmd/internal/gc/unsafe.go
+++ b/src/cmd/internal/gc/unsafe.go
@@ -140,8 +140,8 @@ ret:
 	var val Val
 	val.Ctype = CTINT
 
-	val.U.Xval = new(Mpint)
-	Mpmovecfix(val.U.Xval, v)
+	val.U = new(Mpint)
+	Mpmovecfix(val.U.(*Mpint), v)
 	n := Nod(OLITERAL, nil, nil)
 	n.Orig = nn
 	n.Val = val
diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go
index 495acb1861..81bb8524b3 100644
--- a/src/cmd/internal/gc/walk.go
+++ b/src/cmd/internal/gc/walk.go
@@ -1206,7 +1206,7 @@ func walkexpr(np **Node, init **NodeList) {
 				Yyerror("index out of bounds")
 			}
 		} else if Isconst(n.Left, CTSTR) {
-			n.Bounded = bounded(r, int64(len(n.Left.Val.U.Sval)))
+			n.Bounded = bounded(r, int64(len(n.Left.Val.U.(string))))
 			if Debug['m'] != 0 && n.Bounded && !Isconst(n.Right, CTINT) {
 				Warn("index bounds check elided")
 			}
@@ -1217,16 +1217,16 @@ func walkexpr(np **Node, init **NodeList) {
 					// replace "abc"[1] with 'b'.
 					// delayed until now because "abc"[1] is not
 					// an ideal constant.
-					v := Mpgetfix(n.Right.Val.U.Xval)
+					v := Mpgetfix(n.Right.Val.U.(*Mpint))
 
-					Nodconst(n, n.Type, int64(n.Left.Val.U.Sval[v]))
+					Nodconst(n, n.Type, int64(n.Left.Val.U.(string)[v]))
 					n.Typecheck = 1
 				}
 			}
 		}
 
 		if Isconst(n.Right, CTINT) {
-			if Mpcmpfixfix(n.Right.Val.U.Xval, &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.Xval, Maxintval[TINT]) > 0 {
+			if Mpcmpfixfix(n.Right.Val.U.(*Mpint), &mpzero) < 0 || Mpcmpfixfix(n.Right.Val.U.(*Mpint), Maxintval[TINT]) > 0 {
 				Yyerror("index out of bounds")
 			}
 		}
@@ -1338,7 +1338,7 @@ func walkexpr(np **Node, init **NodeList) {
 	// comparing the lengths instead will yield the same result
 	// without the function call.
 	case OCMPSTR:
-		if (Isconst(n.Left, CTSTR) && len(n.Left.Val.U.Sval) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val.U.Sval) == 0) {
+		if (Isconst(n.Left, CTSTR) && len(n.Left.Val.U.(string)) == 0) || (Isconst(n.Right, CTSTR) && len(n.Right.Val.U.(string)) == 0) {
 			r := Nod(int(n.Etype), Nod(OLEN, n.Left, nil), Nod(OLEN, n.Right, nil))
 			typecheck(&r, Erv)
 			walkexpr(&r, init)
@@ -1458,7 +1458,7 @@ func walkexpr(np **Node, init **NodeList) {
 			l = r
 		}
 		t := n.Type
-		if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.Xval) < (1<<16)/t.Type.Width) {
+		if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.(*Mpint)) < (1<<16)/t.Type.Width) {
 			// var arr [r]T
 			// n = arr[:l]
 			t = aindex(r, t.Type) // [r]T
@@ -2862,7 +2862,7 @@ func addstr(n *Node, init **NodeList) *Node {
 		sz := int64(0)
 		for l := n.List; l != nil; l = l.Next {
 			if n.Op == OLITERAL {
-				sz += int64(len(n.Val.U.Sval))
+				sz += int64(len(n.Val.U.(string)))
 			}
 		}
 
@@ -3415,7 +3415,7 @@ func samecheap(a *Node, b *Node) bool {
 		case OINDEX:
 			ar = a.Right
 			br = b.Right
-			if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.Xval, br.Val.U.Xval) != 0 {
+			if !Isconst(ar, CTINT) || !Isconst(br, CTINT) || Mpcmpfixfix(ar.Val.U.(*Mpint), br.Val.U.(*Mpint)) != 0 {
 				return false
 			}
 		}
@@ -3451,9 +3451,9 @@ func walkrotate(np **Node) {
 	w := int(l.Type.Width * 8)
 
 	if Smallintconst(l.Right) && Smallintconst(r.Right) {
-		sl := int(Mpgetfix(l.Right.Val.U.Xval))
+		sl := int(Mpgetfix(l.Right.Val.U.(*Mpint)))
 		if sl >= 0 {
-			sr := int(Mpgetfix(r.Right.Val.U.Xval))
+			sr := int(Mpgetfix(r.Right.Val.U.(*Mpint)))
 			if sr >= 0 && sl+sr == w {
 				// Rewrite left shift half to left rotate.
 				if l.Op == OLSH {
@@ -3464,7 +3464,7 @@ func walkrotate(np **Node) {
 				n.Op = OLROT
 
 				// Remove rotate 0 and rotate w.
-				s := int(Mpgetfix(n.Right.Val.U.Xval))
+				s := int(Mpgetfix(n.Right.Val.U.(*Mpint)))
 
 				if s == 0 || s == w {
 					n = n.Left
@@ -3507,7 +3507,7 @@ func walkmul(np **Node, init **NodeList) {
 	// x*0 is 0 (and side effects of x).
 	var pow int
 	var w int
-	if Mpgetfix(nr.Val.U.Xval) == 0 {
+	if Mpgetfix(nr.Val.U.(*Mpint)) == 0 {
 		cheapexpr(nl, init)
 		Nodconst(n, n.Type, 0)
 		goto ret
@@ -3600,10 +3600,10 @@ func walkdiv(np **Node, init **NodeList) {
 		m.W = w
 
 		if Issigned[nl.Type.Etype] {
-			m.Sd = Mpgetfix(nr.Val.U.Xval)
+			m.Sd = Mpgetfix(nr.Val.U.(*Mpint))
 			Smagic(&m)
 		} else {
-			m.Ud = uint64(Mpgetfix(nr.Val.U.Xval))
+			m.Ud = uint64(Mpgetfix(nr.Val.U.(*Mpint)))
 			Umagic(&m)
 		}
 
@@ -3797,7 +3797,7 @@ func walkdiv(np **Node, init **NodeList) {
 			// n = nl & (nr-1)
 			n.Op = OAND
 
-			Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.Xval)-1)
+			Nodconst(nc, nl.Type, Mpgetfix(nr.Val.U.(*Mpint))-1)
 		} else {
 			// n = nl >> pow
 			n.Op = ORSH
@@ -3827,7 +3827,7 @@ func bounded(n *Node, max int64) bool {
 	bits := int32(8 * n.Type.Width)
 
 	if Smallintconst(n) {
-		v := Mpgetfix(n.Val.U.Xval)
+		v := Mpgetfix(n.Val.U.(*Mpint))
 		return 0 <= v && v < max
 	}
 
@@ -3835,9 +3835,9 @@ func bounded(n *Node, max int64) bool {
 	case OAND:
 		v := int64(-1)
 		if Smallintconst(n.Left) {
-			v = Mpgetfix(n.Left.Val.U.Xval)
+			v = Mpgetfix(n.Left.Val.U.(*Mpint))
 		} else if Smallintconst(n.Right) {
-			v = Mpgetfix(n.Right.Val.U.Xval)
+			v = Mpgetfix(n.Right.Val.U.(*Mpint))
 		}
 
 		if 0 <= v && v < max {
@@ -3846,7 +3846,7 @@ func bounded(n *Node, max int64) bool {
 
 	case OMOD:
 		if !sign && Smallintconst(n.Right) {
-			v := Mpgetfix(n.Right.Val.U.Xval)
+			v := Mpgetfix(n.Right.Val.U.(*Mpint))
 			if 0 <= v && v <= max {
 				return true
 			}
@@ -3854,7 +3854,7 @@ func bounded(n *Node, max int64) bool {
 
 	case ODIV:
 		if !sign && Smallintconst(n.Right) {
-			v := Mpgetfix(n.Right.Val.U.Xval)
+			v := Mpgetfix(n.Right.Val.U.(*Mpint))
 			for bits > 0 && v >= 2 {
 				bits--
 				v >>= 1
@@ -3863,7 +3863,7 @@ func bounded(n *Node, max int64) bool {
 
 	case ORSH:
 		if !sign && Smallintconst(n.Right) {
-			v := Mpgetfix(n.Right.Val.U.Xval)
+			v := Mpgetfix(n.Right.Val.U.(*Mpint))
 			if v > int64(bits) {
 				return true
 			}
@@ -3996,17 +3996,17 @@ func candiscard(n *Node) bool {
 
 		// Discardable as long as we know it's not division by zero.
 	case ODIV, OMOD:
-		if Isconst(n.Right, CTINT) && mpcmpfixc(n.Right.Val.U.Xval, 0) != 0 {
+		if Isconst(n.Right, CTINT) && mpcmpfixc(n.Right.Val.U.(*Mpint), 0) != 0 {
 			break
 		}
-		if Isconst(n.Right, CTFLT) && mpcmpfltc(n.Right.Val.U.Fval, 0) != 0 {
+		if Isconst(n.Right, CTFLT) && mpcmpfltc(n.Right.Val.U.(*Mpflt), 0) != 0 {
 			break
 		}
 		return false
 
 		// Discardable as long as we know it won't fail because of a bad size.
 	case OMAKECHAN, OMAKEMAP:
-		if Isconst(n.Left, CTINT) && mpcmpfixc(n.Left.Val.U.Xval, 0) == 0 {
+		if Isconst(n.Left, CTINT) && mpcmpfixc(n.Left.Val.U.(*Mpint), 0) == 0 {
 			break
 		}
 		return false
diff --git a/src/cmd/internal/gc/y.go b/src/cmd/internal/gc/y.go
index fd3f2b3176..06f6b13eb6 100644
--- a/src/cmd/internal/gc/y.go
+++ b/src/cmd/internal/gc/y.go
@@ -2327,13 +2327,13 @@ yydefault:
 		{
 			var p *Pkg
 
-			if yyDollar[2].val.U.Sval == "" {
+			if yyDollar[2].val.U.(string) == "" {
 				p = importpkg
 			} else {
-				if isbadimport(yyDollar[2].val.U.Sval) {
+				if isbadimport(yyDollar[2].val.U.(string)) {
 					errorexit()
 				}
-				p = mkpkg(yyDollar[2].val.U.Sval)
+				p = mkpkg(yyDollar[2].val.U.(string))
 			}
 			yyVAL.sym = Pkglookup(yyDollar[4].sym.Name, p)
 		}
@@ -2343,13 +2343,13 @@ yydefault:
 		{
 			var p *Pkg
 
-			if yyDollar[2].val.U.Sval == "" {
+			if yyDollar[2].val.U.(string) == "" {
 				p = importpkg
 			} else {
-				if isbadimport(yyDollar[2].val.U.Sval) {
+				if isbadimport(yyDollar[2].val.U.(string)) {
 					errorexit()
 				}
-				p = mkpkg(yyDollar[2].val.U.Sval)
+				p = mkpkg(yyDollar[2].val.U.(string))
 			}
 			yyVAL.sym = Pkglookup("?", p)
 		}
@@ -3156,7 +3156,7 @@ yydefault:
 		yyDollar = yyS[yypt-4 : yypt+1]
 		//line go.y:1947
 		{
-			importimport(yyDollar[2].sym, yyDollar[3].val.U.Sval)
+			importimport(yyDollar[2].sym, yyDollar[3].val.U.(string))
 		}
 	case 305:
 		yyDollar = yyS[yypt-4 : yypt+1]
@@ -3404,14 +3404,14 @@ yydefault:
 			yyVAL.node = nodlit(yyDollar[2].val)
 			switch yyVAL.node.Val.Ctype {
 			case CTINT, CTRUNE:
-				mpnegfix(yyVAL.node.Val.U.Xval)
+				mpnegfix(yyVAL.node.Val.U.(*Mpint))
 				break
 			case CTFLT:
-				mpnegflt(yyVAL.node.Val.U.Fval)
+				mpnegflt(yyVAL.node.Val.U.(*Mpflt))
 				break
 			case CTCPLX:
-				mpnegflt(&yyVAL.node.Val.U.Cval.Real)
-				mpnegflt(&yyVAL.node.Val.U.Cval.Imag)
+				mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Real)
+				mpnegflt(&yyVAL.node.Val.U.(*Mpcplx).Imag)
 				break
 			default:
 				Yyerror("bad negated constant")
@@ -3432,11 +3432,11 @@ yydefault:
 		{
 			if yyDollar[2].node.Val.Ctype == CTRUNE && yyDollar[4].node.Val.Ctype == CTINT {
 				yyVAL.node = yyDollar[2].node
-				mpaddfixfix(yyDollar[2].node.Val.U.Xval, yyDollar[4].node.Val.U.Xval, 0)
+				mpaddfixfix(yyDollar[2].node.Val.U.(*Mpint), yyDollar[4].node.Val.U.(*Mpint), 0)
 				break
 			}
-			yyDollar[4].node.Val.U.Cval.Real = yyDollar[4].node.Val.U.Cval.Imag
-			Mpmovecflt(&yyDollar[4].node.Val.U.Cval.Imag, 0.0)
+			yyDollar[4].node.Val.U.(*Mpcplx).Real = yyDollar[4].node.Val.U.(*Mpcplx).Imag
+			Mpmovecflt(&yyDollar[4].node.Val.U.(*Mpcplx).Imag, 0.0)
 			yyVAL.node = nodcplxlit(yyDollar[2].node.Val, yyDollar[4].node.Val)
 		}
 	case 346:

From 76ec0ee53a5619b4ead4772c075105bf66c0ac67 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Fri, 3 Apr 2015 17:43:38 -0700
Subject: [PATCH 107/232] cmd/internal/gc: separate Name-only Node fields

Name will be converted from an anonymous to a
named field in a subsequent, automated CL.

No functional changes. Passes toolstash -cmp.

This reduces the size of gc.Node from 424 to 400 bytes.
This in turn reduces the permanent (pprof -inuse_space)
memory usage while compiling the test/rotate?.go tests:

test	old(MB)	new(MB)	change
rotate0	379.49	367.30	-3.21%
rotate1	373.42	361.59	-3.16%
rotate2	381.17	368.77	-3.25%
rotate3	374.30	362.48	-3.15%

Updates #9933.

Change-Id: I21479527c136add4f1efb9342774e3be3e276e83
Reviewed-on: https://go-review.googlesource.com/10120
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/subr.go      |  3 ++-
 src/cmd/internal/gc/syntax.go    | 33 +++++++++++++++++---------------
 src/cmd/internal/gc/typecheck.go |  3 +++
 3 files changed, 23 insertions(+), 16 deletions(-)

diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/internal/gc/subr.go
index b09f4232aa..7f9e78810f 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/internal/gc/subr.go
@@ -380,6 +380,8 @@ func Nod(op int, nleft *Node, nright *Node) *Node {
 	switch op {
 	case OCLOSURE, ODCLFUNC:
 		n.Func = new(Func)
+	case ONAME:
+		n.Name = new(Name)
 	}
 	return n
 }
@@ -771,7 +773,6 @@ func treecopy(n *Node) *Node {
 		}
 		fallthrough
 
-		// fall through
 	case ONAME, OLITERAL, OTYPE:
 		m = n
 	}
diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go
index 70c6f3f567..9ef00a09cb 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/internal/gc/syntax.go
@@ -30,7 +30,6 @@ type Node struct {
 	Etype       uint8 // op for OASOP, etype for OTYPE, exclam for export
 	Bounded     bool  // bounds check unnecessary
 	Class       uint8 // PPARAM, PAUTO, PEXTERN, etc
-	Method      bool  // OCALLMETH is direct method call
 	Embedded    uint8 // ODCLFIELD embedded type
 	Colas       bool  // OAS resulting from :=
 	Diag        uint8 // already printed error about this
@@ -42,15 +41,11 @@ type Node struct {
 	Initorder   uint8
 	Used        bool
 	Isddd       bool // is the argument variadic
-	Readonly    bool
 	Implicit    bool
 	Addrtaken   bool   // address taken, even if not moved to heap
 	Assigned    bool   // is the variable ever assigned to
-	Captured    bool   // is the variable captured by a closure
-	Byval       bool   // is the variable captured by value or by reference
 	Likely      int8   // likeliness of if statement
 	Hasbreak    bool   // has break statement
-	Needzero    bool   // if it contains pointers, needs to be zeroed on function entry
 	Esc         uint16 // EscXXX
 	Funcdepth   int32
 
@@ -69,15 +64,14 @@ type Node struct {
 	Reg int16
 
 	// ONAME
-	Ntype     *Node
-	Defn      *Node // ONAME: initializing assignment; OLABEL: labeled statement
-	Pack      *Node // real package for import . names
-	Curfn     *Node // function for local variables
-	Paramfld  *Type // TFIELD for this PPARAM; also for ODOT, curfn
-	Decldepth int   // declaration loop depth, increased for every loop or label
+	*Name
+	Ntype    *Node
+	Defn     *Node // ONAME: initializing assignment; OLABEL: labeled statement
+	Pack     *Node // real package for import . names
+	Curfn    *Node // function for local variables
+	Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn
 
 	// ONAME func param with PHEAP
-	Heapaddr   *Node // temp holding heap address of param
 	Outerexpr  *Node // expression copied into closure for variable
 	Stackparam *Node // OPARAM node referring to stack copy of param
 	Alloc      *Node // allocation call
@@ -87,9 +81,6 @@ type Node struct {
 	Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF
 	Top     int   // top context (Ecall, Eproc, etc)
 
-	// ONAME substitute while inlining
-	Inlvar *Node
-
 	// OPACK
 	Pkg *Pkg
 
@@ -113,6 +104,18 @@ type Node struct {
 	Opt      interface{} // for optimization passes
 }
 
+// Name holds Node fields used only by ONAME nodes.
+type Name struct {
+	Heapaddr  *Node // temp holding heap address of param
+	Inlvar    *Node // ONAME substitute while inlining
+	Decldepth int   // declaration loop depth, increased for every loop or label
+	Method    bool  // OCALLMETH name
+	Readonly  bool
+	Captured  bool // is the variable captured by a closure
+	Byval     bool // is the variable captured by value or by reference
+	Needzero  bool // if it contains pointers, needs to be zeroed on function entry
+}
+
 // Func holds Node fields used only with function-like nodes.
 type Func struct {
 	Shortname *Node
diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go
index bc6bbdb7e3..6ad8c82c32 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/internal/gc/typecheck.go
@@ -891,6 +891,9 @@ OpSwitch:
 			}
 
 			n.Op = ONAME
+			if n.Name == nil {
+				n.Name = new(Name)
+			}
 			n.Sym = n.Right.Sym
 			n.Type = methodfunc(n.Type, n.Left.Type)
 			n.Xoffset = 0

From 5726af54eb3a52b9446a834991110b945e780e99 Mon Sep 17 00:00:00 2001
From: Daniel Morsing 
Date: Wed, 29 Apr 2015 10:51:42 +0100
Subject: [PATCH 108/232] cmd/internal/gc: ignore declarations of types for
 goto validation

Fixes #8042.

Change-Id: I75080f24104256065fd73b07a13c5b8e7d6da94c
Reviewed-on: https://go-review.googlesource.com/9442
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/dcl.go |  3 +++
 src/cmd/internal/gc/gen.go | 21 ++++++++++++++++++---
 src/cmd/internal/gc/go.go  |  1 +
 test/goto.go               | 16 ++++++++++++++++
 4 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/src/cmd/internal/gc/dcl.go b/src/cmd/internal/gc/dcl.go
index 85a33bec3f..58b69ab8f9 100644
--- a/src/cmd/internal/gc/dcl.go
+++ b/src/cmd/internal/gc/dcl.go
@@ -65,6 +65,9 @@ func popdcl() {
 		}
 		s = Pkglookup(d.Name, d.Pkg)
 		lno = int(s.Lastlineno)
+		if s.Def != nil {
+			d.whyPushed = s.Def.Op
+		}
 		dcopy(s, d)
 		d.Lastlineno = int32(lno)
 		if dflag() {
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go
index cd0e650ca9..bba04d41ad 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/internal/gc/gen.go
@@ -159,6 +159,21 @@ func checkgoto(from *Node, to *Node) {
 		fs = fs.Link
 	}
 	if fs != to.Sym {
+		// more declarations at label than at goto.
+		// figure out if they are all types.
+		ts := to.Sym
+		ntt := nt
+		for ; ntt > nf; ntt-- {
+			if ts.whyPushed != OTYPE {
+				break
+			}
+			ts = ts.Link
+		}
+		// all types, nothing to see here.
+		if ntt == nf {
+			return
+		}
+
 		lno := int(lineno)
 		setlineno(from)
 
@@ -168,11 +183,11 @@ func checkgoto(from *Node, to *Node) {
 		var block *Sym
 
 		var dcl *Sym
-		ts := to.Sym
+		ts = to.Sym
 		for ; nt > nf; nt-- {
 			if ts.Pkg == nil {
 				block = ts
-			} else {
+			} else if ts.whyPushed != OTYPE {
 				dcl = ts
 			}
 			ts = ts.Link
@@ -181,7 +196,7 @@ func checkgoto(from *Node, to *Node) {
 		for ts != fs {
 			if ts.Pkg == nil {
 				block = ts
-			} else {
+			} else if ts.whyPushed != OTYPE {
 				dcl = ts
 			}
 			ts = ts.Link
diff --git a/src/cmd/internal/gc/go.go b/src/cmd/internal/gc/go.go
index 31692bdf00..4800218c95 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/internal/gc/go.go
@@ -111,6 +111,7 @@ type Sym struct {
 	Uniqgen   uint32
 	Importdef *Pkg   // where imported definition was found
 	Linkname  string // link name
+	whyPushed uint8  // why this symbol pushed onto dclstack. Same as Node.Op. Used by goto validation
 
 	// saved and restored by dcopy
 	Pkg        *Pkg
diff --git a/test/goto.go b/test/goto.go
index ca477b3d0c..c626f3d1c1 100644
--- a/test/goto.go
+++ b/test/goto.go
@@ -536,3 +536,19 @@ func _() {
 		goto L // ERROR "goto L jumps into block starting at LINE-4|goto jumps into block"
 	}
 }
+
+// issue 8042
+func _() {
+	goto L
+	type a int
+	L:
+}
+
+// make sure we only complain about variable declarations.
+func _() {
+	goto L // ERROR "goto L jumps over declaration of x at LINE+2|goto jumps over declaration"
+	type a int
+	x := 1	// GCCGO_ERROR "defined here"
+	_ = x
+L:
+}

From 4302fd0409da5e4f1d71471a6770dacdc3301197 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?H=C3=A5vard=20Haugen?= 
Date: Sun, 26 Apr 2015 23:52:42 +0200
Subject: [PATCH 109/232] encoding/json: fix decoding of types with '[]byte' as
 underlying type

All slice types which have elements of kind reflect.Uint8 are marshalled
into base64 for compactness. When decoding such data into a custom type
based on []byte the decoder checked the slice kind instead of the slice
element kind, so no appropriate decoder was found.

Fixed by letting the decoder check slice element kind like the encoder.
This guarantees that already encoded data can still be successfully
decoded.

Fixes #8962.

Change-Id: Ia320d4dc2c6e9e5fe6d8dc15788c81da23d20c4f
Reviewed-on: https://go-review.googlesource.com/9371
Reviewed-by: Peter Waldschmidt 
Reviewed-by: Russ Cox 
---
 src/encoding/json/decode.go      |  2 +-
 src/encoding/json/decode_test.go | 21 +++++++++++++++++++++
 src/encoding/json/encode.go      |  2 --
 3 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/src/encoding/json/decode.go b/src/encoding/json/decode.go
index f26a7d49f0..613641afbb 100644
--- a/src/encoding/json/decode.go
+++ b/src/encoding/json/decode.go
@@ -739,7 +739,7 @@ func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool
 		default:
 			d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
 		case reflect.Slice:
-			if v.Type() != byteSliceType {
+			if v.Type().Elem().Kind() != reflect.Uint8 {
 				d.saveError(&UnmarshalTypeError{"string", v.Type(), int64(d.off)})
 				break
 			}
diff --git a/src/encoding/json/decode_test.go b/src/encoding/json/decode_test.go
index 7ecc8f4402..f208ee8a7c 100644
--- a/src/encoding/json/decode_test.go
+++ b/src/encoding/json/decode_test.go
@@ -1207,7 +1207,28 @@ func TestStringKind(t *testing.T) {
 	if !reflect.DeepEqual(m1, m2) {
 		t.Error("Items should be equal after encoding and then decoding")
 	}
+}
 
+// Custom types with []byte as underlying type could not be marshalled
+// and then unmarshalled.
+// Issue 8962.
+func TestByteKind(t *testing.T) {
+	type byteKind []byte
+
+	a := byteKind("hello")
+
+	data, err := Marshal(a)
+	if err != nil {
+		t.Error(err)
+	}
+	var b byteKind
+	err = Unmarshal(data, &b)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if !reflect.DeepEqual(a, b) {
+		t.Errorf("expected %v == %v", a, b)
+	}
 }
 
 var decodeTypeErrorTests = []struct {
diff --git a/src/encoding/json/encode.go b/src/encoding/json/encode.go
index 4db9f35e69..7789bb5141 100644
--- a/src/encoding/json/encode.go
+++ b/src/encoding/json/encode.go
@@ -275,8 +275,6 @@ func (e *encodeState) error(err error) {
 	panic(err)
 }
 
-var byteSliceType = reflect.TypeOf([]byte(nil))
-
 func isEmptyValue(v reflect.Value) bool {
 	switch v.Kind() {
 	case reflect.Array, reflect.Map, reflect.Slice, reflect.String:

From 97494a45e21099c3a357785f31894a5e69e086c8 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Fri, 15 May 2015 16:35:43 +0000
Subject: [PATCH 110/232] Revert "cmd/internal/gc: ignore declarations of types
 for goto validation"

This reverts commit 5726af54eb3a52b9446a834991110b945e780e99.

It broke all the builds.

Change-Id: I4b1dde86f9433717d303c1dabd6aa1a2bf97fab2
Reviewed-on: https://go-review.googlesource.com/10143
Reviewed-by: Brad Fitzpatrick 
---
 src/cmd/internal/gc/dcl.go |  3 ---
 src/cmd/internal/gc/gen.go | 21 +++------------------
 src/cmd/internal/gc/go.go  |  1 -
 test/goto.go               | 16 ----------------
 4 files changed, 3 insertions(+), 38 deletions(-)

diff --git a/src/cmd/internal/gc/dcl.go b/src/cmd/internal/gc/dcl.go
index 58b69ab8f9..85a33bec3f 100644
--- a/src/cmd/internal/gc/dcl.go
+++ b/src/cmd/internal/gc/dcl.go
@@ -65,9 +65,6 @@ func popdcl() {
 		}
 		s = Pkglookup(d.Name, d.Pkg)
 		lno = int(s.Lastlineno)
-		if s.Def != nil {
-			d.whyPushed = s.Def.Op
-		}
 		dcopy(s, d)
 		d.Lastlineno = int32(lno)
 		if dflag() {
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go
index bba04d41ad..cd0e650ca9 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/internal/gc/gen.go
@@ -159,21 +159,6 @@ func checkgoto(from *Node, to *Node) {
 		fs = fs.Link
 	}
 	if fs != to.Sym {
-		// more declarations at label than at goto.
-		// figure out if they are all types.
-		ts := to.Sym
-		ntt := nt
-		for ; ntt > nf; ntt-- {
-			if ts.whyPushed != OTYPE {
-				break
-			}
-			ts = ts.Link
-		}
-		// all types, nothing to see here.
-		if ntt == nf {
-			return
-		}
-
 		lno := int(lineno)
 		setlineno(from)
 
@@ -183,11 +168,11 @@ func checkgoto(from *Node, to *Node) {
 		var block *Sym
 
 		var dcl *Sym
-		ts = to.Sym
+		ts := to.Sym
 		for ; nt > nf; nt-- {
 			if ts.Pkg == nil {
 				block = ts
-			} else if ts.whyPushed != OTYPE {
+			} else {
 				dcl = ts
 			}
 			ts = ts.Link
@@ -196,7 +181,7 @@ func checkgoto(from *Node, to *Node) {
 		for ts != fs {
 			if ts.Pkg == nil {
 				block = ts
-			} else if ts.whyPushed != OTYPE {
+			} else {
 				dcl = ts
 			}
 			ts = ts.Link
diff --git a/src/cmd/internal/gc/go.go b/src/cmd/internal/gc/go.go
index 4800218c95..31692bdf00 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/internal/gc/go.go
@@ -111,7 +111,6 @@ type Sym struct {
 	Uniqgen   uint32
 	Importdef *Pkg   // where imported definition was found
 	Linkname  string // link name
-	whyPushed uint8  // why this symbol pushed onto dclstack. Same as Node.Op. Used by goto validation
 
 	// saved and restored by dcopy
 	Pkg        *Pkg
diff --git a/test/goto.go b/test/goto.go
index c626f3d1c1..ca477b3d0c 100644
--- a/test/goto.go
+++ b/test/goto.go
@@ -536,19 +536,3 @@ func _() {
 		goto L // ERROR "goto L jumps into block starting at LINE-4|goto jumps into block"
 	}
 }
-
-// issue 8042
-func _() {
-	goto L
-	type a int
-	L:
-}
-
-// make sure we only complain about variable declarations.
-func _() {
-	goto L // ERROR "goto L jumps over declaration of x at LINE+2|goto jumps over declaration"
-	type a int
-	x := 1	// GCCGO_ERROR "defined here"
-	_ = x
-L:
-}

From d4ed30612c9eead7b1d59179cae5d468fb900f35 Mon Sep 17 00:00:00 2001
From: Brad Fitzpatrick 
Date: Fri, 15 May 2015 08:45:16 -0700
Subject: [PATCH 111/232] syscall: don't run fcntl child process test on iOS

Fixes darwin-arm{,64} builds.
Child processes aren't allowed on iOS.

Change-Id: I9258ed4df757ec394ef6327dbda96f5b9705bcdd
Reviewed-on: https://go-review.googlesource.com/10142
Reviewed-by: Hyang-Ah Hana Kim 
Run-TryBot: Brad Fitzpatrick 
---
 src/syscall/syscall_unix_test.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/syscall/syscall_unix_test.go b/src/syscall/syscall_unix_test.go
index 90fd276f82..af92013739 100644
--- a/src/syscall/syscall_unix_test.go
+++ b/src/syscall/syscall_unix_test.go
@@ -67,6 +67,9 @@ func _() {
 // Thus this test also verifies that the Flock_t structure can be
 // roundtripped with F_SETLK and F_GETLK.
 func TestFcntlFlock(t *testing.T) {
+	if runtime.GOOS == "darwin" && (runtime.GOARCH == "arm" || runtime.GOARCH == "arm64") {
+		t.Skip("skipping; no child processes allowed on iOS")
+	}
 	flock := syscall.Flock_t{
 		Type:  syscall.F_WRLCK,
 		Start: 31415, Len: 271828, Whence: 1,

From 82e1651a246db496fa9598f46a6c6af999b699ba Mon Sep 17 00:00:00 2001
From: Matthew Dempsky 
Date: Tue, 7 Apr 2015 11:57:52 -0700
Subject: [PATCH 112/232] cmd/internal/gc, cmd/yacc: merge yaccerrors.go into
 cmd/yacc

This extends cmd/yacc with support for

	%error { tokens } : message

syntax to specify custom error messages to use instead of the default
generic ones.  This allows merging go.errors into go.y and removing
the yaccerrors.go tool.

Updates #9968.

Change-Id: I781219c568b86472755f877f48401eaeab00ead5
Reviewed-on: https://go-review.googlesource.com/8563
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/go.errors     |  81 ---
 src/cmd/internal/gc/go.y          |  63 +-
 src/cmd/internal/gc/lex.go        |   1 -
 src/cmd/internal/gc/subr.go       |  15 -
 src/cmd/internal/gc/y.go          | 584 +++++++++---------
 src/cmd/internal/gc/y.output      | 972 +++++++++++++++---------------
 src/cmd/internal/gc/yaccerrors.go | 194 ------
 src/cmd/internal/gc/yymsg.go      |  83 ---
 src/cmd/yacc/yacc.go              | 128 +++-
 9 files changed, 981 insertions(+), 1140 deletions(-)
 delete mode 100644 src/cmd/internal/gc/go.errors
 delete mode 100644 src/cmd/internal/gc/yaccerrors.go
 delete mode 100644 src/cmd/internal/gc/yymsg.go

diff --git a/src/cmd/internal/gc/go.errors b/src/cmd/internal/gc/go.errors
deleted file mode 100644
index 8370a2007d..0000000000
--- a/src/cmd/internal/gc/go.errors
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright 2010 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Example-based syntax error messages.
-// See yaccerrors.go.
-
-package gc
-
-var yymsg = []struct {
-	yystate int
-	yychar  int
-	msg     string
-}{
-	// Each line of the form % token list
-	// is converted by yaccerrors.go into the yystate and yychar caused
-	// by that token list.
-
-	% loadsys package LIMPORT '(' LLITERAL import_package import_there ','
-		"unexpected comma during import block"},
-
-	% loadsys package LIMPORT LNAME ';'
-		"missing import path; require quoted string"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';'
-		"missing { after if clause"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';'
-		"missing { after switch clause"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';'
-		"missing { after for clause"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY
-		"missing { after for clause"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' ';' '{'
-		"unexpected semicolon or newline before {"},
-
-	% loadsys package imports LTYPE LNAME ';'
-		"unexpected semicolon or newline in type declaration"},
-
-	% loadsys package imports LCHAN '}'
-		"unexpected } in channel type"},
-
-	% loadsys package imports LCHAN ')'
-		"unexpected ) in channel type"},
-
-	% loadsys package imports LCHAN ','
-		"unexpected comma in channel type"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE
-		"unexpected semicolon or newline before else"},
-
-	% loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME
-		"name list not allowed in interface type"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME
-		"var declaration not allowed in for initializer"},
-
-	% loadsys package imports LVAR LNAME '[' ']' LNAME '{'
-		"unexpected { at end of statement"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{'
-		"unexpected { at end of statement"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';'
-		"argument to go/defer must be function call"},
-
-	% loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';'
-		"need trailing comma before newline in composite literal"},
-
-	% loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';'
-		"need trailing comma before newline in composite literal"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME
-		"nested func not allowed"},
-
-	% loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';'
-		"else must be followed by if or statement block"},
-}
diff --git a/src/cmd/internal/gc/go.y b/src/cmd/internal/gc/go.y
index e2e4331a77..7d523ae7c0 100644
--- a/src/cmd/internal/gc/go.y
+++ b/src/cmd/internal/gc/go.y
@@ -117,7 +117,68 @@ import (
 %left		')'
 %left		PreferToRightParen
 
-// TODO(rsc): Add %error-verbose
+%error loadsys package LIMPORT '(' LLITERAL import_package import_there ',':
+	"unexpected comma during import block"
+
+%error loadsys package LIMPORT LNAME ';':
+	"missing import path; require quoted string"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header ';':
+	"missing { after if clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LSWITCH if_header ';':
+	"missing { after switch clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR for_header ';':
+	"missing { after for clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR ';' LBODY:
+	"missing { after for clause"
+
+%error loadsys package imports LFUNC LNAME '(' ')' ';' '{':
+	"unexpected semicolon or newline before {"
+
+%error loadsys package imports LTYPE LNAME ';':
+	"unexpected semicolon or newline in type declaration"
+
+%error loadsys package imports LCHAN '}':
+	"unexpected } in channel type"
+
+%error loadsys package imports LCHAN ')':
+	"unexpected ) in channel type"
+
+%error loadsys package imports LCHAN ',':
+	"unexpected comma in channel type"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' if_stmt ';' LELSE:
+	"unexpected semicolon or newline before else"
+
+%error loadsys package imports LTYPE LNAME LINTERFACE '{' LNAME ',' LNAME:
+	"name list not allowed in interface type"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFOR LVAR LNAME '=' LNAME:
+	"var declaration not allowed in for initializer"
+
+%error loadsys package imports LVAR LNAME '[' ']' LNAME '{':
+	"unexpected { at end of statement"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LVAR LNAME '[' ']' LNAME '{':
+	"unexpected { at end of statement"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LDEFER LNAME ';':
+	"argument to go/defer must be function call"
+
+%error loadsys package imports LVAR LNAME '=' LNAME '{' LNAME ';':
+	"need trailing comma before newline in composite literal"
+
+%error loadsys package imports LVAR LNAME '=' comptype '{' LNAME ';':
+	"need trailing comma before newline in composite literal"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LFUNC LNAME:
+	"nested func not allowed"
+
+%error loadsys package imports LFUNC LNAME '(' ')' '{' LIF if_header loop_body LELSE ';':
+	"else must be followed by if or statement block"
 
 %%
 file:
diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/internal/gc/lex.go
index 1bf0758000..9e2baec220 100644
--- a/src/cmd/internal/gc/lex.go
+++ b/src/cmd/internal/gc/lex.go
@@ -3,7 +3,6 @@
 // license that can be found in the LICENSE file.
 
 //go:generate go tool yacc go.y
-//go:generate go run yaccerrors.go
 //go:generate go run mkbuiltin.go runtime unsafe
 
 package gc
diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/internal/gc/subr.go
index 7f9e78810f..74415be49a 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/internal/gc/subr.go
@@ -125,13 +125,6 @@ func Yyerror(format string, args ...interface{}) {
 	if strings.HasPrefix(msg, "syntax error") {
 		nsyntaxerrors++
 
-		yystate := theparser.(*yyParserImpl).state()
-		yychar := theparser.Lookahead()
-
-		if Debug['x'] != 0 {
-			fmt.Printf("yyerror: yystate=%d yychar=%d\n", yystate, yychar)
-		}
-
 		// An unexpected EOF caused a syntax error. Use the previous
 		// line number since getc generated a fake newline character.
 		if curio.eofnl != 0 {
@@ -144,14 +137,6 @@ func Yyerror(format string, args ...interface{}) {
 		}
 		yyerror_lastsyntax = int(lexlineno)
 
-		// look for parse state-specific errors in list (see go.errors).
-		for i := range yymsg {
-			if yymsg[i].yystate == yystate && yymsg[i].yychar == yychar {
-				yyerrorl(int(lexlineno), "syntax error: %s", yymsg[i].msg)
-				return
-			}
-		}
-
 		// plain "syntax error" gets "near foo" added
 		if msg == "syntax error" {
 			yyerrorl(int(lexlineno), "syntax error near %s", lexbuf.String())
diff --git a/src/cmd/internal/gc/y.go b/src/cmd/internal/gc/y.go
index 06f6b13eb6..72bce9a465 100644
--- a/src/cmd/internal/gc/y.go
+++ b/src/cmd/internal/gc/y.go
@@ -154,7 +154,7 @@ const yyEofCode = 1
 const yyErrCode = 2
 const yyMaxDepth = 200
 
-//line go.y:2243
+//line go.y:2304
 func fixlbrace(lbr int) {
 	// If the opening brace was an LBODY,
 	// set up for another one now that we're done.
@@ -857,6 +857,34 @@ var yyTok3 = [...]int{
 	0,
 }
 
+var yyErrorMessages = [...]struct {
+	state int
+	token int
+	msg   string
+}{
+	{332, 76, "unexpected comma during import block"},
+	{89, 63, "missing import path; require quoted string"},
+	{390, 63, "missing { after if clause"},
+	{387, 63, "missing { after switch clause"},
+	{279, 63, "missing { after for clause"},
+	{498, 36, "missing { after for clause"},
+	{17, 68, "unexpected semicolon or newline before {"},
+	{111, 63, "unexpected semicolon or newline in type declaration"},
+	{78, 69, "unexpected } in channel type"},
+	{78, 61, "unexpected ) in channel type"},
+	{78, 76, "unexpected comma in channel type"},
+	{416, 15, "unexpected semicolon or newline before else"},
+	{329, 76, "name list not allowed in interface type"},
+	{279, 33, "var declaration not allowed in for initializer"},
+	{25, 68, "unexpected { at end of statement"},
+	{371, 68, "unexpected { at end of statement"},
+	{122, 63, "argument to go/defer must be function call"},
+	{398, 63, "need trailing comma before newline in composite literal"},
+	{414, 63, "need trailing comma before newline in composite literal"},
+	{124, 25, "nested func not allowed"},
+	{650, 63, "else must be followed by if or statement block"},
+}
+
 //line yaccpar:1
 
 /*	parser for yacc output	*/
@@ -878,7 +906,6 @@ type yyParser interface {
 
 type yyParserImpl struct {
 	lookahead func() int
-	state     func() int
 }
 
 func (p *yyParserImpl) Lookahead() int {
@@ -888,7 +915,6 @@ func (p *yyParserImpl) Lookahead() int {
 func yyNewParser() yyParser {
 	p := &yyParserImpl{
 		lookahead: func() int { return -1 },
-		state:     func() int { return -1 },
 	}
 	return p
 }
@@ -919,6 +945,13 @@ func yyErrorMessage(state, lookAhead int) string {
 	if !yyErrorVerbose {
 		return "syntax error"
 	}
+
+	for _, e := range yyErrorMessages {
+		if e.state == state && e.token == lookAhead {
+			return "syntax error: " + e.msg
+		}
+	}
+
 	res := "syntax error: unexpected " + yyTokname(lookAhead)
 
 	// To match Bison, suggest at most four expected tokens.
@@ -1021,7 +1054,6 @@ func (yyrcvr *yyParserImpl) Parse(yylex yyLexer) int {
 	yystate := 0
 	yychar := -1
 	yytoken := -1 // yychar translated into internal numbering
-	yyrcvr.state = func() int { return yystate }
 	yyrcvr.lookahead = func() int { return yychar }
 	defer func() {
 		// Make sure we report no lookahead when not parsing.
@@ -1188,13 +1220,13 @@ yydefault:
 
 	case 1:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:128
+		//line go.y:189
 		{
 			xtop = concat(xtop, yyDollar[4].list)
 		}
 	case 2:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:134
+		//line go.y:195
 		{
 			prevlineno = lineno
 			Yyerror("package statement must be first")
@@ -1202,13 +1234,13 @@ yydefault:
 		}
 	case 3:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:140
+		//line go.y:201
 		{
 			mkpackage(yyDollar[2].sym.Name)
 		}
 	case 4:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:150
+		//line go.y:211
 		{
 			importpkg = Runtimepkg
 
@@ -1221,13 +1253,13 @@ yydefault:
 		}
 	case 5:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:162
+		//line go.y:223
 		{
 			importpkg = nil
 		}
 	case 11:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:176
+		//line go.y:237
 		{
 			ipkg := importpkg
 			my := importmyname
@@ -1264,7 +1296,7 @@ yydefault:
 		}
 	case 12:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:211
+		//line go.y:272
 		{
 			// When an invalid import path is passed to importfile,
 			// it calls Yyerror and then sets up a fake import with
@@ -1276,7 +1308,7 @@ yydefault:
 		}
 	case 15:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:227
+		//line go.y:288
 		{
 			// import with original name
 			yyVAL.i = parserline()
@@ -1285,7 +1317,7 @@ yydefault:
 		}
 	case 16:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:234
+		//line go.y:295
 		{
 			// import with given name
 			yyVAL.i = parserline()
@@ -1294,7 +1326,7 @@ yydefault:
 		}
 	case 17:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:241
+		//line go.y:302
 		{
 			// import into my name space
 			yyVAL.i = parserline()
@@ -1303,7 +1335,7 @@ yydefault:
 		}
 	case 18:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:250
+		//line go.y:311
 		{
 			if importpkg.Name == "" {
 				importpkg.Name = yyDollar[2].sym.Name
@@ -1320,7 +1352,7 @@ yydefault:
 		}
 	case 20:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:267
+		//line go.y:328
 		{
 			if yyDollar[1].sym.Name == "safe" {
 				curio.importsafe = true
@@ -1328,64 +1360,64 @@ yydefault:
 		}
 	case 21:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:274
+		//line go.y:335
 		{
 			defercheckwidth()
 		}
 	case 22:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:278
+		//line go.y:339
 		{
 			resumecheckwidth()
 			unimportfile()
 		}
 	case 23:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:287
+		//line go.y:348
 		{
 			Yyerror("empty top-level declaration")
 			yyVAL.list = nil
 		}
 	case 25:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:293
+		//line go.y:354
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 26:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:297
+		//line go.y:358
 		{
 			Yyerror("non-declaration statement outside function body")
 			yyVAL.list = nil
 		}
 	case 27:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:302
+		//line go.y:363
 		{
 			yyVAL.list = nil
 		}
 	case 28:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:308
+		//line go.y:369
 		{
 			yyVAL.list = yyDollar[2].list
 		}
 	case 29:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:312
+		//line go.y:373
 		{
 			yyVAL.list = yyDollar[3].list
 		}
 	case 30:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:316
+		//line go.y:377
 		{
 			yyVAL.list = nil
 		}
 	case 31:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:320
+		//line go.y:381
 		{
 			yyVAL.list = yyDollar[2].list
 			iota_ = -100000
@@ -1393,7 +1425,7 @@ yydefault:
 		}
 	case 32:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:326
+		//line go.y:387
 		{
 			yyVAL.list = yyDollar[3].list
 			iota_ = -100000
@@ -1401,7 +1433,7 @@ yydefault:
 		}
 	case 33:
 		yyDollar = yyS[yypt-7 : yypt+1]
-		//line go.y:332
+		//line go.y:393
 		{
 			yyVAL.list = concat(yyDollar[3].list, yyDollar[5].list)
 			iota_ = -100000
@@ -1409,80 +1441,80 @@ yydefault:
 		}
 	case 34:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:338
+		//line go.y:399
 		{
 			yyVAL.list = nil
 			iota_ = -100000
 		}
 	case 35:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:343
+		//line go.y:404
 		{
 			yyVAL.list = list1(yyDollar[2].node)
 		}
 	case 36:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:347
+		//line go.y:408
 		{
 			yyVAL.list = yyDollar[3].list
 		}
 	case 37:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:351
+		//line go.y:412
 		{
 			yyVAL.list = nil
 		}
 	case 38:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:357
+		//line go.y:418
 		{
 			iota_ = 0
 		}
 	case 39:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:363
+		//line go.y:424
 		{
 			yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, nil)
 		}
 	case 40:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:367
+		//line go.y:428
 		{
 			yyVAL.list = variter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list)
 		}
 	case 41:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:371
+		//line go.y:432
 		{
 			yyVAL.list = variter(yyDollar[1].list, nil, yyDollar[3].list)
 		}
 	case 42:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:377
+		//line go.y:438
 		{
 			yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, yyDollar[4].list)
 		}
 	case 43:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:381
+		//line go.y:442
 		{
 			yyVAL.list = constiter(yyDollar[1].list, nil, yyDollar[3].list)
 		}
 	case 45:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:388
+		//line go.y:449
 		{
 			yyVAL.list = constiter(yyDollar[1].list, yyDollar[2].node, nil)
 		}
 	case 46:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:392
+		//line go.y:453
 		{
 			yyVAL.list = constiter(yyDollar[1].list, nil, nil)
 		}
 	case 47:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:398
+		//line go.y:459
 		{
 			// different from dclname because the name
 			// becomes visible right here, not at the end
@@ -1491,13 +1523,13 @@ yydefault:
 		}
 	case 48:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:407
+		//line go.y:468
 		{
 			yyVAL.node = typedcl1(yyDollar[1].node, yyDollar[2].node, true)
 		}
 	case 49:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:413
+		//line go.y:474
 		{
 			yyVAL.node = yyDollar[1].node
 
@@ -1513,14 +1545,14 @@ yydefault:
 		}
 	case 50:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:427
+		//line go.y:488
 		{
 			yyVAL.node = Nod(OASOP, yyDollar[1].node, yyDollar[3].node)
 			yyVAL.node.Etype = uint8(yyDollar[2].i) // rathole to pass opcode
 		}
 	case 51:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:432
+		//line go.y:493
 		{
 			if yyDollar[1].list.Next == nil && yyDollar[3].list.Next == nil {
 				// simple
@@ -1534,7 +1566,7 @@ yydefault:
 		}
 	case 52:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:444
+		//line go.y:505
 		{
 			if yyDollar[3].list.N.Op == OTYPESW {
 				yyVAL.node = Nod(OTYPESW, nil, yyDollar[3].list.N.Right)
@@ -1554,7 +1586,7 @@ yydefault:
 		}
 	case 53:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:462
+		//line go.y:523
 		{
 			yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1))
 			yyVAL.node.Implicit = true
@@ -1562,7 +1594,7 @@ yydefault:
 		}
 	case 54:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:468
+		//line go.y:529
 		{
 			yyVAL.node = Nod(OASOP, yyDollar[1].node, Nodintconst(1))
 			yyVAL.node.Implicit = true
@@ -1570,7 +1602,7 @@ yydefault:
 		}
 	case 55:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:476
+		//line go.y:537
 		{
 			var n, nn *Node
 
@@ -1595,7 +1627,7 @@ yydefault:
 		}
 	case 56:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:499
+		//line go.y:560
 		{
 			var n *Node
 
@@ -1615,7 +1647,7 @@ yydefault:
 		}
 	case 57:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:517
+		//line go.y:578
 		{
 			// will be converted to OCASE
 			// right will point to next case
@@ -1626,7 +1658,7 @@ yydefault:
 		}
 	case 58:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:526
+		//line go.y:587
 		{
 			var n, nn *Node
 
@@ -1647,13 +1679,13 @@ yydefault:
 		}
 	case 59:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:547
+		//line go.y:608
 		{
 			markdcl()
 		}
 	case 60:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:551
+		//line go.y:612
 		{
 			if yyDollar[3].list == nil {
 				yyVAL.node = Nod(OEMPTY, nil, nil)
@@ -1664,7 +1696,7 @@ yydefault:
 		}
 	case 61:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:562
+		//line go.y:623
 		{
 			// If the last token read by the lexer was consumed
 			// as part of the case, clear it (parser has cleared yychar).
@@ -1677,7 +1709,7 @@ yydefault:
 		}
 	case 62:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:573
+		//line go.y:634
 		{
 			// This is the only place in the language where a statement
 			// list is not allowed to drop the final semicolon, because
@@ -1697,32 +1729,32 @@ yydefault:
 		}
 	case 63:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:592
+		//line go.y:653
 		{
 			yyVAL.list = nil
 		}
 	case 64:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:596
+		//line go.y:657
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[2].node)
 		}
 	case 65:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:602
+		//line go.y:663
 		{
 			markdcl()
 		}
 	case 66:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:606
+		//line go.y:667
 		{
 			yyVAL.list = yyDollar[3].list
 			popdcl()
 		}
 	case 67:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:613
+		//line go.y:674
 		{
 			yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node)
 			yyVAL.node.List = yyDollar[1].list
@@ -1730,7 +1762,7 @@ yydefault:
 		}
 	case 68:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:619
+		//line go.y:680
 		{
 			yyVAL.node = Nod(ORANGE, nil, yyDollar[4].node)
 			yyVAL.node.List = yyDollar[1].list
@@ -1739,14 +1771,14 @@ yydefault:
 		}
 	case 69:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:626
+		//line go.y:687
 		{
 			yyVAL.node = Nod(ORANGE, nil, yyDollar[2].node)
 			yyVAL.node.Etype = 0 // := flag
 		}
 	case 70:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:633
+		//line go.y:694
 		{
 			// init ; test ; incr
 			if yyDollar[5].node != nil && yyDollar[5].node.Colas {
@@ -1761,7 +1793,7 @@ yydefault:
 		}
 	case 71:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:646
+		//line go.y:707
 		{
 			// normal test
 			yyVAL.node = Nod(OFOR, nil, nil)
@@ -1769,27 +1801,27 @@ yydefault:
 		}
 	case 73:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:655
+		//line go.y:716
 		{
 			yyVAL.node = yyDollar[1].node
 			yyVAL.node.Nbody = concat(yyVAL.node.Nbody, yyDollar[2].list)
 		}
 	case 74:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:662
+		//line go.y:723
 		{
 			markdcl()
 		}
 	case 75:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:666
+		//line go.y:727
 		{
 			yyVAL.node = yyDollar[3].node
 			popdcl()
 		}
 	case 76:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:673
+		//line go.y:734
 		{
 			// test
 			yyVAL.node = Nod(OIF, nil, nil)
@@ -1797,7 +1829,7 @@ yydefault:
 		}
 	case 77:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:679
+		//line go.y:740
 		{
 			// init ; test
 			yyVAL.node = Nod(OIF, nil, nil)
@@ -1808,13 +1840,13 @@ yydefault:
 		}
 	case 78:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:691
+		//line go.y:752
 		{
 			markdcl()
 		}
 	case 79:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:695
+		//line go.y:756
 		{
 			if yyDollar[3].node.Ntest == nil {
 				Yyerror("missing condition in if statement")
@@ -1822,13 +1854,13 @@ yydefault:
 		}
 	case 80:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:701
+		//line go.y:762
 		{
 			yyDollar[3].node.Nbody = yyDollar[5].list
 		}
 	case 81:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:705
+		//line go.y:766
 		{
 			var n *Node
 			var nn *NodeList
@@ -1846,13 +1878,13 @@ yydefault:
 		}
 	case 82:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:723
+		//line go.y:784
 		{
 			markdcl()
 		}
 	case 83:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:727
+		//line go.y:788
 		{
 			if yyDollar[4].node.Ntest == nil {
 				Yyerror("missing condition in if statement")
@@ -1862,25 +1894,25 @@ yydefault:
 		}
 	case 84:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:736
+		//line go.y:797
 		{
 			yyVAL.list = nil
 		}
 	case 85:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:740
+		//line go.y:801
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list)
 		}
 	case 86:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:745
+		//line go.y:806
 		{
 			yyVAL.list = nil
 		}
 	case 87:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:749
+		//line go.y:810
 		{
 			l := &NodeList{N: yyDollar[2].node}
 			l.End = l
@@ -1888,13 +1920,13 @@ yydefault:
 		}
 	case 88:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:757
+		//line go.y:818
 		{
 			markdcl()
 		}
 	case 89:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:761
+		//line go.y:822
 		{
 			var n *Node
 			n = yyDollar[3].node.Ntest
@@ -1905,7 +1937,7 @@ yydefault:
 		}
 	case 90:
 		yyDollar = yyS[yypt-7 : yypt+1]
-		//line go.y:770
+		//line go.y:831
 		{
 			yyVAL.node = yyDollar[3].node
 			yyVAL.node.Op = OSWITCH
@@ -1915,13 +1947,13 @@ yydefault:
 		}
 	case 91:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:780
+		//line go.y:841
 		{
 			typesw = Nod(OXXX, typesw, nil)
 		}
 	case 92:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:784
+		//line go.y:845
 		{
 			yyVAL.node = Nod(OSELECT, nil, nil)
 			yyVAL.node.Lineno = typesw.Lineno
@@ -1930,133 +1962,133 @@ yydefault:
 		}
 	case 94:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:797
+		//line go.y:858
 		{
 			yyVAL.node = Nod(OOROR, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 95:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:801
+		//line go.y:862
 		{
 			yyVAL.node = Nod(OANDAND, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 96:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:805
+		//line go.y:866
 		{
 			yyVAL.node = Nod(OEQ, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 97:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:809
+		//line go.y:870
 		{
 			yyVAL.node = Nod(ONE, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 98:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:813
+		//line go.y:874
 		{
 			yyVAL.node = Nod(OLT, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 99:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:817
+		//line go.y:878
 		{
 			yyVAL.node = Nod(OLE, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 100:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:821
+		//line go.y:882
 		{
 			yyVAL.node = Nod(OGE, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 101:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:825
+		//line go.y:886
 		{
 			yyVAL.node = Nod(OGT, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 102:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:829
+		//line go.y:890
 		{
 			yyVAL.node = Nod(OADD, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 103:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:833
+		//line go.y:894
 		{
 			yyVAL.node = Nod(OSUB, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 104:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:837
+		//line go.y:898
 		{
 			yyVAL.node = Nod(OOR, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 105:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:841
+		//line go.y:902
 		{
 			yyVAL.node = Nod(OXOR, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 106:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:845
+		//line go.y:906
 		{
 			yyVAL.node = Nod(OMUL, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 107:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:849
+		//line go.y:910
 		{
 			yyVAL.node = Nod(ODIV, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 108:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:853
+		//line go.y:914
 		{
 			yyVAL.node = Nod(OMOD, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 109:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:857
+		//line go.y:918
 		{
 			yyVAL.node = Nod(OAND, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 110:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:861
+		//line go.y:922
 		{
 			yyVAL.node = Nod(OANDNOT, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 111:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:865
+		//line go.y:926
 		{
 			yyVAL.node = Nod(OLSH, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 112:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:869
+		//line go.y:930
 		{
 			yyVAL.node = Nod(ORSH, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 113:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:874
+		//line go.y:935
 		{
 			yyVAL.node = Nod(OSEND, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 115:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:881
+		//line go.y:942
 		{
 			yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
 		}
 	case 116:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:885
+		//line go.y:946
 		{
 			if yyDollar[2].node.Op == OCOMPLIT {
 				// Special case for &T{...}: turn into (*T){...}.
@@ -2069,57 +2101,57 @@ yydefault:
 		}
 	case 117:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:896
+		//line go.y:957
 		{
 			yyVAL.node = Nod(OPLUS, yyDollar[2].node, nil)
 		}
 	case 118:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:900
+		//line go.y:961
 		{
 			yyVAL.node = Nod(OMINUS, yyDollar[2].node, nil)
 		}
 	case 119:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:904
+		//line go.y:965
 		{
 			yyVAL.node = Nod(ONOT, yyDollar[2].node, nil)
 		}
 	case 120:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:908
+		//line go.y:969
 		{
 			Yyerror("the bitwise complement operator is ^")
 			yyVAL.node = Nod(OCOM, yyDollar[2].node, nil)
 		}
 	case 121:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:913
+		//line go.y:974
 		{
 			yyVAL.node = Nod(OCOM, yyDollar[2].node, nil)
 		}
 	case 122:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:917
+		//line go.y:978
 		{
 			yyVAL.node = Nod(ORECV, yyDollar[2].node, nil)
 		}
 	case 123:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:927
+		//line go.y:988
 		{
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
 		}
 	case 124:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:931
+		//line go.y:992
 		{
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
 			yyVAL.node.List = yyDollar[3].list
 		}
 	case 125:
 		yyDollar = yyS[yypt-6 : yypt+1]
-		//line go.y:936
+		//line go.y:997
 		{
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
 			yyVAL.node.List = yyDollar[3].list
@@ -2127,13 +2159,13 @@ yydefault:
 		}
 	case 126:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:944
+		//line go.y:1005
 		{
 			yyVAL.node = nodlit(yyDollar[1].val)
 		}
 	case 128:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:949
+		//line go.y:1010
 		{
 			if yyDollar[1].node.Op == OPACK {
 				var s *Sym
@@ -2146,31 +2178,31 @@ yydefault:
 		}
 	case 129:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:960
+		//line go.y:1021
 		{
 			yyVAL.node = Nod(ODOTTYPE, yyDollar[1].node, yyDollar[4].node)
 		}
 	case 130:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:964
+		//line go.y:1025
 		{
 			yyVAL.node = Nod(OTYPESW, nil, yyDollar[1].node)
 		}
 	case 131:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:968
+		//line go.y:1029
 		{
 			yyVAL.node = Nod(OINDEX, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 132:
 		yyDollar = yyS[yypt-6 : yypt+1]
-		//line go.y:972
+		//line go.y:1033
 		{
 			yyVAL.node = Nod(OSLICE, yyDollar[1].node, Nod(OKEY, yyDollar[3].node, yyDollar[5].node))
 		}
 	case 133:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:976
+		//line go.y:1037
 		{
 			if yyDollar[5].node == nil {
 				Yyerror("middle index required in 3-index slice")
@@ -2182,7 +2214,7 @@ yydefault:
 		}
 	case 135:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:987
+		//line go.y:1048
 		{
 			// conversion
 			yyVAL.node = Nod(OCALL, yyDollar[1].node, nil)
@@ -2190,7 +2222,7 @@ yydefault:
 		}
 	case 136:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:993
+		//line go.y:1054
 		{
 			yyVAL.node = yyDollar[3].node
 			yyVAL.node.Right = yyDollar[1].node
@@ -2199,7 +2231,7 @@ yydefault:
 		}
 	case 137:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1000
+		//line go.y:1061
 		{
 			yyVAL.node = yyDollar[3].node
 			yyVAL.node.Right = yyDollar[1].node
@@ -2207,7 +2239,7 @@ yydefault:
 		}
 	case 138:
 		yyDollar = yyS[yypt-7 : yypt+1]
-		//line go.y:1006
+		//line go.y:1067
 		{
 			Yyerror("cannot parenthesize type in composite literal")
 			yyVAL.node = yyDollar[5].node
@@ -2216,7 +2248,7 @@ yydefault:
 		}
 	case 140:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1015
+		//line go.y:1076
 		{
 			// composite expression.
 			// make node early so we get the right line number.
@@ -2224,13 +2256,13 @@ yydefault:
 		}
 	case 141:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1023
+		//line go.y:1084
 		{
 			yyVAL.node = Nod(OKEY, yyDollar[1].node, yyDollar[3].node)
 		}
 	case 142:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1029
+		//line go.y:1090
 		{
 			// These nodes do not carry line numbers.
 			// Since a composite literal commonly spans several lines,
@@ -2245,21 +2277,21 @@ yydefault:
 		}
 	case 143:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1042
+		//line go.y:1103
 		{
 			yyVAL.node = yyDollar[2].node
 			yyVAL.node.List = yyDollar[3].list
 		}
 	case 145:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1050
+		//line go.y:1111
 		{
 			yyVAL.node = yyDollar[2].node
 			yyVAL.node.List = yyDollar[3].list
 		}
 	case 147:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1058
+		//line go.y:1119
 		{
 			yyVAL.node = yyDollar[2].node
 
@@ -2273,19 +2305,19 @@ yydefault:
 		}
 	case 151:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1079
+		//line go.y:1140
 		{
 			yyVAL.i = LBODY
 		}
 	case 152:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1083
+		//line go.y:1144
 		{
 			yyVAL.i = '{'
 		}
 	case 153:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1094
+		//line go.y:1155
 		{
 			if yyDollar[1].sym == nil {
 				yyVAL.node = nil
@@ -2295,19 +2327,19 @@ yydefault:
 		}
 	case 154:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1104
+		//line go.y:1165
 		{
 			yyVAL.node = dclname(yyDollar[1].sym)
 		}
 	case 155:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1109
+		//line go.y:1170
 		{
 			yyVAL.node = nil
 		}
 	case 157:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1116
+		//line go.y:1177
 		{
 			yyVAL.sym = yyDollar[1].sym
 			// during imports, unqualified non-exported identifiers are from builtinpkg
@@ -2317,13 +2349,13 @@ yydefault:
 		}
 	case 159:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1125
+		//line go.y:1186
 		{
 			yyVAL.sym = nil
 		}
 	case 160:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1131
+		//line go.y:1192
 		{
 			var p *Pkg
 
@@ -2339,7 +2371,7 @@ yydefault:
 		}
 	case 161:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1145
+		//line go.y:1206
 		{
 			var p *Pkg
 
@@ -2355,7 +2387,7 @@ yydefault:
 		}
 	case 162:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1161
+		//line go.y:1222
 		{
 			yyVAL.node = oldname(yyDollar[1].sym)
 			if yyVAL.node.Pack != nil {
@@ -2364,38 +2396,38 @@ yydefault:
 		}
 	case 164:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1182
+		//line go.y:1243
 		{
 			Yyerror("final argument in variadic function missing type")
 			yyVAL.node = Nod(ODDD, typenod(typ(TINTER)), nil)
 		}
 	case 165:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1187
+		//line go.y:1248
 		{
 			yyVAL.node = Nod(ODDD, yyDollar[2].node, nil)
 		}
 	case 171:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1198
+		//line go.y:1259
 		{
 			yyVAL.node = yyDollar[2].node
 		}
 	case 175:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1207
+		//line go.y:1268
 		{
 			yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
 		}
 	case 180:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1217
+		//line go.y:1278
 		{
 			yyVAL.node = yyDollar[2].node
 		}
 	case 190:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1238
+		//line go.y:1299
 		{
 			if yyDollar[1].node.Op == OPACK {
 				var s *Sym
@@ -2408,53 +2440,53 @@ yydefault:
 		}
 	case 191:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1251
+		//line go.y:1312
 		{
 			yyVAL.node = Nod(OTARRAY, yyDollar[2].node, yyDollar[4].node)
 		}
 	case 192:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1255
+		//line go.y:1316
 		{
 			// array literal of nelem
 			yyVAL.node = Nod(OTARRAY, Nod(ODDD, nil, nil), yyDollar[4].node)
 		}
 	case 193:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1260
+		//line go.y:1321
 		{
 			yyVAL.node = Nod(OTCHAN, yyDollar[2].node, nil)
 			yyVAL.node.Etype = Cboth
 		}
 	case 194:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1265
+		//line go.y:1326
 		{
 			yyVAL.node = Nod(OTCHAN, yyDollar[3].node, nil)
 			yyVAL.node.Etype = Csend
 		}
 	case 195:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1270
+		//line go.y:1331
 		{
 			yyVAL.node = Nod(OTMAP, yyDollar[3].node, yyDollar[5].node)
 		}
 	case 198:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1278
+		//line go.y:1339
 		{
 			yyVAL.node = Nod(OIND, yyDollar[2].node, nil)
 		}
 	case 199:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1284
+		//line go.y:1345
 		{
 			yyVAL.node = Nod(OTCHAN, yyDollar[3].node, nil)
 			yyVAL.node.Etype = Crecv
 		}
 	case 200:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1291
+		//line go.y:1352
 		{
 			yyVAL.node = Nod(OTSTRUCT, nil, nil)
 			yyVAL.node.List = yyDollar[3].list
@@ -2462,14 +2494,14 @@ yydefault:
 		}
 	case 201:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1297
+		//line go.y:1358
 		{
 			yyVAL.node = Nod(OTSTRUCT, nil, nil)
 			fixlbrace(yyDollar[2].i)
 		}
 	case 202:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1304
+		//line go.y:1365
 		{
 			yyVAL.node = Nod(OTINTER, nil, nil)
 			yyVAL.node.List = yyDollar[3].list
@@ -2477,14 +2509,14 @@ yydefault:
 		}
 	case 203:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1310
+		//line go.y:1371
 		{
 			yyVAL.node = Nod(OTINTER, nil, nil)
 			fixlbrace(yyDollar[2].i)
 		}
 	case 204:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1321
+		//line go.y:1382
 		{
 			yyVAL.node = yyDollar[2].node
 			if yyVAL.node == nil {
@@ -2502,7 +2534,7 @@ yydefault:
 		}
 	case 205:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1339
+		//line go.y:1400
 		{
 			var t *Node
 
@@ -2535,7 +2567,7 @@ yydefault:
 		}
 	case 206:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:1370
+		//line go.y:1431
 		{
 			var rcvr, t *Node
 
@@ -2573,7 +2605,7 @@ yydefault:
 		}
 	case 207:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1408
+		//line go.y:1469
 		{
 			var s *Sym
 			var t *Type
@@ -2600,7 +2632,7 @@ yydefault:
 		}
 	case 208:
 		yyDollar = yyS[yypt-8 : yypt+1]
-		//line go.y:1433
+		//line go.y:1494
 		{
 			yyVAL.node = methodname1(newname(yyDollar[4].sym), yyDollar[2].list.N.Right)
 			yyVAL.node.Type = functype(yyDollar[2].list.N, yyDollar[6].list, yyDollar[8].list)
@@ -2618,7 +2650,7 @@ yydefault:
 		}
 	case 209:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1451
+		//line go.y:1512
 		{
 			yyDollar[3].list = checkarglist(yyDollar[3].list, 1)
 			yyVAL.node = Nod(OTFUNC, nil, nil)
@@ -2627,13 +2659,13 @@ yydefault:
 		}
 	case 210:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1459
+		//line go.y:1520
 		{
 			yyVAL.list = nil
 		}
 	case 211:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1463
+		//line go.y:1524
 		{
 			yyVAL.list = yyDollar[2].list
 			if yyVAL.list == nil {
@@ -2642,51 +2674,51 @@ yydefault:
 		}
 	case 212:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1472
+		//line go.y:1533
 		{
 			yyVAL.list = nil
 		}
 	case 213:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1476
+		//line go.y:1537
 		{
 			yyVAL.list = list1(Nod(ODCLFIELD, nil, yyDollar[1].node))
 		}
 	case 214:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1480
+		//line go.y:1541
 		{
 			yyDollar[2].list = checkarglist(yyDollar[2].list, 0)
 			yyVAL.list = yyDollar[2].list
 		}
 	case 215:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1487
+		//line go.y:1548
 		{
 			closurehdr(yyDollar[1].node)
 		}
 	case 216:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1493
+		//line go.y:1554
 		{
 			yyVAL.node = closurebody(yyDollar[3].list)
 			fixlbrace(yyDollar[2].i)
 		}
 	case 217:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1498
+		//line go.y:1559
 		{
 			yyVAL.node = closurebody(nil)
 		}
 	case 218:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1509
+		//line go.y:1570
 		{
 			yyVAL.list = nil
 		}
 	case 219:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1513
+		//line go.y:1574
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[2].list)
 			if nsyntaxerrors == 0 {
@@ -2699,49 +2731,49 @@ yydefault:
 		}
 	case 221:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1527
+		//line go.y:1588
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
 		}
 	case 223:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1534
+		//line go.y:1595
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
 		}
 	case 224:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1540
+		//line go.y:1601
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 225:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1544
+		//line go.y:1605
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 227:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1551
+		//line go.y:1612
 		{
 			yyVAL.list = concat(yyDollar[1].list, yyDollar[3].list)
 		}
 	case 228:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1557
+		//line go.y:1618
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 229:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1561
+		//line go.y:1622
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 230:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1567
+		//line go.y:1628
 		{
 			var l *NodeList
 
@@ -2767,14 +2799,14 @@ yydefault:
 		}
 	case 231:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1591
+		//line go.y:1652
 		{
 			yyDollar[1].node.Val = yyDollar[2].val
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 232:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1596
+		//line go.y:1657
 		{
 			yyDollar[2].node.Val = yyDollar[4].val
 			yyVAL.list = list1(yyDollar[2].node)
@@ -2782,7 +2814,7 @@ yydefault:
 		}
 	case 233:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1602
+		//line go.y:1663
 		{
 			yyDollar[2].node.Right = Nod(OIND, yyDollar[2].node.Right, nil)
 			yyDollar[2].node.Val = yyDollar[3].val
@@ -2790,7 +2822,7 @@ yydefault:
 		}
 	case 234:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1608
+		//line go.y:1669
 		{
 			yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil)
 			yyDollar[3].node.Val = yyDollar[5].val
@@ -2799,7 +2831,7 @@ yydefault:
 		}
 	case 235:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1615
+		//line go.y:1676
 		{
 			yyDollar[3].node.Right = Nod(OIND, yyDollar[3].node.Right, nil)
 			yyDollar[3].node.Val = yyDollar[5].val
@@ -2808,7 +2840,7 @@ yydefault:
 		}
 	case 236:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1624
+		//line go.y:1685
 		{
 			var n *Node
 
@@ -2820,7 +2852,7 @@ yydefault:
 		}
 	case 237:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1634
+		//line go.y:1695
 		{
 			var pkg *Pkg
 
@@ -2835,33 +2867,33 @@ yydefault:
 		}
 	case 238:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1649
+		//line go.y:1710
 		{
 			yyVAL.node = embedded(yyDollar[1].sym, localpkg)
 		}
 	case 239:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1655
+		//line go.y:1716
 		{
 			yyVAL.node = Nod(ODCLFIELD, yyDollar[1].node, yyDollar[2].node)
 			ifacedcl(yyVAL.node)
 		}
 	case 240:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1660
+		//line go.y:1721
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[1].sym))
 		}
 	case 241:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1664
+		//line go.y:1725
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, oldname(yyDollar[2].sym))
 			Yyerror("cannot parenthesize embedded type")
 		}
 	case 242:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1671
+		//line go.y:1732
 		{
 			// without func keyword
 			yyDollar[2].list = checkarglist(yyDollar[2].list, 1)
@@ -2871,7 +2903,7 @@ yydefault:
 		}
 	case 244:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1685
+		//line go.y:1746
 		{
 			yyVAL.node = Nod(ONONAME, nil, nil)
 			yyVAL.node.Sym = yyDollar[1].sym
@@ -2879,7 +2911,7 @@ yydefault:
 		}
 	case 245:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1691
+		//line go.y:1752
 		{
 			yyVAL.node = Nod(ONONAME, nil, nil)
 			yyVAL.node.Sym = yyDollar[1].sym
@@ -2887,56 +2919,56 @@ yydefault:
 		}
 	case 247:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1700
+		//line go.y:1761
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 248:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1704
+		//line go.y:1765
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 249:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1709
+		//line go.y:1770
 		{
 			yyVAL.list = nil
 		}
 	case 250:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1713
+		//line go.y:1774
 		{
 			yyVAL.list = yyDollar[1].list
 		}
 	case 251:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1721
+		//line go.y:1782
 		{
 			yyVAL.node = nil
 		}
 	case 253:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1726
+		//line go.y:1787
 		{
 			yyVAL.node = liststmt(yyDollar[1].list)
 		}
 	case 255:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1731
+		//line go.y:1792
 		{
 			yyVAL.node = nil
 		}
 	case 261:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1742
+		//line go.y:1803
 		{
 			yyDollar[1].node = Nod(OLABEL, yyDollar[1].node, nil)
 			yyDollar[1].node.Sym = dclstack // context, for goto restrictions
 		}
 	case 262:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1747
+		//line go.y:1808
 		{
 			var l *NodeList
 
@@ -2949,7 +2981,7 @@ yydefault:
 		}
 	case 263:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1758
+		//line go.y:1819
 		{
 			// will be converted to OFALL
 			yyVAL.node = Nod(OXFALL, nil, nil)
@@ -2957,38 +2989,38 @@ yydefault:
 		}
 	case 264:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1764
+		//line go.y:1825
 		{
 			yyVAL.node = Nod(OBREAK, yyDollar[2].node, nil)
 		}
 	case 265:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1768
+		//line go.y:1829
 		{
 			yyVAL.node = Nod(OCONTINUE, yyDollar[2].node, nil)
 		}
 	case 266:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1772
+		//line go.y:1833
 		{
 			yyVAL.node = Nod(OPROC, yyDollar[2].node, nil)
 		}
 	case 267:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1776
+		//line go.y:1837
 		{
 			yyVAL.node = Nod(ODEFER, yyDollar[2].node, nil)
 		}
 	case 268:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1780
+		//line go.y:1841
 		{
 			yyVAL.node = Nod(OGOTO, yyDollar[2].node, nil)
 			yyVAL.node.Sym = dclstack // context, for goto restrictions
 		}
 	case 269:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1785
+		//line go.y:1846
 		{
 			yyVAL.node = Nod(ORETURN, nil, nil)
 			yyVAL.node.List = yyDollar[2].list
@@ -3010,7 +3042,7 @@ yydefault:
 		}
 	case 270:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1807
+		//line go.y:1868
 		{
 			yyVAL.list = nil
 			if yyDollar[1].node != nil {
@@ -3019,7 +3051,7 @@ yydefault:
 		}
 	case 271:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1814
+		//line go.y:1875
 		{
 			yyVAL.list = yyDollar[1].list
 			if yyDollar[3].node != nil {
@@ -3028,163 +3060,163 @@ yydefault:
 		}
 	case 272:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1823
+		//line go.y:1884
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 273:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1827
+		//line go.y:1888
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 274:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1833
+		//line go.y:1894
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 275:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1837
+		//line go.y:1898
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 276:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1843
+		//line go.y:1904
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 277:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1847
+		//line go.y:1908
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 278:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1853
+		//line go.y:1914
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 279:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1857
+		//line go.y:1918
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 280:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1866
+		//line go.y:1927
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 281:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1870
+		//line go.y:1931
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 282:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1874
+		//line go.y:1935
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 283:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:1878
+		//line go.y:1939
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 284:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1883
+		//line go.y:1944
 		{
 			yyVAL.list = nil
 		}
 	case 285:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:1887
+		//line go.y:1948
 		{
 			yyVAL.list = yyDollar[1].list
 		}
 	case 290:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1901
+		//line go.y:1962
 		{
 			yyVAL.node = nil
 		}
 	case 292:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1907
+		//line go.y:1968
 		{
 			yyVAL.list = nil
 		}
 	case 294:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1913
+		//line go.y:1974
 		{
 			yyVAL.node = nil
 		}
 	case 296:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1919
+		//line go.y:1980
 		{
 			yyVAL.list = nil
 		}
 	case 298:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1925
+		//line go.y:1986
 		{
 			yyVAL.list = nil
 		}
 	case 300:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1931
+		//line go.y:1992
 		{
 			yyVAL.list = nil
 		}
 	case 302:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:1937
+		//line go.y:1998
 		{
 			yyVAL.val.Ctype = CTxxx
 		}
 	case 304:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1947
+		//line go.y:2008
 		{
 			importimport(yyDollar[2].sym, yyDollar[3].val.U.(string))
 		}
 	case 305:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1951
+		//line go.y:2012
 		{
 			importvar(yyDollar[2].sym, yyDollar[3].typ)
 		}
 	case 306:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:1955
+		//line go.y:2016
 		{
 			importconst(yyDollar[2].sym, Types[TIDEAL], yyDollar[4].node)
 		}
 	case 307:
 		yyDollar = yyS[yypt-6 : yypt+1]
-		//line go.y:1959
+		//line go.y:2020
 		{
 			importconst(yyDollar[2].sym, yyDollar[3].typ, yyDollar[5].node)
 		}
 	case 308:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1963
+		//line go.y:2024
 		{
 			importtype(yyDollar[2].typ, yyDollar[3].typ)
 		}
 	case 309:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:1967
+		//line go.y:2028
 		{
 			if yyDollar[2].node == nil {
 				dclcontext = PEXTERN // since we skip the funcbody below
@@ -3205,27 +3237,27 @@ yydefault:
 		}
 	case 310:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1988
+		//line go.y:2049
 		{
 			yyVAL.sym = yyDollar[1].sym
 			structpkg = yyVAL.sym.Pkg
 		}
 	case 311:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:1995
+		//line go.y:2056
 		{
 			yyVAL.typ = pkgtype(yyDollar[1].sym)
 			importsym(yyDollar[1].sym, OTYPE)
 		}
 	case 317:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2015
+		//line go.y:2076
 		{
 			yyVAL.typ = pkgtype(yyDollar[1].sym)
 		}
 	case 318:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2019
+		//line go.y:2080
 		{
 			// predefined name like uint8
 			yyDollar[1].sym = Pkglookup(yyDollar[1].sym.Name, builtinpkg)
@@ -3238,43 +3270,43 @@ yydefault:
 		}
 	case 319:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2030
+		//line go.y:2091
 		{
 			yyVAL.typ = aindex(nil, yyDollar[3].typ)
 		}
 	case 320:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2034
+		//line go.y:2095
 		{
 			yyVAL.typ = aindex(nodlit(yyDollar[2].val), yyDollar[4].typ)
 		}
 	case 321:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2038
+		//line go.y:2099
 		{
 			yyVAL.typ = maptype(yyDollar[3].typ, yyDollar[5].typ)
 		}
 	case 322:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2042
+		//line go.y:2103
 		{
 			yyVAL.typ = tostruct(yyDollar[3].list)
 		}
 	case 323:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2046
+		//line go.y:2107
 		{
 			yyVAL.typ = tointerface(yyDollar[3].list)
 		}
 	case 324:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:2050
+		//line go.y:2111
 		{
 			yyVAL.typ = Ptrto(yyDollar[2].typ)
 		}
 	case 325:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:2054
+		//line go.y:2115
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[2].typ
@@ -3282,7 +3314,7 @@ yydefault:
 		}
 	case 326:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2060
+		//line go.y:2121
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[3].typ
@@ -3290,7 +3322,7 @@ yydefault:
 		}
 	case 327:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2066
+		//line go.y:2127
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[3].typ
@@ -3298,7 +3330,7 @@ yydefault:
 		}
 	case 328:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2074
+		//line go.y:2135
 		{
 			yyVAL.typ = typ(TCHAN)
 			yyVAL.typ.Type = yyDollar[3].typ
@@ -3306,13 +3338,13 @@ yydefault:
 		}
 	case 329:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2082
+		//line go.y:2143
 		{
 			yyVAL.typ = functype(nil, yyDollar[3].list, yyDollar[5].list)
 		}
 	case 330:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2088
+		//line go.y:2149
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[2].typ))
 			if yyDollar[1].sym != nil {
@@ -3322,7 +3354,7 @@ yydefault:
 		}
 	case 331:
 		yyDollar = yyS[yypt-4 : yypt+1]
-		//line go.y:2096
+		//line go.y:2157
 		{
 			var t *Type
 
@@ -3339,7 +3371,7 @@ yydefault:
 		}
 	case 332:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2113
+		//line go.y:2174
 		{
 			var s *Sym
 			var p *Pkg
@@ -3363,43 +3395,43 @@ yydefault:
 		}
 	case 333:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2137
+		//line go.y:2198
 		{
 			yyVAL.node = Nod(ODCLFIELD, newname(yyDollar[1].sym), typenod(functype(fakethis(), yyDollar[3].list, yyDollar[5].list)))
 		}
 	case 334:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2141
+		//line go.y:2202
 		{
 			yyVAL.node = Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ))
 		}
 	case 335:
 		yyDollar = yyS[yypt-0 : yypt+1]
-		//line go.y:2146
+		//line go.y:2207
 		{
 			yyVAL.list = nil
 		}
 	case 337:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2153
+		//line go.y:2214
 		{
 			yyVAL.list = yyDollar[2].list
 		}
 	case 338:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2157
+		//line go.y:2218
 		{
 			yyVAL.list = list1(Nod(ODCLFIELD, nil, typenod(yyDollar[1].typ)))
 		}
 	case 339:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2167
+		//line go.y:2228
 		{
 			yyVAL.node = nodlit(yyDollar[1].val)
 		}
 	case 340:
 		yyDollar = yyS[yypt-2 : yypt+1]
-		//line go.y:2171
+		//line go.y:2232
 		{
 			yyVAL.node = nodlit(yyDollar[2].val)
 			switch yyVAL.node.Val.Ctype {
@@ -3419,7 +3451,7 @@ yydefault:
 		}
 	case 341:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2189
+		//line go.y:2250
 		{
 			yyVAL.node = oldname(Pkglookup(yyDollar[1].sym.Name, builtinpkg))
 			if yyVAL.node.Op != OLITERAL {
@@ -3428,7 +3460,7 @@ yydefault:
 		}
 	case 343:
 		yyDollar = yyS[yypt-5 : yypt+1]
-		//line go.y:2199
+		//line go.y:2260
 		{
 			if yyDollar[2].node.Val.Ctype == CTRUNE && yyDollar[4].node.Val.Ctype == CTINT {
 				yyVAL.node = yyDollar[2].node
@@ -3441,37 +3473,37 @@ yydefault:
 		}
 	case 346:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2215
+		//line go.y:2276
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 347:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2219
+		//line go.y:2280
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 348:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2225
+		//line go.y:2286
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 349:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2229
+		//line go.y:2290
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
 	case 350:
 		yyDollar = yyS[yypt-1 : yypt+1]
-		//line go.y:2235
+		//line go.y:2296
 		{
 			yyVAL.list = list1(yyDollar[1].node)
 		}
 	case 351:
 		yyDollar = yyS[yypt-3 : yypt+1]
-		//line go.y:2239
+		//line go.y:2300
 		{
 			yyVAL.list = list(yyDollar[1].list, yyDollar[3].node)
 		}
diff --git a/src/cmd/internal/gc/y.output b/src/cmd/internal/gc/y.output
index f105838a7f..2821702aea 100644
--- a/src/cmd/internal/gc/y.output
+++ b/src/cmd/internal/gc/y.output
@@ -3,7 +3,7 @@ state 0
 	$accept: .file $end 
 	$$4: .    (4)
 
-	.  reduce 4 (src line 149)
+	.  reduce 4 (src line 210)
 
 	file  goto 1
 	loadsys  goto 2
@@ -21,7 +21,7 @@ state 2
 	package: .    (2)
 
 	LPACKAGE  shift 5
-	.  reduce 2 (src line 132)
+	.  reduce 2 (src line 193)
 
 	package  goto 4
 
@@ -37,7 +37,7 @@ state 4
 	file:  loadsys package.imports xdcl_list 
 	imports: .    (6)
 
-	.  reduce 6 (src line 166)
+	.  reduce 6 (src line 227)
 
 	imports  goto 8
 
@@ -56,7 +56,7 @@ state 6
 	loadsys:  $$4 import_package.import_there 
 	$$21: .    (21)
 
-	.  reduce 21 (src line 273)
+	.  reduce 21 (src line 334)
 
 	import_there  goto 14
 	$$21  goto 15
@@ -74,7 +74,7 @@ state 8
 	xdcl_list: .    (218)
 
 	LIMPORT  shift 19
-	.  reduce 218 (src line 1508)
+	.  reduce 218 (src line 1569)
 
 	xdcl_list  goto 17
 	import  goto 18
@@ -89,19 +89,19 @@ state 9
 state 10
 	sym:  LNAME.    (157)
 
-	.  reduce 157 (src line 1114)
+	.  reduce 157 (src line 1175)
 
 
 state 11
 	sym:  hidden_importsym.    (158)
 
-	.  reduce 158 (src line 1123)
+	.  reduce 158 (src line 1184)
 
 
 state 12
 	sym:  '?'.    (159)
 
-	.  reduce 159 (src line 1124)
+	.  reduce 159 (src line 1185)
 
 
 state 13
@@ -115,14 +115,14 @@ state 13
 state 14
 	loadsys:  $$4 import_package import_there.    (5)
 
-	.  reduce 5 (src line 160)
+	.  reduce 5 (src line 221)
 
 
 state 15
 	import_there:  $$21.hidden_import_list '$' '$' 
 	hidden_import_list: .    (344)
 
-	.  reduce 344 (src line 2210)
+	.  reduce 344 (src line 2271)
 
 	hidden_import_list  goto 22
 
@@ -131,7 +131,7 @@ state 16
 	import_safety: .    (19)
 
 	LNAME  shift 24
-	.  reduce 19 (src line 265)
+	.  reduce 19 (src line 326)
 
 	import_safety  goto 23
 
@@ -140,7 +140,7 @@ state 17
 	xdcl_list:  xdcl_list.xdcl ';' 
 	xdcl: .    (23)
 
-	$end  reduce 1 (src line 123)
+	$end  reduce 1 (src line 184)
 	error  shift 29
 	LLITERAL  shift 68
 	LBREAK  shift 41
@@ -170,7 +170,7 @@ state 17
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 23 (src line 286)
+	';'  reduce 23 (src line 347)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -236,7 +236,7 @@ state 19
 state 20
 	package:  LPACKAGE sym ';'.    (3)
 
-	.  reduce 3 (src line 139)
+	.  reduce 3 (src line 200)
 
 
 state 21
@@ -271,7 +271,7 @@ state 23
 state 24
 	import_safety:  LNAME.    (20)
 
-	.  reduce 20 (src line 266)
+	.  reduce 20 (src line 327)
 
 
 state 25
@@ -284,25 +284,25 @@ state 25
 state 26
 	xdcl:  common_dcl.    (24)
 
-	.  reduce 24 (src line 291)
+	.  reduce 24 (src line 352)
 
 
 state 27
 	xdcl:  xfndcl.    (25)
 
-	.  reduce 25 (src line 292)
+	.  reduce 25 (src line 353)
 
 
 state 28
 	xdcl:  non_dcl_stmt.    (26)
 
-	.  reduce 26 (src line 296)
+	.  reduce 26 (src line 357)
 
 
 state 29
 	xdcl:  error.    (27)
 
-	.  reduce 27 (src line 301)
+	.  reduce 27 (src line 362)
 
 
 state 30
@@ -373,31 +373,31 @@ state 33
 state 34
 	non_dcl_stmt:  simple_stmt.    (256)
 
-	.  reduce 256 (src line 1735)
+	.  reduce 256 (src line 1796)
 
 
 state 35
 	non_dcl_stmt:  for_stmt.    (257)
 
-	.  reduce 257 (src line 1737)
+	.  reduce 257 (src line 1798)
 
 
 state 36
 	non_dcl_stmt:  switch_stmt.    (258)
 
-	.  reduce 258 (src line 1738)
+	.  reduce 258 (src line 1799)
 
 
 state 37
 	non_dcl_stmt:  select_stmt.    (259)
 
-	.  reduce 259 (src line 1739)
+	.  reduce 259 (src line 1800)
 
 
 state 38
 	non_dcl_stmt:  if_stmt.    (260)
 
-	.  reduce 260 (src line 1740)
+	.  reduce 260 (src line 1801)
 
 
 state 39
@@ -410,7 +410,7 @@ state 39
 state 40
 	non_dcl_stmt:  LFALL.    (263)
 
-	.  reduce 263 (src line 1757)
+	.  reduce 263 (src line 1818)
 
 
 state 41
@@ -420,7 +420,7 @@ state 41
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 155 (src line 1108)
+	.  reduce 155 (src line 1169)
 
 	sym  goto 119
 	new_name  goto 118
@@ -434,7 +434,7 @@ state 42
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 155 (src line 1108)
+	.  reduce 155 (src line 1169)
 
 	sym  goto 119
 	new_name  goto 118
@@ -538,7 +538,7 @@ state 46
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 292 (src line 1906)
+	.  reduce 292 (src line 1967)
 
 	sym  goto 123
 	expr  goto 129
@@ -562,7 +562,7 @@ state 46
 state 47
 	lconst:  LCONST.    (38)
 
-	.  reduce 38 (src line 355)
+	.  reduce 38 (src line 416)
 
 
 state 48
@@ -593,7 +593,7 @@ state 48
 	expr_list:  expr.    (276)
 
 	LASOP  shift 130
-	LCOLAS  reduce 276 (src line 1841)
+	LCOLAS  reduce 276 (src line 1902)
 	LANDAND  shift 134
 	LANDNOT  shift 149
 	LCOMM  shift 152
@@ -616,9 +616,9 @@ state 48
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	'='  reduce 276 (src line 1841)
-	','  reduce 276 (src line 1841)
-	.  reduce 49 (src line 411)
+	'='  reduce 276 (src line 1902)
+	','  reduce 276 (src line 1902)
+	.  reduce 49 (src line 472)
 
 
 state 49
@@ -636,7 +636,7 @@ state 50
 	for_stmt:  LFOR.$$74 for_body 
 	$$74: .    (74)
 
-	.  reduce 74 (src line 660)
+	.  reduce 74 (src line 721)
 
 	$$74  goto 156
 
@@ -644,7 +644,7 @@ state 51
 	switch_stmt:  LSWITCH.$$88 if_header $$89 LBODY caseblock_list '}' 
 	$$88: .    (88)
 
-	.  reduce 88 (src line 755)
+	.  reduce 88 (src line 816)
 
 	$$88  goto 157
 
@@ -652,7 +652,7 @@ state 52
 	select_stmt:  LSELECT.$$91 LBODY caseblock_list '}' 
 	$$91: .    (91)
 
-	.  reduce 91 (src line 778)
+	.  reduce 91 (src line 839)
 
 	$$91  goto 158
 
@@ -660,28 +660,28 @@ state 53
 	if_stmt:  LIF.$$78 if_header $$79 loop_body $$80 elseif_list else 
 	$$78: .    (78)
 
-	.  reduce 78 (src line 689)
+	.  reduce 78 (src line 750)
 
 	$$78  goto 159
 
 state 54
 	labelname:  new_name.    (163)
 
-	.  reduce 163 (src line 1168)
+	.  reduce 163 (src line 1229)
 
 
 state 55
 	expr:  uexpr.    (93)
 
-	.  reduce 93 (src line 794)
+	.  reduce 93 (src line 855)
 
 
 state 56
 	new_name:  sym.    (153)
 	name:  sym.    (162)
 
-	':'  reduce 153 (src line 1092)
-	.  reduce 162 (src line 1159)
+	':'  reduce 153 (src line 1153)
+	.  reduce 162 (src line 1220)
 
 
 state 57
@@ -699,7 +699,7 @@ state 57
 	'('  shift 160
 	'.'  shift 161
 	'['  shift 162
-	.  reduce 114 (src line 878)
+	.  reduce 114 (src line 939)
 
 
 state 58
@@ -1027,7 +1027,7 @@ state 66
 	pexpr:  pexpr_no_paren.    (146)
 
 	'{'  shift 171
-	.  reduce 146 (src line 1055)
+	.  reduce 146 (src line 1116)
 
 
 state 67
@@ -1078,19 +1078,19 @@ state 67
 state 68
 	pexpr_no_paren:  LLITERAL.    (126)
 
-	.  reduce 126 (src line 942)
+	.  reduce 126 (src line 1003)
 
 
 state 69
 	pexpr_no_paren:  name.    (127)
 
-	.  reduce 127 (src line 947)
+	.  reduce 127 (src line 1008)
 
 
 state 70
 	pexpr_no_paren:  pseudocall.    (134)
 
-	.  reduce 134 (src line 985)
+	.  reduce 134 (src line 1046)
 
 
 state 71
@@ -1112,23 +1112,23 @@ state 72
 state 73
 	pexpr_no_paren:  fnliteral.    (139)
 
-	.  reduce 139 (src line 1012)
+	.  reduce 139 (src line 1073)
 
 
 state 74
 	convtype:  fntype.    (181)
 	fnlitdcl:  fntype.    (215)
 
-	'('  reduce 181 (src line 1221)
-	.  reduce 215 (src line 1485)
+	'('  reduce 181 (src line 1282)
+	.  reduce 215 (src line 1546)
 
 
 state 75
 	convtype:  othertype.    (182)
 	comptype:  othertype.    (183)
 
-	'('  reduce 182 (src line 1223)
-	.  reduce 183 (src line 1225)
+	'('  reduce 182 (src line 1284)
+	.  reduce 183 (src line 1286)
 
 
 state 76
@@ -1167,7 +1167,7 @@ state 77
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1900)
+	.  reduce 290 (src line 1961)
 
 	sym  goto 123
 	expr  goto 188
@@ -1226,13 +1226,13 @@ state 79
 state 80
 	othertype:  structtype.    (196)
 
-	.  reduce 196 (src line 1273)
+	.  reduce 196 (src line 1334)
 
 
 state 81
 	othertype:  interfacetype.    (197)
 
-	.  reduce 197 (src line 1274)
+	.  reduce 197 (src line 1335)
 
 
 state 82
@@ -1258,13 +1258,13 @@ state 83
 state 84
 	imports:  imports import ';'.    (7)
 
-	.  reduce 7 (src line 167)
+	.  reduce 7 (src line 228)
 
 
 state 85
 	import:  LIMPORT import_stmt.    (8)
 
-	.  reduce 8 (src line 169)
+	.  reduce 8 (src line 230)
 
 
 state 86
@@ -1291,7 +1291,7 @@ state 87
 	$$21: .    (21)
 
 	LPACKAGE  shift 7
-	.  reduce 21 (src line 273)
+	.  reduce 21 (src line 334)
 
 	import_package  goto 204
 	import_there  goto 205
@@ -1300,7 +1300,7 @@ state 87
 state 88
 	import_here:  LLITERAL.    (15)
 
-	.  reduce 15 (src line 225)
+	.  reduce 15 (src line 286)
 
 
 state 89
@@ -1336,7 +1336,7 @@ state 92
 state 93
 	hidden_import_list:  hidden_import_list hidden_import.    (345)
 
-	.  reduce 345 (src line 2211)
+	.  reduce 345 (src line 2272)
 
 
 state 94
@@ -1389,19 +1389,19 @@ state 98
 state 99
 	import_package:  LPACKAGE LNAME import_safety ';'.    (18)
 
-	.  reduce 18 (src line 248)
+	.  reduce 18 (src line 309)
 
 
 state 100
 	xdcl_list:  xdcl_list xdcl ';'.    (219)
 
-	.  reduce 219 (src line 1512)
+	.  reduce 219 (src line 1573)
 
 
 state 101
 	common_dcl:  LVAR vardcl.    (28)
 
-	.  reduce 28 (src line 306)
+	.  reduce 28 (src line 367)
 
 
 state 102
@@ -1458,19 +1458,19 @@ state 103
 state 104
 	dcl_name_list:  dcl_name.    (274)
 
-	.  reduce 274 (src line 1831)
+	.  reduce 274 (src line 1892)
 
 
 state 105
 	dcl_name:  sym.    (154)
 
-	.  reduce 154 (src line 1102)
+	.  reduce 154 (src line 1163)
 
 
 state 106
 	common_dcl:  lconst constdcl.    (31)
 
-	.  reduce 31 (src line 319)
+	.  reduce 31 (src line 380)
 
 
 state 107
@@ -1526,7 +1526,7 @@ state 108
 state 109
 	common_dcl:  LTYPE typedcl.    (35)
 
-	.  reduce 35 (src line 342)
+	.  reduce 35 (src line 403)
 
 
 state 110
@@ -1577,7 +1577,7 @@ state 111
 state 112
 	typedclname:  sym.    (47)
 
-	.  reduce 47 (src line 396)
+	.  reduce 47 (src line 457)
 
 
 state 113
@@ -1585,7 +1585,7 @@ state 113
 	fnbody: .    (210)
 
 	'{'  shift 242
-	.  reduce 210 (src line 1458)
+	.  reduce 210 (src line 1519)
 
 	fnbody  goto 241
 
@@ -1607,7 +1607,7 @@ state 114
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1708)
+	.  reduce 249 (src line 1769)
 
 	sym  goto 247
 	ntype  goto 249
@@ -1637,43 +1637,43 @@ state 116
 	non_dcl_stmt:  labelname ':'.$$261 stmt 
 	$$261: .    (261)
 
-	.  reduce 261 (src line 1741)
+	.  reduce 261 (src line 1802)
 
 	$$261  goto 252
 
 state 117
 	non_dcl_stmt:  LBREAK onew_name.    (264)
 
-	.  reduce 264 (src line 1763)
+	.  reduce 264 (src line 1824)
 
 
 state 118
 	onew_name:  new_name.    (156)
 
-	.  reduce 156 (src line 1112)
+	.  reduce 156 (src line 1173)
 
 
 state 119
 	new_name:  sym.    (153)
 
-	.  reduce 153 (src line 1092)
+	.  reduce 153 (src line 1153)
 
 
 state 120
 	non_dcl_stmt:  LCONTINUE onew_name.    (265)
 
-	.  reduce 265 (src line 1767)
+	.  reduce 265 (src line 1828)
 
 
 state 121
 	pexpr_no_paren:  pseudocall.    (134)
 	non_dcl_stmt:  LGO pseudocall.    (266)
 
-	'('  reduce 134 (src line 985)
-	'.'  reduce 134 (src line 985)
-	'{'  reduce 134 (src line 985)
-	'['  reduce 134 (src line 985)
-	.  reduce 266 (src line 1771)
+	'('  reduce 134 (src line 1046)
+	'.'  reduce 134 (src line 1046)
+	'{'  reduce 134 (src line 1046)
+	'['  reduce 134 (src line 1046)
+	.  reduce 266 (src line 1832)
 
 
 state 122
@@ -1696,7 +1696,7 @@ state 122
 state 123
 	name:  sym.    (162)
 
-	.  reduce 162 (src line 1159)
+	.  reduce 162 (src line 1220)
 
 
 state 124
@@ -1710,23 +1710,23 @@ state 125
 	pexpr_no_paren:  pseudocall.    (134)
 	non_dcl_stmt:  LDEFER pseudocall.    (267)
 
-	'('  reduce 134 (src line 985)
-	'.'  reduce 134 (src line 985)
-	'{'  reduce 134 (src line 985)
-	'['  reduce 134 (src line 985)
-	.  reduce 267 (src line 1775)
+	'('  reduce 134 (src line 1046)
+	'.'  reduce 134 (src line 1046)
+	'{'  reduce 134 (src line 1046)
+	'['  reduce 134 (src line 1046)
+	.  reduce 267 (src line 1836)
 
 
 state 126
 	non_dcl_stmt:  LGOTO new_name.    (268)
 
-	.  reduce 268 (src line 1779)
+	.  reduce 268 (src line 1840)
 
 
 state 127
 	non_dcl_stmt:  LRETURN oexpr_list.    (269)
 
-	.  reduce 269 (src line 1784)
+	.  reduce 269 (src line 1845)
 
 
 state 128
@@ -1734,7 +1734,7 @@ state 128
 	oexpr_list:  expr_list.    (293)
 
 	','  shift 155
-	.  reduce 293 (src line 1910)
+	.  reduce 293 (src line 1971)
 
 
 state 129
@@ -1780,7 +1780,7 @@ state 129
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 276 (src line 1841)
+	.  reduce 276 (src line 1902)
 
 
 state 130
@@ -1827,13 +1827,13 @@ state 130
 state 131
 	simple_stmt:  expr LINC.    (53)
 
-	.  reduce 53 (src line 461)
+	.  reduce 53 (src line 522)
 
 
 state 132
 	simple_stmt:  expr LDEC.    (54)
 
-	.  reduce 54 (src line 467)
+	.  reduce 54 (src line 528)
 
 
 state 133
@@ -2805,7 +2805,7 @@ state 156
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1912)
+	.  reduce 294 (src line 1973)
 
 	sym  goto 123
 	expr  goto 48
@@ -2853,7 +2853,7 @@ state 157
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1912)
+	.  reduce 294 (src line 1973)
 
 	sym  goto 123
 	expr  goto 48
@@ -2906,7 +2906,7 @@ state 159
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1912)
+	.  reduce 294 (src line 1973)
 
 	sym  goto 123
 	expr  goto 48
@@ -3016,7 +3016,7 @@ state 162
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1900)
+	.  reduce 290 (src line 1961)
 
 	sym  goto 123
 	expr  goto 294
@@ -3039,56 +3039,56 @@ state 162
 state 163
 	uexpr:  '*' uexpr.    (115)
 
-	.  reduce 115 (src line 880)
+	.  reduce 115 (src line 941)
 
 
 state 164
 	uexpr:  '&' uexpr.    (116)
 
-	.  reduce 116 (src line 884)
+	.  reduce 116 (src line 945)
 
 
 state 165
 	uexpr:  '+' uexpr.    (117)
 
-	.  reduce 117 (src line 895)
+	.  reduce 117 (src line 956)
 
 
 state 166
 	uexpr:  '-' uexpr.    (118)
 
-	.  reduce 118 (src line 899)
+	.  reduce 118 (src line 960)
 
 
 state 167
 	uexpr:  '!' uexpr.    (119)
 
-	.  reduce 119 (src line 903)
+	.  reduce 119 (src line 964)
 
 
 state 168
 	uexpr:  '~' uexpr.    (120)
 
-	.  reduce 120 (src line 907)
+	.  reduce 120 (src line 968)
 
 
 state 169
 	uexpr:  '^' uexpr.    (121)
 
-	.  reduce 121 (src line 912)
+	.  reduce 121 (src line 973)
 
 
 state 170
 	uexpr:  LCOMM uexpr.    (122)
 
-	.  reduce 122 (src line 916)
+	.  reduce 122 (src line 977)
 
 
 state 171
 	pexpr_no_paren:  pexpr_no_paren '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1014)
+	.  reduce 140 (src line 1075)
 
 	start_complit  goto 296
 
@@ -3143,19 +3143,19 @@ state 173
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 148 (src line 1070)
+	.  reduce 148 (src line 1131)
 
 
 state 174
 	expr_or_type:  non_expr_type.    (149)
 
-	.  reduce 149 (src line 1072)
+	.  reduce 149 (src line 1133)
 
 
 state 175
 	non_expr_type:  recvchantype.    (172)
 
-	.  reduce 172 (src line 1202)
+	.  reduce 172 (src line 1263)
 
 
 state 176
@@ -3163,11 +3163,11 @@ state 176
 	convtype:  fntype.    (181)
 	fnlitdcl:  fntype.    (215)
 
-	error  reduce 215 (src line 1485)
-	LBODY  reduce 215 (src line 1485)
-	'('  reduce 181 (src line 1221)
-	'{'  reduce 215 (src line 1485)
-	.  reduce 173 (src line 1204)
+	error  reduce 215 (src line 1546)
+	LBODY  reduce 215 (src line 1546)
+	'('  reduce 181 (src line 1282)
+	'{'  reduce 215 (src line 1546)
+	.  reduce 173 (src line 1265)
 
 
 state 177
@@ -3175,10 +3175,10 @@ state 177
 	convtype:  othertype.    (182)
 	comptype:  othertype.    (183)
 
-	LBODY  reduce 183 (src line 1225)
-	'('  reduce 182 (src line 1223)
-	'{'  reduce 183 (src line 1225)
-	.  reduce 174 (src line 1205)
+	LBODY  reduce 183 (src line 1286)
+	'('  reduce 182 (src line 1284)
+	'{'  reduce 183 (src line 1286)
+	.  reduce 174 (src line 1266)
 
 
 state 178
@@ -3310,20 +3310,20 @@ state 181
 	pexpr_no_paren:  comptype lbrace.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1014)
+	.  reduce 140 (src line 1075)
 
 	start_complit  goto 301
 
 state 182
 	lbrace:  LBODY.    (151)
 
-	.  reduce 151 (src line 1077)
+	.  reduce 151 (src line 1138)
 
 
 state 183
 	lbrace:  '{'.    (152)
 
-	.  reduce 152 (src line 1082)
+	.  reduce 152 (src line 1143)
 
 
 state 184
@@ -3359,9 +3359,9 @@ state 184
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1720)
+	';'  reduce 251 (src line 1781)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1720)
+	'}'  reduce 251 (src line 1781)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -3403,7 +3403,7 @@ state 184
 state 185
 	fnliteral:  fnlitdcl error.    (217)
 
-	.  reduce 217 (src line 1497)
+	.  reduce 217 (src line 1558)
 
 
 state 186
@@ -3463,13 +3463,13 @@ state 188
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 291 (src line 1904)
+	.  reduce 291 (src line 1965)
 
 
 state 189
 	othertype:  LCHAN non_recvchantype.    (193)
 
-	.  reduce 193 (src line 1259)
+	.  reduce 193 (src line 1320)
 
 
 state 190
@@ -3504,25 +3504,25 @@ state 190
 state 191
 	non_recvchantype:  fntype.    (176)
 
-	.  reduce 176 (src line 1211)
+	.  reduce 176 (src line 1272)
 
 
 state 192
 	non_recvchantype:  othertype.    (177)
 
-	.  reduce 177 (src line 1213)
+	.  reduce 177 (src line 1274)
 
 
 state 193
 	non_recvchantype:  ptrtype.    (178)
 
-	.  reduce 178 (src line 1214)
+	.  reduce 178 (src line 1275)
 
 
 state 194
 	non_recvchantype:  dotname.    (179)
 
-	.  reduce 179 (src line 1215)
+	.  reduce 179 (src line 1276)
 
 
 state 195
@@ -3588,7 +3588,7 @@ state 197
 	dotname:  name.'.' sym 
 
 	'.'  shift 314
-	.  reduce 189 (src line 1235)
+	.  reduce 189 (src line 1296)
 
 
 state 198
@@ -3665,27 +3665,27 @@ state 201
 	osemi: .    (286)
 
 	';'  shift 333
-	.  reduce 286 (src line 1894)
+	.  reduce 286 (src line 1955)
 
 	osemi  goto 332
 
 state 202
 	import:  LIMPORT '(' ')'.    (10)
 
-	.  reduce 10 (src line 172)
+	.  reduce 10 (src line 233)
 
 
 state 203
 	import_stmt_list:  import_stmt.    (13)
 
-	.  reduce 13 (src line 221)
+	.  reduce 13 (src line 282)
 
 
 state 204
 	import_stmt:  import_here import_package.import_there 
 	$$21: .    (21)
 
-	.  reduce 21 (src line 273)
+	.  reduce 21 (src line 334)
 
 	import_there  goto 334
 	$$21  goto 15
@@ -3693,37 +3693,37 @@ state 204
 state 205
 	import_stmt:  import_here import_there.    (12)
 
-	.  reduce 12 (src line 210)
+	.  reduce 12 (src line 271)
 
 
 state 206
 	import_here:  sym LLITERAL.    (16)
 
-	.  reduce 16 (src line 233)
+	.  reduce 16 (src line 294)
 
 
 state 207
 	import_here:  '.' LLITERAL.    (17)
 
-	.  reduce 17 (src line 240)
+	.  reduce 17 (src line 301)
 
 
 state 208
 	hidden_importsym:  '@' LLITERAL '.' LNAME.    (160)
 
-	.  reduce 160 (src line 1129)
+	.  reduce 160 (src line 1190)
 
 
 state 209
 	hidden_importsym:  '@' LLITERAL '.' '?'.    (161)
 
-	.  reduce 161 (src line 1144)
+	.  reduce 161 (src line 1205)
 
 
 state 210
 	import_there:  $$21 hidden_import_list '$' '$'.    (22)
 
-	.  reduce 22 (src line 277)
+	.  reduce 22 (src line 338)
 
 
 state 211
@@ -3757,7 +3757,7 @@ state 212
 state 213
 	hidden_pkg_importsym:  hidden_importsym.    (310)
 
-	.  reduce 310 (src line 1986)
+	.  reduce 310 (src line 2047)
 
 
 state 214
@@ -3807,7 +3807,7 @@ state 215
 state 216
 	hidden_pkgtype:  hidden_pkg_importsym.    (311)
 
-	.  reduce 311 (src line 1993)
+	.  reduce 311 (src line 2054)
 
 
 state 217
@@ -3815,7 +3815,7 @@ state 217
 	fnbody: .    (210)
 
 	'{'  shift 242
-	.  reduce 210 (src line 1458)
+	.  reduce 210 (src line 1519)
 
 	fnbody  goto 353
 
@@ -3845,20 +3845,20 @@ state 220
 	osemi: .    (286)
 
 	';'  shift 359
-	.  reduce 286 (src line 1894)
+	.  reduce 286 (src line 1955)
 
 	osemi  goto 358
 
 state 221
 	common_dcl:  LVAR '(' ')'.    (30)
 
-	.  reduce 30 (src line 315)
+	.  reduce 30 (src line 376)
 
 
 state 222
 	vardcl_list:  vardcl.    (220)
 
-	.  reduce 220 (src line 1524)
+	.  reduce 220 (src line 1585)
 
 
 state 223
@@ -3866,7 +3866,7 @@ state 223
 	vardcl:  dcl_name_list ntype.'=' expr_list 
 
 	'='  shift 360
-	.  reduce 39 (src line 361)
+	.  reduce 39 (src line 422)
 
 
 state 224
@@ -3926,31 +3926,31 @@ state 225
 state 226
 	ntype:  recvchantype.    (166)
 
-	.  reduce 166 (src line 1191)
+	.  reduce 166 (src line 1252)
 
 
 state 227
 	ntype:  fntype.    (167)
 
-	.  reduce 167 (src line 1193)
+	.  reduce 167 (src line 1254)
 
 
 state 228
 	ntype:  othertype.    (168)
 
-	.  reduce 168 (src line 1194)
+	.  reduce 168 (src line 1255)
 
 
 state 229
 	ntype:  ptrtype.    (169)
 
-	.  reduce 169 (src line 1195)
+	.  reduce 169 (src line 1256)
 
 
 state 230
 	ntype:  dotname.    (170)
 
-	.  reduce 170 (src line 1196)
+	.  reduce 170 (src line 1257)
 
 
 state 231
@@ -3995,14 +3995,14 @@ state 233
 	osemi: .    (286)
 
 	';'  shift 366
-	.  reduce 286 (src line 1894)
+	.  reduce 286 (src line 1955)
 
 	osemi  goto 365
 
 state 234
 	common_dcl:  lconst '(' ')'.    (34)
 
-	.  reduce 34 (src line 337)
+	.  reduce 34 (src line 398)
 
 
 state 235
@@ -4060,32 +4060,32 @@ state 237
 	osemi: .    (286)
 
 	';'  shift 370
-	.  reduce 286 (src line 1894)
+	.  reduce 286 (src line 1955)
 
 	osemi  goto 369
 
 state 238
 	common_dcl:  LTYPE '(' ')'.    (37)
 
-	.  reduce 37 (src line 350)
+	.  reduce 37 (src line 411)
 
 
 state 239
 	typedcl_list:  typedcl.    (224)
 
-	.  reduce 224 (src line 1538)
+	.  reduce 224 (src line 1599)
 
 
 state 240
 	typedcl:  typedclname ntype.    (48)
 
-	.  reduce 48 (src line 405)
+	.  reduce 48 (src line 466)
 
 
 state 241
 	xfndcl:  LFUNC fndcl fnbody.    (204)
 
-	.  reduce 204 (src line 1319)
+	.  reduce 204 (src line 1380)
 
 
 state 242
@@ -4121,9 +4121,9 @@ state 242
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1720)
+	';'  reduce 251 (src line 1781)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1720)
+	'}'  reduce 251 (src line 1781)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -4176,20 +4176,20 @@ state 244
 	ocomma: .    (288)
 
 	','  shift 373
-	.  reduce 288 (src line 1897)
+	.  reduce 288 (src line 1958)
 
 	ocomma  goto 374
 
 state 245
 	arg_type_list:  arg_type.    (247)
 
-	.  reduce 247 (src line 1698)
+	.  reduce 247 (src line 1759)
 
 
 state 246
 	arg_type:  name_or_type.    (243)
 
-	.  reduce 243 (src line 1682)
+	.  reduce 243 (src line 1743)
 
 
 state 247
@@ -4210,7 +4210,7 @@ state 247
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 162 (src line 1159)
+	.  reduce 162 (src line 1220)
 
 	sym  goto 123
 	ntype  goto 249
@@ -4229,13 +4229,13 @@ state 247
 state 248
 	arg_type:  dotdotdot.    (246)
 
-	.  reduce 246 (src line 1696)
+	.  reduce 246 (src line 1757)
 
 
 state 249
 	name_or_type:  ntype.    (150)
 
-	.  reduce 150 (src line 1074)
+	.  reduce 150 (src line 1135)
 
 
 state 250
@@ -4254,7 +4254,7 @@ state 250
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 164 (src line 1180)
+	.  reduce 164 (src line 1241)
 
 	sym  goto 123
 	ntype  goto 377
@@ -4285,7 +4285,7 @@ state 251
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1708)
+	.  reduce 249 (src line 1769)
 
 	sym  goto 247
 	ntype  goto 249
@@ -4311,11 +4311,11 @@ state 252
 	error  shift 307
 	LLITERAL  shift 68
 	LBREAK  shift 41
-	LCASE  reduce 251 (src line 1720)
+	LCASE  reduce 251 (src line 1781)
 	LCHAN  shift 78
 	LCONST  shift 47
 	LCONTINUE  shift 42
-	LDEFAULT  reduce 251 (src line 1720)
+	LDEFAULT  reduce 251 (src line 1781)
 	LDEFER  shift 44
 	LFALL  shift 40
 	LFOR  shift 50
@@ -4339,9 +4339,9 @@ state 252
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1720)
+	';'  reduce 251 (src line 1781)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1720)
+	'}'  reduce 251 (src line 1781)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -4396,7 +4396,7 @@ state 253
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1708)
+	.  reduce 249 (src line 1769)
 
 	sym  goto 247
 	ntype  goto 249
@@ -4458,7 +4458,7 @@ state 254
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 50 (src line 426)
+	.  reduce 50 (src line 487)
 
 
 state 255
@@ -4502,7 +4502,7 @@ state 255
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 94 (src line 796)
+	.  reduce 94 (src line 857)
 
 
 state 256
@@ -4545,7 +4545,7 @@ state 256
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 95 (src line 800)
+	.  reduce 95 (src line 861)
 
 
 state 257
@@ -4582,7 +4582,7 @@ state 257
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 96 (src line 804)
+	.  reduce 96 (src line 865)
 
 
 state 258
@@ -4619,7 +4619,7 @@ state 258
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 97 (src line 808)
+	.  reduce 97 (src line 869)
 
 
 state 259
@@ -4656,7 +4656,7 @@ state 259
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 98 (src line 812)
+	.  reduce 98 (src line 873)
 
 
 state 260
@@ -4693,7 +4693,7 @@ state 260
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 99 (src line 816)
+	.  reduce 99 (src line 877)
 
 
 state 261
@@ -4730,7 +4730,7 @@ state 261
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 100 (src line 820)
+	.  reduce 100 (src line 881)
 
 
 state 262
@@ -4767,7 +4767,7 @@ state 262
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 101 (src line 824)
+	.  reduce 101 (src line 885)
 
 
 state 263
@@ -4800,7 +4800,7 @@ state 263
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 102 (src line 828)
+	.  reduce 102 (src line 889)
 
 
 state 264
@@ -4833,7 +4833,7 @@ state 264
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 103 (src line 832)
+	.  reduce 103 (src line 893)
 
 
 state 265
@@ -4866,7 +4866,7 @@ state 265
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 104 (src line 836)
+	.  reduce 104 (src line 897)
 
 
 state 266
@@ -4899,7 +4899,7 @@ state 266
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 105 (src line 840)
+	.  reduce 105 (src line 901)
 
 
 state 267
@@ -4925,7 +4925,7 @@ state 267
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 106 (src line 844)
+	.  reduce 106 (src line 905)
 
 
 state 268
@@ -4951,7 +4951,7 @@ state 268
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 107 (src line 848)
+	.  reduce 107 (src line 909)
 
 
 state 269
@@ -4977,7 +4977,7 @@ state 269
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 108 (src line 852)
+	.  reduce 108 (src line 913)
 
 
 state 270
@@ -5003,7 +5003,7 @@ state 270
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 109 (src line 856)
+	.  reduce 109 (src line 917)
 
 
 state 271
@@ -5029,7 +5029,7 @@ state 271
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 110 (src line 860)
+	.  reduce 110 (src line 921)
 
 
 state 272
@@ -5055,7 +5055,7 @@ state 272
 	expr:  expr.LRSH expr 
 	expr:  expr.LCOMM expr 
 
-	.  reduce 111 (src line 864)
+	.  reduce 111 (src line 925)
 
 
 state 273
@@ -5081,7 +5081,7 @@ state 273
 	expr:  expr LRSH expr.    (112)
 	expr:  expr.LCOMM expr 
 
-	.  reduce 112 (src line 868)
+	.  reduce 112 (src line 929)
 
 
 state 274
@@ -5126,7 +5126,7 @@ state 274
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 113 (src line 873)
+	.  reduce 113 (src line 934)
 
 
 state 275
@@ -5134,7 +5134,7 @@ state 275
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 51 (src line 431)
+	.  reduce 51 (src line 492)
 
 
 state 276
@@ -5142,7 +5142,7 @@ state 276
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 52 (src line 443)
+	.  reduce 52 (src line 504)
 
 
 state 277
@@ -5188,13 +5188,13 @@ state 277
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 277 (src line 1846)
+	.  reduce 277 (src line 1907)
 
 
 state 278
 	for_stmt:  LFOR $$74 for_body.    (75)
 
-	.  reduce 75 (src line 665)
+	.  reduce 75 (src line 726)
 
 
 state 279
@@ -5210,19 +5210,19 @@ state 280
 	for_header:  osimple_stmt.    (71)
 
 	';'  shift 383
-	.  reduce 71 (src line 645)
+	.  reduce 71 (src line 706)
 
 
 state 281
 	for_header:  range_stmt.    (72)
 
-	.  reduce 72 (src line 651)
+	.  reduce 72 (src line 712)
 
 
 state 282
 	osimple_stmt:  simple_stmt.    (295)
 
-	.  reduce 295 (src line 1916)
+	.  reduce 295 (src line 1977)
 
 
 state 283
@@ -5283,7 +5283,7 @@ state 285
 	switch_stmt:  LSWITCH $$88 if_header.$$89 LBODY caseblock_list '}' 
 	$$89: .    (89)
 
-	.  reduce 89 (src line 760)
+	.  reduce 89 (src line 821)
 
 	$$89  goto 387
 
@@ -5292,14 +5292,14 @@ state 286
 	if_header:  osimple_stmt.';' osimple_stmt 
 
 	';'  shift 388
-	.  reduce 76 (src line 671)
+	.  reduce 76 (src line 732)
 
 
 state 287
 	select_stmt:  LSELECT $$91 LBODY.caseblock_list '}' 
 	caseblock_list: .    (63)
 
-	.  reduce 63 (src line 591)
+	.  reduce 63 (src line 652)
 
 	caseblock_list  goto 389
 
@@ -5307,14 +5307,14 @@ state 288
 	if_stmt:  LIF $$78 if_header.$$79 loop_body $$80 elseif_list else 
 	$$79: .    (79)
 
-	.  reduce 79 (src line 694)
+	.  reduce 79 (src line 755)
 
 	$$79  goto 390
 
 state 289
 	pseudocall:  pexpr '(' ')'.    (123)
 
-	.  reduce 123 (src line 925)
+	.  reduce 123 (src line 986)
 
 
 state 290
@@ -5325,20 +5325,20 @@ state 290
 
 	LDDD  shift 392
 	','  shift 393
-	.  reduce 288 (src line 1897)
+	.  reduce 288 (src line 1958)
 
 	ocomma  goto 391
 
 state 291
 	expr_or_type_list:  expr_or_type.    (278)
 
-	.  reduce 278 (src line 1851)
+	.  reduce 278 (src line 1912)
 
 
 state 292
 	pexpr_no_paren:  pexpr '.' sym.    (128)
 
-	.  reduce 128 (src line 948)
+	.  reduce 128 (src line 1009)
 
 
 state 293
@@ -5432,7 +5432,7 @@ state 294
 	'%'  shift 147
 	'&'  shift 148
 	']'  shift 396
-	.  reduce 291 (src line 1904)
+	.  reduce 291 (src line 1965)
 
 
 state 295
@@ -5467,7 +5467,7 @@ state 296
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1882)
+	.  reduce 284 (src line 1943)
 
 	sym  goto 123
 	expr  goto 402
@@ -5495,13 +5495,13 @@ state 297
 	pexpr:  '(' expr_or_type ')'.    (147)
 
 	'{'  shift 404
-	.  reduce 147 (src line 1057)
+	.  reduce 147 (src line 1118)
 
 
 state 298
 	non_expr_type:  '*' non_expr_type.    (175)
 
-	.  reduce 175 (src line 1206)
+	.  reduce 175 (src line 1267)
 
 
 state 299
@@ -5581,7 +5581,7 @@ state 300
 	'%'  shift 147
 	'&'  shift 148
 	','  shift 413
-	.  reduce 288 (src line 1897)
+	.  reduce 288 (src line 1958)
 
 	ocomma  goto 412
 
@@ -5609,7 +5609,7 @@ state 301
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1882)
+	.  reduce 284 (src line 1943)
 
 	sym  goto 123
 	expr  goto 402
@@ -5644,38 +5644,38 @@ state 302
 state 303
 	stmt_list:  stmt.    (270)
 
-	.  reduce 270 (src line 1805)
+	.  reduce 270 (src line 1866)
 
 
 state 304
 	stmt:  compound_stmt.    (252)
 
-	.  reduce 252 (src line 1724)
+	.  reduce 252 (src line 1785)
 
 
 state 305
 	stmt:  common_dcl.    (253)
 
-	.  reduce 253 (src line 1725)
+	.  reduce 253 (src line 1786)
 
 
 state 306
 	stmt:  non_dcl_stmt.    (254)
 
-	.  reduce 254 (src line 1729)
+	.  reduce 254 (src line 1790)
 
 
 state 307
 	stmt:  error.    (255)
 
-	.  reduce 255 (src line 1730)
+	.  reduce 255 (src line 1791)
 
 
 state 308
 	compound_stmt:  '{'.$$59 stmt_list '}' 
 	$$59: .    (59)
 
-	.  reduce 59 (src line 545)
+	.  reduce 59 (src line 606)
 
 	$$59  goto 417
 
@@ -5740,7 +5740,7 @@ state 310
 state 311
 	othertype:  LCHAN LCOMM ntype.    (194)
 
-	.  reduce 194 (src line 1264)
+	.  reduce 194 (src line 1325)
 
 
 state 312
@@ -5753,7 +5753,7 @@ state 312
 state 313
 	ptrtype:  '*' ntype.    (198)
 
-	.  reduce 198 (src line 1276)
+	.  reduce 198 (src line 1337)
 
 
 state 314
@@ -5780,20 +5780,20 @@ state 316
 	osemi: .    (286)
 
 	';'  shift 424
-	.  reduce 286 (src line 1894)
+	.  reduce 286 (src line 1955)
 
 	osemi  goto 423
 
 state 317
 	structtype:  LSTRUCT lbrace '}'.    (201)
 
-	.  reduce 201 (src line 1296)
+	.  reduce 201 (src line 1357)
 
 
 state 318
 	structdcl_list:  structdcl.    (226)
 
-	.  reduce 226 (src line 1548)
+	.  reduce 226 (src line 1609)
 
 
 state 319
@@ -5832,7 +5832,7 @@ state 320
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 427
 
@@ -5861,13 +5861,13 @@ state 322
 state 323
 	new_name_list:  new_name.    (272)
 
-	.  reduce 272 (src line 1821)
+	.  reduce 272 (src line 1882)
 
 
 state 324
 	embed:  packname.    (238)
 
-	.  reduce 238 (src line 1647)
+	.  reduce 238 (src line 1708)
 
 
 state 325
@@ -5875,11 +5875,11 @@ state 325
 	packname:  LNAME.    (236)
 	packname:  LNAME.'.' sym 
 
-	LLITERAL  reduce 236 (src line 1622)
-	';'  reduce 236 (src line 1622)
+	LLITERAL  reduce 236 (src line 1683)
+	';'  reduce 236 (src line 1683)
 	'.'  shift 434
-	'}'  reduce 236 (src line 1622)
-	.  reduce 157 (src line 1114)
+	'}'  reduce 236 (src line 1683)
+	.  reduce 157 (src line 1175)
 
 
 state 326
@@ -5888,20 +5888,20 @@ state 326
 	osemi: .    (286)
 
 	';'  shift 436
-	.  reduce 286 (src line 1894)
+	.  reduce 286 (src line 1955)
 
 	osemi  goto 435
 
 state 327
 	interfacetype:  LINTERFACE lbrace '}'.    (203)
 
-	.  reduce 203 (src line 1309)
+	.  reduce 203 (src line 1370)
 
 
 state 328
 	interfacedcl_list:  interfacedcl.    (228)
 
-	.  reduce 228 (src line 1555)
+	.  reduce 228 (src line 1616)
 
 
 state 329
@@ -5915,7 +5915,7 @@ state 329
 state 330
 	interfacedcl:  packname.    (240)
 
-	.  reduce 240 (src line 1659)
+	.  reduce 240 (src line 1720)
 
 
 state 331
@@ -5942,7 +5942,7 @@ state 333
 	'.'  shift 90
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1895)
+	.  reduce 287 (src line 1956)
 
 	import_here  goto 87
 	sym  goto 89
@@ -5952,7 +5952,7 @@ state 333
 state 334
 	import_stmt:  import_here import_package import_there.    (11)
 
-	.  reduce 11 (src line 174)
+	.  reduce 11 (src line 235)
 
 
 state 335
@@ -5972,31 +5972,31 @@ state 336
 state 337
 	hidden_type:  hidden_type_misc.    (312)
 
-	.  reduce 312 (src line 2004)
+	.  reduce 312 (src line 2065)
 
 
 state 338
 	hidden_type:  hidden_type_recv_chan.    (313)
 
-	.  reduce 313 (src line 2006)
+	.  reduce 313 (src line 2067)
 
 
 state 339
 	hidden_type:  hidden_type_func.    (314)
 
-	.  reduce 314 (src line 2007)
+	.  reduce 314 (src line 2068)
 
 
 state 340
 	hidden_type_misc:  hidden_importsym.    (317)
 
-	.  reduce 317 (src line 2013)
+	.  reduce 317 (src line 2074)
 
 
 state 341
 	hidden_type_misc:  LNAME.    (318)
 
-	.  reduce 318 (src line 2018)
+	.  reduce 318 (src line 2079)
 
 
 state 342
@@ -6131,7 +6131,7 @@ state 354
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1918)
+	.  reduce 296 (src line 1979)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -6151,7 +6151,7 @@ state 355
 state 356
 	hidden_funarg_list:  hidden_funarg.    (346)
 
-	.  reduce 346 (src line 2213)
+	.  reduce 346 (src line 2274)
 
 
 state 357
@@ -6191,7 +6191,7 @@ state 359
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1895)
+	.  reduce 287 (src line 1956)
 
 	sym  goto 105
 	dcl_name  goto 104
@@ -6246,13 +6246,13 @@ state 361
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 41 (src line 370)
+	.  reduce 41 (src line 431)
 
 
 state 362
 	dcl_name_list:  dcl_name_list ',' dcl_name.    (275)
 
-	.  reduce 275 (src line 1836)
+	.  reduce 275 (src line 1897)
 
 
 state 363
@@ -6305,7 +6305,7 @@ state 366
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1895)
+	.  reduce 287 (src line 1956)
 
 	sym  goto 105
 	dcl_name  goto 104
@@ -6362,7 +6362,7 @@ state 368
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 43 (src line 380)
+	.  reduce 43 (src line 441)
 
 
 state 369
@@ -6379,7 +6379,7 @@ state 370
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1895)
+	.  reduce 287 (src line 1956)
 
 	sym  goto 112
 	typedclname  goto 111
@@ -6412,7 +6412,7 @@ state 372
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1470)
+	.  reduce 212 (src line 1531)
 
 	sym  goto 485
 	dotname  goto 493
@@ -6444,7 +6444,7 @@ state 373
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 289 (src line 1898)
+	.  reduce 289 (src line 1959)
 
 	sym  goto 247
 	ntype  goto 249
@@ -6464,25 +6464,25 @@ state 373
 state 374
 	oarg_type_list_ocomma:  arg_type_list ocomma.    (250)
 
-	.  reduce 250 (src line 1712)
+	.  reduce 250 (src line 1773)
 
 
 state 375
 	arg_type:  sym name_or_type.    (244)
 
-	.  reduce 244 (src line 1684)
+	.  reduce 244 (src line 1745)
 
 
 state 376
 	arg_type:  sym dotdotdot.    (245)
 
-	.  reduce 245 (src line 1690)
+	.  reduce 245 (src line 1751)
 
 
 state 377
 	dotdotdot:  LDDD ntype.    (165)
 
-	.  reduce 165 (src line 1186)
+	.  reduce 165 (src line 1247)
 
 
 state 378
@@ -6495,7 +6495,7 @@ state 378
 state 379
 	non_dcl_stmt:  labelname ':' $$261 stmt.    (262)
 
-	.  reduce 262 (src line 1746)
+	.  reduce 262 (src line 1807)
 
 
 state 380
@@ -6508,14 +6508,14 @@ state 380
 state 381
 	for_body:  for_header loop_body.    (73)
 
-	.  reduce 73 (src line 653)
+	.  reduce 73 (src line 714)
 
 
 state 382
 	loop_body:  LBODY.$$65 stmt_list '}' 
 	$$65: .    (65)
 
-	.  reduce 65 (src line 600)
+	.  reduce 65 (src line 661)
 
 	$$65  goto 497
 
@@ -6542,7 +6542,7 @@ state 383
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1912)
+	.  reduce 294 (src line 1973)
 
 	sym  goto 123
 	expr  goto 48
@@ -6695,7 +6695,7 @@ state 386
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 69 (src line 625)
+	.  reduce 69 (src line 686)
 
 
 state 387
@@ -6728,7 +6728,7 @@ state 388
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1912)
+	.  reduce 294 (src line 1973)
 
 	sym  goto 123
 	expr  goto 48
@@ -6782,7 +6782,7 @@ state 392
 	ocomma: .    (288)
 
 	','  shift 413
-	.  reduce 288 (src line 1897)
+	.  reduce 288 (src line 1958)
 
 	ocomma  goto 510
 
@@ -6809,7 +6809,7 @@ state 393
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 289 (src line 1898)
+	.  reduce 289 (src line 1959)
 
 	sym  goto 123
 	expr  goto 173
@@ -6848,7 +6848,7 @@ state 395
 state 396
 	pexpr_no_paren:  pexpr '[' expr ']'.    (131)
 
-	.  reduce 131 (src line 967)
+	.  reduce 131 (src line 1028)
 
 
 state 397
@@ -6875,7 +6875,7 @@ state 397
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1900)
+	.  reduce 290 (src line 1961)
 
 	sym  goto 123
 	expr  goto 188
@@ -6909,20 +6909,20 @@ state 399
 	ocomma: .    (288)
 
 	','  shift 516
-	.  reduce 288 (src line 1897)
+	.  reduce 288 (src line 1958)
 
 	ocomma  goto 517
 
 state 400
 	keyval_list:  keyval.    (280)
 
-	.  reduce 280 (src line 1864)
+	.  reduce 280 (src line 1925)
 
 
 state 401
 	keyval_list:  bare_complitexpr.    (281)
 
-	.  reduce 281 (src line 1869)
+	.  reduce 281 (src line 1930)
 
 
 state 402
@@ -6970,14 +6970,14 @@ state 402
 	'%'  shift 147
 	'&'  shift 148
 	':'  shift 518
-	.  reduce 142 (src line 1027)
+	.  reduce 142 (src line 1088)
 
 
 state 403
 	bare_complitexpr:  '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1014)
+	.  reduce 140 (src line 1075)
 
 	start_complit  goto 519
 
@@ -6985,7 +6985,7 @@ state 404
 	pexpr_no_paren:  '(' expr_or_type ')' '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1014)
+	.  reduce 140 (src line 1075)
 
 	start_complit  goto 520
 
@@ -7022,47 +7022,47 @@ state 405
 state 406
 	recvchantype:  LCOMM LCHAN ntype.    (199)
 
-	.  reduce 199 (src line 1282)
+	.  reduce 199 (src line 1343)
 
 
 state 407
 	ntype:  fntype.    (167)
 	non_recvchantype:  fntype.    (176)
 
-	LBODY  reduce 176 (src line 1211)
-	'('  reduce 176 (src line 1211)
-	'{'  reduce 176 (src line 1211)
-	.  reduce 167 (src line 1193)
+	LBODY  reduce 176 (src line 1272)
+	'('  reduce 176 (src line 1272)
+	'{'  reduce 176 (src line 1272)
+	.  reduce 167 (src line 1254)
 
 
 state 408
 	ntype:  othertype.    (168)
 	non_recvchantype:  othertype.    (177)
 
-	LBODY  reduce 177 (src line 1213)
-	'('  reduce 177 (src line 1213)
-	'{'  reduce 177 (src line 1213)
-	.  reduce 168 (src line 1194)
+	LBODY  reduce 177 (src line 1274)
+	'('  reduce 177 (src line 1274)
+	'{'  reduce 177 (src line 1274)
+	.  reduce 168 (src line 1255)
 
 
 state 409
 	ntype:  ptrtype.    (169)
 	non_recvchantype:  ptrtype.    (178)
 
-	LBODY  reduce 178 (src line 1214)
-	'('  reduce 178 (src line 1214)
-	'{'  reduce 178 (src line 1214)
-	.  reduce 169 (src line 1195)
+	LBODY  reduce 178 (src line 1275)
+	'('  reduce 178 (src line 1275)
+	'{'  reduce 178 (src line 1275)
+	.  reduce 169 (src line 1256)
 
 
 state 410
 	ntype:  dotname.    (170)
 	non_recvchantype:  dotname.    (179)
 
-	LBODY  reduce 179 (src line 1215)
-	'('  reduce 179 (src line 1215)
-	'{'  reduce 179 (src line 1215)
-	.  reduce 170 (src line 1196)
+	LBODY  reduce 179 (src line 1276)
+	'('  reduce 179 (src line 1276)
+	'{'  reduce 179 (src line 1276)
+	.  reduce 170 (src line 1257)
 
 
 state 411
@@ -7105,7 +7105,7 @@ state 412
 state 413
 	ocomma:  ','.    (289)
 
-	.  reduce 289 (src line 1898)
+	.  reduce 289 (src line 1959)
 
 
 state 414
@@ -7118,7 +7118,7 @@ state 414
 state 415
 	fnliteral:  fnlitdcl lbrace stmt_list '}'.    (216)
 
-	.  reduce 216 (src line 1491)
+	.  reduce 216 (src line 1552)
 
 
 state 416
@@ -7128,11 +7128,11 @@ state 416
 	error  shift 307
 	LLITERAL  shift 68
 	LBREAK  shift 41
-	LCASE  reduce 251 (src line 1720)
+	LCASE  reduce 251 (src line 1781)
 	LCHAN  shift 78
 	LCONST  shift 47
 	LCONTINUE  shift 42
-	LDEFAULT  reduce 251 (src line 1720)
+	LDEFAULT  reduce 251 (src line 1781)
 	LDEFER  shift 44
 	LFALL  shift 40
 	LFOR  shift 50
@@ -7156,9 +7156,9 @@ state 416
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1720)
+	';'  reduce 251 (src line 1781)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1720)
+	'}'  reduce 251 (src line 1781)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -7229,9 +7229,9 @@ state 417
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1720)
+	';'  reduce 251 (src line 1781)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1720)
+	'}'  reduce 251 (src line 1781)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -7273,25 +7273,25 @@ state 417
 state 418
 	othertype:  '[' oexpr ']' ntype.    (191)
 
-	.  reduce 191 (src line 1249)
+	.  reduce 191 (src line 1310)
 
 
 state 419
 	othertype:  '[' LDDD ']' ntype.    (192)
 
-	.  reduce 192 (src line 1254)
+	.  reduce 192 (src line 1315)
 
 
 state 420
 	non_recvchantype:  '(' ntype ')'.    (180)
 
-	.  reduce 180 (src line 1216)
+	.  reduce 180 (src line 1277)
 
 
 state 421
 	dotname:  name '.' sym.    (190)
 
-	.  reduce 190 (src line 1237)
+	.  reduce 190 (src line 1298)
 
 
 state 422
@@ -7339,7 +7339,7 @@ state 424
 	'('  shift 321
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1895)
+	.  reduce 287 (src line 1956)
 
 	sym  goto 119
 	packname  goto 324
@@ -7354,7 +7354,7 @@ state 425
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 529
 
@@ -7373,13 +7373,13 @@ state 426
 state 427
 	structdcl:  embed oliteral.    (231)
 
-	.  reduce 231 (src line 1590)
+	.  reduce 231 (src line 1651)
 
 
 state 428
 	oliteral:  LLITERAL.    (303)
 
-	.  reduce 303 (src line 1940)
+	.  reduce 303 (src line 2001)
 
 
 state 429
@@ -7403,7 +7403,7 @@ state 431
 	packname:  LNAME.'.' sym 
 
 	'.'  shift 434
-	.  reduce 236 (src line 1622)
+	.  reduce 236 (src line 1683)
 
 
 state 432
@@ -7411,7 +7411,7 @@ state 432
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 533
 
@@ -7450,7 +7450,7 @@ state 436
 	'('  shift 331
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1895)
+	.  reduce 287 (src line 1956)
 
 	sym  goto 119
 	packname  goto 330
@@ -7461,7 +7461,7 @@ state 436
 state 437
 	interfacedcl:  new_name indcl.    (239)
 
-	.  reduce 239 (src line 1653)
+	.  reduce 239 (src line 1714)
 
 
 state 438
@@ -7481,7 +7481,7 @@ state 438
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1708)
+	.  reduce 249 (src line 1769)
 
 	sym  goto 247
 	ntype  goto 249
@@ -7510,25 +7510,25 @@ state 439
 state 440
 	import:  LIMPORT '(' import_stmt_list osemi ')'.    (9)
 
-	.  reduce 9 (src line 171)
+	.  reduce 9 (src line 232)
 
 
 state 441
 	import_stmt_list:  import_stmt_list ';' import_stmt.    (14)
 
-	.  reduce 14 (src line 223)
+	.  reduce 14 (src line 284)
 
 
 state 442
 	hidden_import:  LIMPORT LNAME LLITERAL ';'.    (304)
 
-	.  reduce 304 (src line 1945)
+	.  reduce 304 (src line 2006)
 
 
 state 443
 	hidden_import:  LVAR hidden_pkg_importsym hidden_type ';'.    (305)
 
-	.  reduce 305 (src line 1950)
+	.  reduce 305 (src line 2011)
 
 
 state 444
@@ -7587,7 +7587,7 @@ state 447
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 298 (src line 1924)
+	.  reduce 298 (src line 1985)
 
 	sym  goto 546
 	hidden_importsym  goto 11
@@ -7610,7 +7610,7 @@ state 448
 	'['  shift 342
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 300 (src line 1930)
+	.  reduce 300 (src line 1991)
 
 	sym  goto 550
 	hidden_importsym  goto 553
@@ -7625,13 +7625,13 @@ state 448
 state 449
 	hidden_type_misc:  '*' hidden_type.    (324)
 
-	.  reduce 324 (src line 2049)
+	.  reduce 324 (src line 2110)
 
 
 state 450
 	hidden_type_misc:  LCHAN hidden_type_non_recv_chan.    (325)
 
-	.  reduce 325 (src line 2053)
+	.  reduce 325 (src line 2114)
 
 
 state 451
@@ -7666,13 +7666,13 @@ state 452
 state 453
 	hidden_type_non_recv_chan:  hidden_type_misc.    (315)
 
-	.  reduce 315 (src line 2009)
+	.  reduce 315 (src line 2070)
 
 
 state 454
 	hidden_type_non_recv_chan:  hidden_type_func.    (316)
 
-	.  reduce 316 (src line 2011)
+	.  reduce 316 (src line 2072)
 
 
 state 455
@@ -7703,7 +7703,7 @@ state 456
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1918)
+	.  reduce 296 (src line 1979)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -7721,7 +7721,7 @@ state 457
 state 458
 	hidden_constant:  hidden_literal.    (342)
 
-	.  reduce 342 (src line 2196)
+	.  reduce 342 (src line 2257)
 
 
 state 459
@@ -7741,7 +7741,7 @@ state 459
 state 460
 	hidden_literal:  LLITERAL.    (339)
 
-	.  reduce 339 (src line 2165)
+	.  reduce 339 (src line 2226)
 
 
 state 461
@@ -7754,7 +7754,7 @@ state 461
 state 462
 	hidden_literal:  sym.    (341)
 
-	.  reduce 341 (src line 2188)
+	.  reduce 341 (src line 2249)
 
 
 state 463
@@ -7776,13 +7776,13 @@ state 463
 state 464
 	hidden_import:  LTYPE hidden_pkgtype hidden_type ';'.    (308)
 
-	.  reduce 308 (src line 1962)
+	.  reduce 308 (src line 2023)
 
 
 state 465
 	hidden_import:  LFUNC hidden_fndcl fnbody ';'.    (309)
 
-	.  reduce 309 (src line 1966)
+	.  reduce 309 (src line 2027)
 
 
 state 466
@@ -7797,7 +7797,7 @@ state 467
 	hidden_funarg_list:  hidden_funarg_list.',' hidden_funarg 
 
 	','  shift 469
-	.  reduce 297 (src line 1922)
+	.  reduce 297 (src line 1983)
 
 
 state 468
@@ -7828,7 +7828,7 @@ state 470
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 565
 
@@ -7856,13 +7856,13 @@ state 471
 state 472
 	common_dcl:  LVAR '(' vardcl_list osemi ')'.    (29)
 
-	.  reduce 29 (src line 311)
+	.  reduce 29 (src line 372)
 
 
 state 473
 	vardcl_list:  vardcl_list ';' vardcl.    (221)
 
-	.  reduce 221 (src line 1526)
+	.  reduce 221 (src line 1587)
 
 
 state 474
@@ -7870,19 +7870,19 @@ state 474
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 40 (src line 366)
+	.  reduce 40 (src line 427)
 
 
 state 475
 	ntype:  '(' ntype ')'.    (171)
 
-	.  reduce 171 (src line 1197)
+	.  reduce 171 (src line 1258)
 
 
 state 476
 	common_dcl:  lconst '(' constdcl osemi ')'.    (32)
 
-	.  reduce 32 (src line 325)
+	.  reduce 32 (src line 386)
 
 
 state 477
@@ -7891,20 +7891,20 @@ state 477
 	osemi: .    (286)
 
 	';'  shift 568
-	.  reduce 286 (src line 1894)
+	.  reduce 286 (src line 1955)
 
 	osemi  goto 567
 
 state 478
 	constdcl_list:  constdcl1.    (222)
 
-	.  reduce 222 (src line 1531)
+	.  reduce 222 (src line 1592)
 
 
 state 479
 	constdcl1:  constdcl.    (44)
 
-	.  reduce 44 (src line 385)
+	.  reduce 44 (src line 446)
 
 
 state 480
@@ -7928,7 +7928,7 @@ state 480
 	'?'  shift 12
 	'@'  shift 13
 	','  shift 225
-	.  reduce 46 (src line 391)
+	.  reduce 46 (src line 452)
 
 	sym  goto 123
 	ntype  goto 569
@@ -7947,25 +7947,25 @@ state 481
 	expr_list:  expr_list.',' expr 
 
 	','  shift 155
-	.  reduce 42 (src line 375)
+	.  reduce 42 (src line 436)
 
 
 state 482
 	common_dcl:  LTYPE '(' typedcl_list osemi ')'.    (36)
 
-	.  reduce 36 (src line 346)
+	.  reduce 36 (src line 407)
 
 
 state 483
 	typedcl_list:  typedcl_list ';' typedcl.    (225)
 
-	.  reduce 225 (src line 1543)
+	.  reduce 225 (src line 1604)
 
 
 state 484
 	fnbody:  '{' stmt_list '}'.    (211)
 
-	.  reduce 211 (src line 1462)
+	.  reduce 211 (src line 1523)
 
 
 state 485
@@ -7973,19 +7973,19 @@ state 485
 	fndcl:  '(' oarg_type_list_ocomma ')' sym.'(' oarg_type_list_ocomma ')' fnres 
 
 	'('  shift 570
-	.  reduce 162 (src line 1159)
+	.  reduce 162 (src line 1220)
 
 
 state 486
 	fntype:  LFUNC '(' oarg_type_list_ocomma ')' fnres.    (209)
 
-	.  reduce 209 (src line 1449)
+	.  reduce 209 (src line 1510)
 
 
 state 487
 	fnres:  fnret_type.    (213)
 
-	.  reduce 213 (src line 1475)
+	.  reduce 213 (src line 1536)
 
 
 state 488
@@ -8005,7 +8005,7 @@ state 488
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1708)
+	.  reduce 249 (src line 1769)
 
 	sym  goto 247
 	ntype  goto 249
@@ -8027,37 +8027,37 @@ state 488
 state 489
 	fnret_type:  recvchantype.    (184)
 
-	.  reduce 184 (src line 1228)
+	.  reduce 184 (src line 1289)
 
 
 state 490
 	fnret_type:  fntype.    (185)
 
-	.  reduce 185 (src line 1230)
+	.  reduce 185 (src line 1291)
 
 
 state 491
 	fnret_type:  othertype.    (186)
 
-	.  reduce 186 (src line 1231)
+	.  reduce 186 (src line 1292)
 
 
 state 492
 	fnret_type:  ptrtype.    (187)
 
-	.  reduce 187 (src line 1232)
+	.  reduce 187 (src line 1293)
 
 
 state 493
 	fnret_type:  dotname.    (188)
 
-	.  reduce 188 (src line 1233)
+	.  reduce 188 (src line 1294)
 
 
 state 494
 	arg_type_list:  arg_type_list ',' arg_type.    (248)
 
-	.  reduce 248 (src line 1703)
+	.  reduce 248 (src line 1764)
 
 
 state 495
@@ -8076,7 +8076,7 @@ state 495
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1470)
+	.  reduce 212 (src line 1531)
 
 	sym  goto 123
 	dotname  goto 493
@@ -8107,7 +8107,7 @@ state 496
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1470)
+	.  reduce 212 (src line 1531)
 
 	sym  goto 123
 	dotname  goto 493
@@ -8155,9 +8155,9 @@ state 497
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1720)
+	';'  reduce 251 (src line 1781)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1720)
+	'}'  reduce 251 (src line 1781)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -8289,33 +8289,33 @@ state 501
 	switch_stmt:  LSWITCH $$88 if_header $$89 LBODY.caseblock_list '}' 
 	caseblock_list: .    (63)
 
-	.  reduce 63 (src line 591)
+	.  reduce 63 (src line 652)
 
 	caseblock_list  goto 577
 
 state 502
 	if_header:  osimple_stmt ';' osimple_stmt.    (77)
 
-	.  reduce 77 (src line 678)
+	.  reduce 77 (src line 739)
 
 
 state 503
 	caseblock_list:  caseblock_list caseblock.    (64)
 
-	.  reduce 64 (src line 595)
+	.  reduce 64 (src line 656)
 
 
 state 504
 	select_stmt:  LSELECT $$91 LBODY caseblock_list '}'.    (92)
 
-	.  reduce 92 (src line 783)
+	.  reduce 92 (src line 844)
 
 
 state 505
 	caseblock:  case.$$61 stmt_list 
 	$$61: .    (61)
 
-	.  reduce 61 (src line 560)
+	.  reduce 61 (src line 621)
 
 	$$61  goto 578
 
@@ -8377,14 +8377,14 @@ state 508
 	if_stmt:  LIF $$78 if_header $$79 loop_body.$$80 elseif_list else 
 	$$80: .    (80)
 
-	.  reduce 80 (src line 700)
+	.  reduce 80 (src line 761)
 
 	$$80  goto 581
 
 state 509
 	pseudocall:  pexpr '(' expr_or_type_list ocomma ')'.    (124)
 
-	.  reduce 124 (src line 930)
+	.  reduce 124 (src line 991)
 
 
 state 510
@@ -8397,19 +8397,19 @@ state 510
 state 511
 	expr_or_type_list:  expr_or_type_list ',' expr_or_type.    (279)
 
-	.  reduce 279 (src line 1856)
+	.  reduce 279 (src line 1917)
 
 
 state 512
 	pexpr_no_paren:  pexpr '.' '(' expr_or_type ')'.    (129)
 
-	.  reduce 129 (src line 959)
+	.  reduce 129 (src line 1020)
 
 
 state 513
 	pexpr_no_paren:  pexpr '.' '(' LTYPE ')'.    (130)
 
-	.  reduce 130 (src line 963)
+	.  reduce 130 (src line 1024)
 
 
 state 514
@@ -8424,7 +8424,7 @@ state 514
 state 515
 	pexpr_no_paren:  pexpr_no_paren '{' start_complit braced_keyval_list '}'.    (137)
 
-	.  reduce 137 (src line 999)
+	.  reduce 137 (src line 1060)
 
 
 state 516
@@ -8452,7 +8452,7 @@ state 516
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 289 (src line 1898)
+	.  reduce 289 (src line 1959)
 
 	sym  goto 123
 	expr  goto 402
@@ -8476,7 +8476,7 @@ state 516
 state 517
 	braced_keyval_list:  keyval_list ocomma.    (285)
 
-	.  reduce 285 (src line 1886)
+	.  reduce 285 (src line 1947)
 
 
 state 518
@@ -8546,7 +8546,7 @@ state 519
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1882)
+	.  reduce 284 (src line 1943)
 
 	sym  goto 123
 	expr  goto 402
@@ -8593,7 +8593,7 @@ state 520
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1882)
+	.  reduce 284 (src line 1943)
 
 	sym  goto 123
 	expr  goto 402
@@ -8627,19 +8627,19 @@ state 521
 state 522
 	pexpr_no_paren:  convtype '(' expr ocomma ')'.    (135)
 
-	.  reduce 135 (src line 986)
+	.  reduce 135 (src line 1047)
 
 
 state 523
 	pexpr_no_paren:  comptype lbrace start_complit braced_keyval_list '}'.    (136)
 
-	.  reduce 136 (src line 992)
+	.  reduce 136 (src line 1053)
 
 
 state 524
 	stmt_list:  stmt_list ';' stmt.    (271)
 
-	.  reduce 271 (src line 1813)
+	.  reduce 271 (src line 1874)
 
 
 state 525
@@ -8654,31 +8654,31 @@ state 525
 state 526
 	othertype:  LMAP '[' ntype ']' ntype.    (195)
 
-	.  reduce 195 (src line 1269)
+	.  reduce 195 (src line 1330)
 
 
 state 527
 	structtype:  LSTRUCT lbrace structdcl_list osemi '}'.    (200)
 
-	.  reduce 200 (src line 1289)
+	.  reduce 200 (src line 1350)
 
 
 state 528
 	structdcl_list:  structdcl_list ';' structdcl.    (227)
 
-	.  reduce 227 (src line 1550)
+	.  reduce 227 (src line 1611)
 
 
 state 529
 	structdcl:  new_name_list ntype oliteral.    (230)
 
-	.  reduce 230 (src line 1565)
+	.  reduce 230 (src line 1626)
 
 
 state 530
 	new_name_list:  new_name_list ',' new_name.    (273)
 
-	.  reduce 273 (src line 1826)
+	.  reduce 273 (src line 1887)
 
 
 state 531
@@ -8686,7 +8686,7 @@ state 531
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 594
 
@@ -8700,7 +8700,7 @@ state 532
 state 533
 	structdcl:  '*' embed oliteral.    (233)
 
-	.  reduce 233 (src line 1601)
+	.  reduce 233 (src line 1662)
 
 
 state 534
@@ -8713,19 +8713,19 @@ state 534
 state 535
 	packname:  LNAME '.' sym.    (237)
 
-	.  reduce 237 (src line 1633)
+	.  reduce 237 (src line 1694)
 
 
 state 536
 	interfacetype:  LINTERFACE lbrace interfacedcl_list osemi '}'.    (202)
 
-	.  reduce 202 (src line 1302)
+	.  reduce 202 (src line 1363)
 
 
 state 537
 	interfacedcl_list:  interfacedcl_list ';' interfacedcl.    (229)
 
-	.  reduce 229 (src line 1560)
+	.  reduce 229 (src line 1621)
 
 
 state 538
@@ -8738,13 +8738,13 @@ state 538
 state 539
 	interfacedcl:  '(' packname ')'.    (241)
 
-	.  reduce 241 (src line 1663)
+	.  reduce 241 (src line 1724)
 
 
 state 540
 	hidden_type_misc:  '[' ']' hidden_type.    (319)
 
-	.  reduce 319 (src line 2029)
+	.  reduce 319 (src line 2090)
 
 
 state 541
@@ -8787,13 +8787,13 @@ state 544
 	hidden_structdcl_list:  hidden_structdcl_list.';' hidden_structdcl 
 
 	';'  shift 601
-	.  reduce 299 (src line 1928)
+	.  reduce 299 (src line 1989)
 
 
 state 545
 	hidden_structdcl_list:  hidden_structdcl.    (348)
 
-	.  reduce 348 (src line 2223)
+	.  reduce 348 (src line 2284)
 
 
 state 546
@@ -8829,13 +8829,13 @@ state 548
 	hidden_interfacedcl_list:  hidden_interfacedcl_list.';' hidden_interfacedcl 
 
 	';'  shift 604
-	.  reduce 301 (src line 1934)
+	.  reduce 301 (src line 1995)
 
 
 state 549
 	hidden_interfacedcl_list:  hidden_interfacedcl.    (350)
 
-	.  reduce 350 (src line 2233)
+	.  reduce 350 (src line 2294)
 
 
 state 550
@@ -8848,23 +8848,23 @@ state 550
 state 551
 	hidden_interfacedcl:  hidden_type.    (334)
 
-	.  reduce 334 (src line 2140)
+	.  reduce 334 (src line 2201)
 
 
 state 552
 	sym:  LNAME.    (157)
 	hidden_type_misc:  LNAME.    (318)
 
-	'('  reduce 157 (src line 1114)
-	.  reduce 318 (src line 2018)
+	'('  reduce 157 (src line 1175)
+	.  reduce 318 (src line 2079)
 
 
 state 553
 	sym:  hidden_importsym.    (158)
 	hidden_type_misc:  hidden_importsym.    (317)
 
-	'('  reduce 158 (src line 1123)
-	.  reduce 317 (src line 2013)
+	'('  reduce 158 (src line 1184)
+	.  reduce 317 (src line 2074)
 
 
 state 554
@@ -8877,13 +8877,13 @@ state 554
 state 555
 	hidden_type_misc:  LCHAN LCOMM hidden_type.    (327)
 
-	.  reduce 327 (src line 2065)
+	.  reduce 327 (src line 2126)
 
 
 state 556
 	hidden_type_recv_chan:  LCOMM LCHAN hidden_type.    (328)
 
-	.  reduce 328 (src line 2072)
+	.  reduce 328 (src line 2133)
 
 
 state 557
@@ -8896,7 +8896,7 @@ state 557
 state 558
 	hidden_import:  LCONST hidden_pkg_importsym '=' hidden_constant ';'.    (306)
 
-	.  reduce 306 (src line 1954)
+	.  reduce 306 (src line 2015)
 
 
 state 559
@@ -8909,7 +8909,7 @@ state 559
 state 560
 	hidden_literal:  '-' LLITERAL.    (340)
 
-	.  reduce 340 (src line 2170)
+	.  reduce 340 (src line 2231)
 
 
 state 561
@@ -8934,7 +8934,7 @@ state 562
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2145)
+	.  reduce 335 (src line 2206)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -8954,13 +8954,13 @@ state 563
 state 564
 	hidden_funarg_list:  hidden_funarg_list ',' hidden_funarg.    (347)
 
-	.  reduce 347 (src line 2218)
+	.  reduce 347 (src line 2279)
 
 
 state 565
 	hidden_funarg:  sym hidden_type oliteral.    (330)
 
-	.  reduce 330 (src line 2086)
+	.  reduce 330 (src line 2147)
 
 
 state 566
@@ -8968,7 +8968,7 @@ state 566
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 615
 
@@ -8986,7 +8986,7 @@ state 568
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 287 (src line 1895)
+	.  reduce 287 (src line 1956)
 
 	sym  goto 105
 	dcl_name  goto 104
@@ -9000,7 +9000,7 @@ state 569
 	constdcl1:  dcl_name_list ntype.    (45)
 
 	'='  shift 367
-	.  reduce 45 (src line 387)
+	.  reduce 45 (src line 448)
 
 
 state 570
@@ -9020,7 +9020,7 @@ state 570
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 249 (src line 1708)
+	.  reduce 249 (src line 1769)
 
 	sym  goto 247
 	ntype  goto 249
@@ -9049,7 +9049,7 @@ state 571
 state 572
 	fndcl:  sym '(' oarg_type_list_ocomma ')' fnres.    (205)
 
-	.  reduce 205 (src line 1337)
+	.  reduce 205 (src line 1398)
 
 
 state 573
@@ -9084,7 +9084,7 @@ state 574
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1912)
+	.  reduce 294 (src line 1973)
 
 	sym  goto 123
 	expr  goto 48
@@ -9149,7 +9149,7 @@ state 575
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 67 (src line 611)
+	.  reduce 67 (src line 672)
 
 
 state 576
@@ -9195,7 +9195,7 @@ state 576
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 68 (src line 618)
+	.  reduce 68 (src line 679)
 
 
 state 577
@@ -9217,11 +9217,11 @@ state 578
 	error  shift 307
 	LLITERAL  shift 68
 	LBREAK  shift 41
-	LCASE  reduce 251 (src line 1720)
+	LCASE  reduce 251 (src line 1781)
 	LCHAN  shift 78
 	LCONST  shift 47
 	LCONTINUE  shift 42
-	LDEFAULT  reduce 251 (src line 1720)
+	LDEFAULT  reduce 251 (src line 1781)
 	LDEFER  shift 44
 	LFALL  shift 40
 	LFOR  shift 50
@@ -9245,9 +9245,9 @@ state 578
 	'*'  shift 58
 	'&'  shift 59
 	'('  shift 67
-	';'  reduce 251 (src line 1720)
+	';'  reduce 251 (src line 1781)
 	'{'  shift 308
-	'}'  reduce 251 (src line 1720)
+	'}'  reduce 251 (src line 1781)
 	'!'  shift 62
 	'~'  shift 63
 	'['  shift 77
@@ -9302,27 +9302,27 @@ state 579
 state 580
 	case:  LDEFAULT ':'.    (58)
 
-	.  reduce 58 (src line 525)
+	.  reduce 58 (src line 586)
 
 
 state 581
 	if_stmt:  LIF $$78 if_header $$79 loop_body $$80.elseif_list else 
 	elseif_list: .    (84)
 
-	.  reduce 84 (src line 735)
+	.  reduce 84 (src line 796)
 
 	elseif_list  goto 628
 
 state 582
 	pseudocall:  pexpr '(' expr_or_type_list LDDD ocomma ')'.    (125)
 
-	.  reduce 125 (src line 935)
+	.  reduce 125 (src line 996)
 
 
 state 583
 	pexpr_no_paren:  pexpr '[' oexpr ':' oexpr ']'.    (132)
 
-	.  reduce 132 (src line 971)
+	.  reduce 132 (src line 1032)
 
 
 state 584
@@ -9348,7 +9348,7 @@ state 584
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 290 (src line 1900)
+	.  reduce 290 (src line 1961)
 
 	sym  goto 123
 	expr  goto 188
@@ -9371,19 +9371,19 @@ state 584
 state 585
 	keyval_list:  keyval_list ',' keyval.    (282)
 
-	.  reduce 282 (src line 1873)
+	.  reduce 282 (src line 1934)
 
 
 state 586
 	keyval_list:  keyval_list ',' bare_complitexpr.    (283)
 
-	.  reduce 283 (src line 1877)
+	.  reduce 283 (src line 1938)
 
 
 state 587
 	keyval:  expr ':' complitexpr.    (141)
 
-	.  reduce 141 (src line 1021)
+	.  reduce 141 (src line 1082)
 
 
 state 588
@@ -9429,14 +9429,14 @@ state 588
 	'/'  shift 146
 	'%'  shift 147
 	'&'  shift 148
-	.  reduce 144 (src line 1047)
+	.  reduce 144 (src line 1108)
 
 
 state 589
 	complitexpr:  '{'.start_complit braced_keyval_list '}' 
 	start_complit: .    (140)
 
-	.  reduce 140 (src line 1014)
+	.  reduce 140 (src line 1075)
 
 	start_complit  goto 630
 
@@ -9458,22 +9458,22 @@ state 592
 	ntype:  '(' ntype ')'.    (171)
 	non_recvchantype:  '(' ntype ')'.    (180)
 
-	LBODY  reduce 180 (src line 1216)
-	'('  reduce 180 (src line 1216)
-	'{'  reduce 180 (src line 1216)
-	.  reduce 171 (src line 1197)
+	LBODY  reduce 180 (src line 1277)
+	'('  reduce 180 (src line 1277)
+	'{'  reduce 180 (src line 1277)
+	.  reduce 171 (src line 1258)
 
 
 state 593
 	compound_stmt:  '{' $$59 stmt_list '}'.    (60)
 
-	.  reduce 60 (src line 550)
+	.  reduce 60 (src line 611)
 
 
 state 594
 	structdcl:  '(' embed ')' oliteral.    (232)
 
-	.  reduce 232 (src line 1595)
+	.  reduce 232 (src line 1656)
 
 
 state 595
@@ -9481,7 +9481,7 @@ state 595
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 633
 
@@ -9490,7 +9490,7 @@ state 596
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 634
 
@@ -9510,7 +9510,7 @@ state 597
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1470)
+	.  reduce 212 (src line 1531)
 
 	sym  goto 123
 	dotname  goto 493
@@ -9528,7 +9528,7 @@ state 597
 state 598
 	hidden_type_misc:  '[' LLITERAL ']' hidden_type.    (320)
 
-	.  reduce 320 (src line 2033)
+	.  reduce 320 (src line 2094)
 
 
 state 599
@@ -9555,7 +9555,7 @@ state 599
 state 600
 	hidden_type_misc:  LSTRUCT '{' ohidden_structdcl_list '}'.    (322)
 
-	.  reduce 322 (src line 2041)
+	.  reduce 322 (src line 2102)
 
 
 state 601
@@ -9575,14 +9575,14 @@ state 602
 	oliteral: .    (302)
 
 	LLITERAL  shift 428
-	.  reduce 302 (src line 1936)
+	.  reduce 302 (src line 1997)
 
 	oliteral  goto 638
 
 state 603
 	hidden_type_misc:  LINTERFACE '{' ohidden_interfacedcl_list '}'.    (323)
 
-	.  reduce 323 (src line 2045)
+	.  reduce 323 (src line 2106)
 
 
 state 604
@@ -9616,7 +9616,7 @@ state 605
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1918)
+	.  reduce 296 (src line 1979)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -9627,7 +9627,7 @@ state 605
 state 606
 	hidden_type_misc:  LCHAN '(' hidden_type_recv_chan ')'.    (326)
 
-	.  reduce 326 (src line 2059)
+	.  reduce 326 (src line 2120)
 
 
 state 607
@@ -9645,7 +9645,7 @@ state 607
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2145)
+	.  reduce 335 (src line 2206)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -9672,19 +9672,19 @@ state 608
 state 609
 	hidden_import:  LCONST hidden_pkg_importsym hidden_type '=' hidden_constant ';'.    (307)
 
-	.  reduce 307 (src line 1958)
+	.  reduce 307 (src line 2019)
 
 
 state 610
 	hidden_fndcl:  hidden_pkg_importsym '(' ohidden_funarg_list ')' ohidden_funres.    (207)
 
-	.  reduce 207 (src line 1406)
+	.  reduce 207 (src line 1467)
 
 
 state 611
 	ohidden_funres:  hidden_funres.    (336)
 
-	.  reduce 336 (src line 2149)
+	.  reduce 336 (src line 2210)
 
 
 state 612
@@ -9694,7 +9694,7 @@ state 612
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1918)
+	.  reduce 296 (src line 1979)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -9705,7 +9705,7 @@ state 612
 state 613
 	hidden_funres:  hidden_type.    (338)
 
-	.  reduce 338 (src line 2156)
+	.  reduce 338 (src line 2217)
 
 
 state 614
@@ -9715,7 +9715,7 @@ state 614
 	LNAME  shift 10
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 296 (src line 1918)
+	.  reduce 296 (src line 1979)
 
 	sym  goto 357
 	hidden_importsym  goto 11
@@ -9726,19 +9726,19 @@ state 614
 state 615
 	hidden_funarg:  sym LDDD hidden_type oliteral.    (331)
 
-	.  reduce 331 (src line 2095)
+	.  reduce 331 (src line 2156)
 
 
 state 616
 	common_dcl:  lconst '(' constdcl ';' constdcl_list osemi ')'.    (33)
 
-	.  reduce 33 (src line 331)
+	.  reduce 33 (src line 392)
 
 
 state 617
 	constdcl_list:  constdcl_list ';' constdcl1.    (223)
 
-	.  reduce 223 (src line 1533)
+	.  reduce 223 (src line 1594)
 
 
 state 618
@@ -9751,25 +9751,25 @@ state 618
 state 619
 	fnres:  '(' oarg_type_list_ocomma ')'.    (214)
 
-	.  reduce 214 (src line 1479)
+	.  reduce 214 (src line 1540)
 
 
 state 620
 	loop_body:  LBODY $$65 stmt_list '}'.    (66)
 
-	.  reduce 66 (src line 605)
+	.  reduce 66 (src line 666)
 
 
 state 621
 	for_header:  osimple_stmt ';' osimple_stmt ';' osimple_stmt.    (70)
 
-	.  reduce 70 (src line 631)
+	.  reduce 70 (src line 692)
 
 
 state 622
 	switch_stmt:  LSWITCH $$88 if_header $$89 LBODY caseblock_list '}'.    (90)
 
-	.  reduce 90 (src line 769)
+	.  reduce 90 (src line 830)
 
 
 state 623
@@ -9777,13 +9777,13 @@ state 623
 	stmt_list:  stmt_list.';' stmt 
 
 	';'  shift 416
-	.  reduce 62 (src line 572)
+	.  reduce 62 (src line 633)
 
 
 state 624
 	case:  LCASE expr_or_type_list ':'.    (55)
 
-	.  reduce 55 (src line 474)
+	.  reduce 55 (src line 535)
 
 
 state 625
@@ -9918,7 +9918,7 @@ state 628
 	else: .    (86)
 
 	LELSE  shift 650
-	.  reduce 86 (src line 744)
+	.  reduce 86 (src line 805)
 
 	elseif  goto 649
 	else  goto 648
@@ -9954,7 +9954,7 @@ state 630
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 284 (src line 1882)
+	.  reduce 284 (src line 1943)
 
 	sym  goto 123
 	expr  goto 402
@@ -9980,55 +9980,55 @@ state 630
 state 631
 	bare_complitexpr:  '{' start_complit braced_keyval_list '}'.    (143)
 
-	.  reduce 143 (src line 1041)
+	.  reduce 143 (src line 1102)
 
 
 state 632
 	pexpr_no_paren:  '(' expr_or_type ')' '{' start_complit braced_keyval_list '}'.    (138)
 
-	.  reduce 138 (src line 1005)
+	.  reduce 138 (src line 1066)
 
 
 state 633
 	structdcl:  '(' '*' embed ')' oliteral.    (234)
 
-	.  reduce 234 (src line 1607)
+	.  reduce 234 (src line 1668)
 
 
 state 634
 	structdcl:  '*' '(' embed ')' oliteral.    (235)
 
-	.  reduce 235 (src line 1614)
+	.  reduce 235 (src line 1675)
 
 
 state 635
 	indcl:  '(' oarg_type_list_ocomma ')' fnres.    (242)
 
-	.  reduce 242 (src line 1669)
+	.  reduce 242 (src line 1730)
 
 
 state 636
 	hidden_type_misc:  LMAP '[' hidden_type ']' hidden_type.    (321)
 
-	.  reduce 321 (src line 2037)
+	.  reduce 321 (src line 2098)
 
 
 state 637
 	hidden_structdcl_list:  hidden_structdcl_list ';' hidden_structdcl.    (349)
 
-	.  reduce 349 (src line 2228)
+	.  reduce 349 (src line 2289)
 
 
 state 638
 	hidden_structdcl:  sym hidden_type oliteral.    (332)
 
-	.  reduce 332 (src line 2111)
+	.  reduce 332 (src line 2172)
 
 
 state 639
 	hidden_interfacedcl_list:  hidden_interfacedcl_list ';' hidden_interfacedcl.    (351)
 
-	.  reduce 351 (src line 2238)
+	.  reduce 351 (src line 2299)
 
 
 state 640
@@ -10041,7 +10041,7 @@ state 640
 state 641
 	hidden_type_func:  LFUNC '(' ohidden_funarg_list ')' ohidden_funres.    (329)
 
-	.  reduce 329 (src line 2080)
+	.  reduce 329 (src line 2141)
 
 
 state 642
@@ -10081,7 +10081,7 @@ state 645
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 212 (src line 1470)
+	.  reduce 212 (src line 1531)
 
 	sym  goto 123
 	dotname  goto 493
@@ -10193,13 +10193,13 @@ state 647
 state 648
 	if_stmt:  LIF $$78 if_header $$79 loop_body $$80 elseif_list else.    (81)
 
-	.  reduce 81 (src line 704)
+	.  reduce 81 (src line 765)
 
 
 state 649
 	elseif_list:  elseif_list elseif.    (85)
 
-	.  reduce 85 (src line 739)
+	.  reduce 85 (src line 800)
 
 
 state 650
@@ -10215,7 +10215,7 @@ state 650
 state 651
 	pexpr_no_paren:  pexpr '[' oexpr ':' oexpr ':' oexpr ']'.    (133)
 
-	.  reduce 133 (src line 975)
+	.  reduce 133 (src line 1036)
 
 
 state 652
@@ -10240,7 +10240,7 @@ state 653
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2145)
+	.  reduce 335 (src line 2206)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -10253,13 +10253,13 @@ state 653
 state 654
 	hidden_constant:  '(' hidden_literal '+' hidden_literal ')'.    (343)
 
-	.  reduce 343 (src line 2198)
+	.  reduce 343 (src line 2259)
 
 
 state 655
 	hidden_funres:  '(' ohidden_funarg_list ')'.    (337)
 
-	.  reduce 337 (src line 2151)
+	.  reduce 337 (src line 2212)
 
 
 state 656
@@ -10277,7 +10277,7 @@ state 656
 	'('  shift 612
 	'['  shift 342
 	'@'  shift 13
-	.  reduce 335 (src line 2145)
+	.  reduce 335 (src line 2206)
 
 	hidden_importsym  goto 340
 	hidden_funres  goto 611
@@ -10290,51 +10290,51 @@ state 656
 state 657
 	fndcl:  '(' oarg_type_list_ocomma ')' sym '(' oarg_type_list_ocomma ')' fnres.    (206)
 
-	.  reduce 206 (src line 1369)
+	.  reduce 206 (src line 1430)
 
 
 state 658
 	case:  LCASE expr_or_type_list '=' expr ':'.    (56)
 
-	.  reduce 56 (src line 498)
+	.  reduce 56 (src line 559)
 
 
 state 659
 	case:  LCASE expr_or_type_list LCOLAS expr ':'.    (57)
 
-	.  reduce 57 (src line 516)
+	.  reduce 57 (src line 577)
 
 
 state 660
 	elseif:  LELSE LIF.$$82 if_header loop_body 
 	$$82: .    (82)
 
-	.  reduce 82 (src line 721)
+	.  reduce 82 (src line 782)
 
 	$$82  goto 665
 
 state 661
 	else:  LELSE compound_stmt.    (87)
 
-	.  reduce 87 (src line 748)
+	.  reduce 87 (src line 809)
 
 
 state 662
 	complitexpr:  '{' start_complit braced_keyval_list '}'.    (145)
 
-	.  reduce 145 (src line 1049)
+	.  reduce 145 (src line 1110)
 
 
 state 663
 	hidden_interfacedcl:  sym '(' ohidden_funarg_list ')' ohidden_funres.    (333)
 
-	.  reduce 333 (src line 2135)
+	.  reduce 333 (src line 2196)
 
 
 state 664
 	hidden_fndcl:  '(' hidden_funarg_list ')' sym '(' ohidden_funarg_list ')' ohidden_funres.    (208)
 
-	.  reduce 208 (src line 1432)
+	.  reduce 208 (src line 1493)
 
 
 state 665
@@ -10360,7 +10360,7 @@ state 665
 	'['  shift 77
 	'?'  shift 12
 	'@'  shift 13
-	.  reduce 294 (src line 1912)
+	.  reduce 294 (src line 1973)
 
 	sym  goto 123
 	expr  goto 48
@@ -10394,7 +10394,7 @@ state 666
 state 667
 	elseif:  LELSE LIF $$82 if_header loop_body.    (83)
 
-	.  reduce 83 (src line 726)
+	.  reduce 83 (src line 787)
 
 
 76 terminals, 142 nonterminals
diff --git a/src/cmd/internal/gc/yaccerrors.go b/src/cmd/internal/gc/yaccerrors.go
deleted file mode 100644
index 9dc54d9c8c..0000000000
--- a/src/cmd/internal/gc/yaccerrors.go
+++ /dev/null
@@ -1,194 +0,0 @@
-// Copyright 2010 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// +build ignore
-
-// This program implements the core idea from
-//
-//	Clinton L. Jeffery, Generating LR syntax error messages from examples,
-//	ACM TOPLAS 25(5) (September 2003).  http://doi.acm.org/10.1145/937563.937566
-//
-// It reads Bison's summary of a grammar followed by a file
-// like go.errors, replacing lines beginning with % by the
-// yystate and yychar that will be active when an error happens
-// while parsing that line.
-//
-// Unlike the system described in the paper, the lines in go.errors
-// give grammar symbol name lists, not actual program fragments.
-// This is a little less programmer-friendly but doesn't require being
-// able to run the text through lex.c.
-
-package main
-
-import (
-	"bufio"
-	"fmt"
-	"io"
-	"log"
-	"os"
-	"strconv"
-	"strings"
-)
-
-func xatoi(s string) int {
-	n, err := strconv.Atoi(s)
-	if err != nil {
-		log.Fatal(err)
-	}
-	return n
-}
-
-func trimParen(s string) string {
-	s = strings.TrimPrefix(s, "(")
-	s = strings.TrimSuffix(s, ")")
-	return s
-}
-
-type action struct {
-	token string
-	n     int
-}
-
-var shift = map[int][]action{}
-var reduce = map[int][]action{}
-
-type rule struct {
-	lhs  string
-	size int
-}
-
-var rules = map[int]rule{}
-
-func readYaccOutput() {
-	r, err := os.Open("y.output")
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer r.Close()
-
-	var state int
-
-	scanner := bufio.NewScanner(r)
-	for scanner.Scan() {
-		f := strings.Fields(scanner.Text())
-		nf := len(f)
-
-		if nf >= 4 && f[1] == "terminals," && f[3] == "nonterminals" {
-			// We're done.
-			break
-		}
-
-		if nf >= 2 && f[0] == "state" {
-			state = xatoi(f[1])
-			continue
-		}
-		if nf >= 3 && (f[1] == "shift" || f[1] == "goto") {
-			shift[state] = append(shift[state], action{f[0], xatoi(f[2])})
-			continue
-		}
-		if nf >= 3 && f[1] == "reduce" {
-			reduce[state] = append(reduce[state], action{f[0], xatoi(f[2])})
-			continue
-		}
-		if nf >= 3 && strings.HasSuffix(f[0], ":") && strings.HasPrefix(f[nf-1], "(") && strings.HasSuffix(f[nf-1], ")") {
-			n := xatoi(trimParen(f[nf-1]))
-
-			size := nf - 2
-			if size == 1 && f[1] == "." {
-				size = 0
-			}
-
-			rules[n] = rule{strings.TrimSuffix(f[0], ":"), size}
-			continue
-		}
-	}
-}
-
-func runMachine(w io.Writer, s string) {
-	f := strings.Fields(s)
-
-	// Run it through the LR machine and print the induced "yystate, yychar,"
-	// at the point where the error happens.
-
-	var stack []int
-	state := 0
-	i := 1
-	tok := ""
-
-Loop:
-	if tok == "" && i < len(f) {
-		tok = f[i]
-		i++
-	}
-
-	for _, a := range shift[state] {
-		if a.token == tok {
-			if false {
-				fmt.Println("SHIFT ", tok, " ", state, " -> ", a)
-			}
-			stack = append(stack, state)
-			state = a.n
-			tok = ""
-			goto Loop
-		}
-	}
-
-	for _, a := range reduce[state] {
-		if a.token == tok || a.token == "." {
-			stack = append(stack, state)
-			rule, ok := rules[a.n]
-			if !ok {
-				log.Fatal("missing rule")
-			}
-			stack = stack[:len(stack)-rule.size]
-			state = stack[len(stack)-1]
-			stack = stack[:len(stack)-1]
-			if tok != "" {
-				i--
-			}
-			tok = rule.lhs
-			if false {
-				fmt.Println("REDUCE ", stack, " ", state, " ", tok, " rule ", rule)
-			}
-			goto Loop
-		}
-	}
-
-	// No shift or reduce applied - found the error.
-	fmt.Fprintf(w, "\t{%d, %s,\n", state, tok)
-}
-
-func processGoErrors() {
-	r, err := os.Open("go.errors")
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer r.Close()
-
-	w, err := os.Create("yymsg.go")
-	if err != nil {
-		log.Fatal(err)
-	}
-	defer w.Close()
-
-	fmt.Fprintf(w, "// DO NOT EDIT - generated with go generate\n\n")
-
-	scanner := bufio.NewScanner(r)
-	for scanner.Scan() {
-		s := scanner.Text()
-
-		// Treat % as first field on line as introducing a pattern (token sequence).
-		if strings.HasPrefix(strings.TrimSpace(s), "%") {
-			runMachine(w, s)
-			continue
-		}
-
-		fmt.Fprintln(w, s)
-	}
-}
-
-func main() {
-	readYaccOutput()
-	processGoErrors()
-}
diff --git a/src/cmd/internal/gc/yymsg.go b/src/cmd/internal/gc/yymsg.go
deleted file mode 100644
index cb45cb8d1b..0000000000
--- a/src/cmd/internal/gc/yymsg.go
+++ /dev/null
@@ -1,83 +0,0 @@
-// DO NOT EDIT - generated with go generate
-
-// Copyright 2010 The Go Authors.  All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Example-based syntax error messages.
-// See yaccerrors.go.
-
-package gc
-
-var yymsg = []struct {
-	yystate int
-	yychar  int
-	msg     string
-}{
-	// Each line of the form % token list
-	// is converted by yaccerrors.go into the yystate and yychar caused
-	// by that token list.
-
-	{332, ',',
-		"unexpected comma during import block"},
-
-	{89, ';',
-		"missing import path; require quoted string"},
-
-	{390, ';',
-		"missing { after if clause"},
-
-	{387, ';',
-		"missing { after switch clause"},
-
-	{279, ';',
-		"missing { after for clause"},
-
-	{498, LBODY,
-		"missing { after for clause"},
-
-	{17, '{',
-		"unexpected semicolon or newline before {"},
-
-	{111, ';',
-		"unexpected semicolon or newline in type declaration"},
-
-	{78, '}',
-		"unexpected } in channel type"},
-
-	{78, ')',
-		"unexpected ) in channel type"},
-
-	{78, ',',
-		"unexpected comma in channel type"},
-
-	{416, LELSE,
-		"unexpected semicolon or newline before else"},
-
-	{329, ',',
-		"name list not allowed in interface type"},
-
-	{279, LVAR,
-		"var declaration not allowed in for initializer"},
-
-	{25, '{',
-		"unexpected { at end of statement"},
-
-	{371, '{',
-		"unexpected { at end of statement"},
-
-	{122, ';',
-		"argument to go/defer must be function call"},
-
-	{398, ';',
-		"need trailing comma before newline in composite literal"},
-
-	{414, ';',
-		"need trailing comma before newline in composite literal"},
-
-	{124, LNAME,
-		"nested func not allowed"},
-
-	{650, ';',
-		"else must be followed by if or statement block"},
-}
diff --git a/src/cmd/yacc/yacc.go b/src/cmd/yacc/yacc.go
index 5c7b0b71b1..53c0fab174 100644
--- a/src/cmd/yacc/yacc.go
+++ b/src/cmd/yacc/yacc.go
@@ -128,6 +128,7 @@ const (
 	TYPEDEF
 	TYPENAME
 	UNION
+	ERROR
 )
 
 const ENDFILE = 0
@@ -325,8 +326,24 @@ var resrv = []Resrv{
 	{"type", TYPEDEF},
 	{"union", UNION},
 	{"struct", UNION},
+	{"error", ERROR},
 }
 
+type Error struct {
+	lineno int
+	tokens []string
+	msg    string
+}
+
+var errors []Error
+
+type Row struct {
+	actions       []int
+	defaultAction int
+}
+
+var stateTable []Row
+
 var zznewstate = 0
 
 const EOF = -1
@@ -402,6 +419,27 @@ outer:
 			}
 			start = chfind(1, tokname)
 
+		case ERROR:
+			lno := lineno
+			var tokens []string
+			for {
+				t := gettok()
+				if t == ':' {
+					break
+				}
+				if t != IDENTIFIER && t != IDENTCOLON {
+					errorf("bad syntax in %%error")
+				}
+				tokens = append(tokens, tokname)
+				if t == IDENTCOLON {
+					break
+				}
+			}
+			if gettok() != IDENTIFIER {
+				errorf("bad syntax in %%error")
+			}
+			errors = append(errors, Error{lno, tokens, tokname})
+
 		case TYPEDEF:
 			t = gettok()
 			if t != TYPENAME {
@@ -2155,6 +2193,10 @@ func output() {
 	}
 	fmt.Fprintf(ftable, "\nvar %sExca = [...]int{\n", prefix)
 
+	if len(errors) > 0 {
+		stateTable = make([]Row, nstate)
+	}
+
 	noset := mkset()
 
 	// output the stuff for state i
@@ -2368,6 +2410,15 @@ func wrstate(i int) {
 	var j0, j1, u int
 	var pp, qq int
 
+	if len(errors) > 0 {
+		actions := append([]int(nil), temp1...)
+		defaultAction := ERRCODE
+		if lastred != 0 {
+			defaultAction = -lastred
+		}
+		stateTable[i] = Row{actions, defaultAction}
+	}
+
 	if foutput == nil {
 		return
 	}
@@ -2914,6 +2965,20 @@ func others() {
 	}
 	fmt.Fprintf(ftable, "%d,\n}\n", 0)
 
+	// Custom error messages.
+	fmt.Fprintf(ftable, "\n")
+	fmt.Fprintf(ftable, "var %sErrorMessages = [...]struct {\n", prefix)
+	fmt.Fprintf(ftable, "\tstate int\n")
+	fmt.Fprintf(ftable, "\ttoken int\n")
+	fmt.Fprintf(ftable, "\tmsg   string\n")
+	fmt.Fprintf(ftable, "}{\n")
+	for _, error := range errors {
+		lineno = error.lineno
+		state, token := runMachine(error.tokens)
+		fmt.Fprintf(ftable, "\t{%v, %v, %s},\n", state, token, error.msg)
+	}
+	fmt.Fprintf(ftable, "}\n")
+
 	// copy parser text
 	ch := getrune(finput)
 	for ch != EOF {
@@ -2932,6 +2997,59 @@ func others() {
 	fmt.Fprintf(ftable, "%v", parts[1])
 }
 
+func runMachine(tokens []string) (state, token int) {
+	var stack []int
+	i := 0
+	token = -1
+
+Loop:
+	if token < 0 {
+		token = chfind(2, tokens[i])
+		i++
+	}
+
+	row := stateTable[state]
+
+	c := token
+	if token >= NTBASE {
+		c = token - NTBASE + ntokens
+	}
+	action := row.actions[c]
+	if action == 0 {
+		action = row.defaultAction
+	}
+
+	switch {
+	case action == ACCEPTCODE:
+		errorf("tokens are accepted")
+		return
+	case action == ERRCODE:
+		if token >= NTBASE {
+			errorf("error at non-terminal token %s", symnam(token))
+		}
+		return
+	case action > 0:
+		// Shift to state action.
+		stack = append(stack, state)
+		state = action
+		token = -1
+		goto Loop
+	default:
+		// Reduce by production -action.
+		prod := prdptr[-action]
+		if rhsLen := len(prod) - 2; rhsLen > 0 {
+			n := len(stack) - rhsLen
+			state = stack[n]
+			stack = stack[:n]
+		}
+		if token >= 0 {
+			i--
+		}
+		token = prod[0]
+		goto Loop
+	}
+}
+
 func arout(s string, v []int, n int) {
 	s = prefix + s
 	fmt.Fprintf(ftable, "var %v = [...]int{\n", s)
@@ -3212,7 +3330,6 @@ type $$Parser interface {
 
 type $$ParserImpl struct {
 	lookahead func() int
-	state     func() int
 }
 
 func (p *$$ParserImpl) Lookahead() int {
@@ -3222,7 +3339,6 @@ func (p *$$ParserImpl) Lookahead() int {
 func $$NewParser() $$Parser {
 	p := &$$ParserImpl{
 		lookahead: func() int { return -1 },
-		state:     func() int { return -1 },
 	}
 	return p
 }
@@ -3253,6 +3369,13 @@ func $$ErrorMessage(state, lookAhead int) string {
 	if !$$ErrorVerbose {
 		return "syntax error"
 	}
+
+	for _, e := range $$ErrorMessages {
+		if e.state == state && e.token == lookAhead {
+			return "syntax error: " + e.msg
+		}
+	}
+
 	res := "syntax error: unexpected " + $$Tokname(lookAhead)
 
 	// To match Bison, suggest at most four expected tokens.
@@ -3355,7 +3478,6 @@ func ($$rcvr *$$ParserImpl) Parse($$lex $$Lexer) int {
 	$$state := 0
 	$$char := -1
 	$$token := -1 // $$char translated into internal numbering
-	$$rcvr.state = func() int { return $$state }
 	$$rcvr.lookahead = func() int { return $$char }
 	defer func() {
 		// Make sure we report no lookahead when not parsing.

From 1467776b17c7dc232f5586944785f85f48862b49 Mon Sep 17 00:00:00 2001
From: Matthew Dempsky 
Date: Mon, 20 Apr 2015 13:32:40 -0700
Subject: [PATCH 113/232] cmd/internal/obj: update callers to
 Linkline{fmt,hist} and remove

Does the TODOs added by https://golang.org/cl/7623.

Passes rsc.io/toolstash/buildall.

Change-Id: I23913a8f03834640e9795d48318febb3f88c10f9
Reviewed-on: https://go-review.googlesource.com/9160
Reviewed-by: Russ Cox 
---
 src/cmd/asm/internal/lex/input.go     |  3 +-
 src/cmd/asm/internal/lex/tokenizer.go |  6 ++--
 src/cmd/internal/asm/lexbody.go       |  4 +--
 src/cmd/internal/asm/macbody.go       |  5 ++--
 src/cmd/internal/gc/lex.go            |  8 +++---
 src/cmd/internal/gc/subr.go           | 40 +++++++++++++++------------
 src/cmd/internal/gc/util.go           |  3 +-
 src/cmd/internal/obj/line_test.go     | 14 +++++-----
 src/cmd/internal/obj/obj.go           | 33 ----------------------
 src/cmd/internal/obj/util.go          |  4 +--
 10 files changed, 44 insertions(+), 76 deletions(-)

diff --git a/src/cmd/asm/internal/lex/input.go b/src/cmd/asm/internal/lex/input.go
index 730042b149..7e495b8edf 100644
--- a/src/cmd/asm/internal/lex/input.go
+++ b/src/cmd/asm/internal/lex/input.go
@@ -13,7 +13,6 @@ import (
 	"text/scanner"
 
 	"cmd/asm/internal/flags"
-	"cmd/internal/obj"
 )
 
 // Input is the main input: a stack of readers and some macro definitions.
@@ -436,7 +435,7 @@ func (in *Input) line() {
 	if tok != '\n' {
 		in.Error("unexpected token at end of #line: ", tok)
 	}
-	obj.Linklinehist(linkCtxt, histLine, file, line)
+	linkCtxt.LineHist.Update(histLine, file, line)
 	in.Stack.SetPos(line, file)
 }
 
diff --git a/src/cmd/asm/internal/lex/tokenizer.go b/src/cmd/asm/internal/lex/tokenizer.go
index 28a4b85253..6a4d95491f 100644
--- a/src/cmd/asm/internal/lex/tokenizer.go
+++ b/src/cmd/asm/internal/lex/tokenizer.go
@@ -10,8 +10,6 @@ import (
 	"strings"
 	"text/scanner"
 	"unicode"
-
-	"cmd/internal/obj"
 )
 
 // A Tokenizer is a simple wrapping of text/scanner.Scanner, configured
@@ -40,7 +38,7 @@ func NewTokenizer(name string, r io.Reader, file *os.File) *Tokenizer {
 	s.Position.Filename = name
 	s.IsIdentRune = isIdentRune
 	if file != nil {
-		obj.Linklinehist(linkCtxt, histLine, name, 0)
+		linkCtxt.LineHist.Push(histLine, name)
 	}
 	return &Tokenizer{
 		s:        &s,
@@ -149,6 +147,6 @@ func (t *Tokenizer) Close() {
 	if t.file != nil {
 		t.file.Close()
 		// It's an open file, so pop the line history.
-		obj.Linklinehist(linkCtxt, histLine, "", 0)
+		linkCtxt.LineHist.Pop(histLine)
 	}
 }
diff --git a/src/cmd/internal/asm/lexbody.go b/src/cmd/internal/asm/lexbody.go
index b5e5d1eee2..a1519c8566 100644
--- a/src/cmd/internal/asm/lexbody.go
+++ b/src/cmd/internal/asm/lexbody.go
@@ -149,7 +149,7 @@ func newfile(s string, f *os.File) {
 	}
 
 	fi.P = nil
-	obj.Linklinehist(Ctxt, int(Lineno), s, 0)
+	Ctxt.LineHist.Push(int(Lineno), s)
 }
 
 var thetext *obj.LSym
@@ -630,7 +630,7 @@ loop:
 	n, _ = i.F.Read(i.B[:])
 	if n == 0 {
 		i.F.Close()
-		obj.Linklinehist(Ctxt, int(Lineno), "", 0)
+		Ctxt.LineHist.Pop(int(Lineno))
 		goto pop
 	}
 	fi.P = i.B[1:n]
diff --git a/src/cmd/internal/asm/macbody.go b/src/cmd/internal/asm/macbody.go
index c488ea1e56..4565d3a37f 100644
--- a/src/cmd/internal/asm/macbody.go
+++ b/src/cmd/internal/asm/macbody.go
@@ -32,7 +32,6 @@ package asm
 
 import (
 	"bytes"
-	"cmd/internal/obj"
 	"fmt"
 	"os"
 	"strings"
@@ -683,7 +682,7 @@ func maclin() {
 	}
 
 nn:
-	obj.Linklinehist(Ctxt, int(Lineno), symb, int(n))
+	Ctxt.LineHist.Update(int(Lineno), symb, int(n))
 	return
 
 bad:
@@ -796,7 +795,7 @@ func macprag() {
 		/*
 		 * put pragma-line in as a funny history
 		 */
-		obj.Linklinehist(Ctxt, int(Lineno), symb, -1)
+		Ctxt.AddImport(symb)
 		return
 	}
 	if s != nil && s.Name == "pack" {
diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/internal/gc/lex.go
index 9e2baec220..92c079e154 100644
--- a/src/cmd/internal/gc/lex.go
+++ b/src/cmd/internal/gc/lex.go
@@ -313,7 +313,7 @@ func Main() {
 	lexlineno = 1
 
 	for _, infile = range flag.Args() {
-		linehist(infile, 0, 0)
+		linehistpush(infile)
 
 		curio.infile = infile
 		var err error
@@ -344,7 +344,7 @@ func Main() {
 			errorexit()
 		}
 
-		linehist("", 0, 0)
+		linehistpop()
 		if curio.bin != nil {
 			obj.Bterm(curio.bin)
 		}
@@ -763,7 +763,7 @@ func importfile(f *Val, line int) {
 
 	// assume files move (get installed)
 	// so don't record the full path.
-	linehist(file[len(file)-len(path_)-2:], -1, 1) // acts as #pragma lib
+	linehistpragma(file[len(file)-len(path_)-2:]) // acts as #pragma lib
 
 	/*
 	 * position the input right
@@ -1654,7 +1654,7 @@ func getlinepragma() int {
 	}
 
 	name = text[:linep-1]
-	linehist(name, int32(n), 0)
+	linehistupdate(name, n)
 	return c
 
 out:
diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/internal/gc/subr.go
index 74415be49a..33741c3baf 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/internal/gc/subr.go
@@ -199,26 +199,32 @@ func Fatal(fmt_ string, args ...interface{}) {
 	errorexit()
 }
 
-func linehist(file string, off int32, relative int) {
+func linehistpragma(file string) {
 	if Debug['i'] != 0 {
-		if file != "" {
-			if off < 0 {
-				fmt.Printf("pragma %s", file)
-			} else if off > 0 {
-				fmt.Printf("line %s", file)
-			} else {
-				fmt.Printf("import %s", file)
-			}
-		} else {
-			fmt.Printf("end of import")
-		}
-		fmt.Printf(" at line %v\n", Ctxt.Line(int(lexlineno)))
+		fmt.Printf("pragma %s at line %v\n", file, Ctxt.Line(int(lexlineno)))
 	}
+	Ctxt.AddImport(file)
+}
 
-	if off < 0 && file[0] != '/' && relative == 0 {
-		file = fmt.Sprintf("%s/%s", Ctxt.Pathname, file)
+func linehistpush(file string) {
+	if Debug['i'] != 0 {
+		fmt.Printf("import %s at line %v\n", file, Ctxt.Line(int(lexlineno)))
 	}
-	obj.Linklinehist(Ctxt, int(lexlineno), file, int(off))
+	Ctxt.LineHist.Push(int(lexlineno), file)
+}
+
+func linehistpop() {
+	if Debug['i'] != 0 {
+		fmt.Printf("end of import at line %v\n", Ctxt.Line(int(lexlineno)))
+	}
+	Ctxt.LineHist.Pop(int(lexlineno))
+}
+
+func linehistupdate(file string, off int) {
+	if Debug['i'] != 0 {
+		fmt.Printf("line %s at line %v\n", file, Ctxt.Line(int(lexlineno)))
+	}
+	Ctxt.LineHist.Update(int(lexlineno), file, off)
 }
 
 func setlineno(n *Node) int32 {
@@ -2345,7 +2351,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
 	lineno = lexlineno
 	if genwrapper_linehistdone == 0 {
 		// All the wrappers can share the same linehist entry.
-		linehist("", 0, 0)
+		linehistpush("")
 
 		genwrapper_linehistdone = 1
 	}
diff --git a/src/cmd/internal/gc/util.go b/src/cmd/internal/gc/util.go
index 5dc6561b48..c59af0665b 100644
--- a/src/cmd/internal/gc/util.go
+++ b/src/cmd/internal/gc/util.go
@@ -1,7 +1,6 @@
 package gc
 
 import (
-	"cmd/internal/obj"
 	"os"
 	"runtime"
 	"runtime/pprof"
@@ -10,7 +9,7 @@ import (
 )
 
 func (n *Node) Line() string {
-	return obj.Linklinefmt(Ctxt, int(n.Lineno), false, false)
+	return Ctxt.LineHist.LineString(int(n.Lineno))
 }
 
 func atoi(s string) int {
diff --git a/src/cmd/internal/obj/line_test.go b/src/cmd/internal/obj/line_test.go
index dde5d64e17..5486f0d648 100644
--- a/src/cmd/internal/obj/line_test.go
+++ b/src/cmd/internal/obj/line_test.go
@@ -13,13 +13,13 @@ func TestLineHist(t *testing.T) {
 	ctxt := new(Link)
 	ctxt.Hash = make(map[SymVer]*LSym)
 
-	Linklinehist(ctxt, 1, "a.c", 0)
-	Linklinehist(ctxt, 3, "a.h", 0)
-	Linklinehist(ctxt, 5, "", 0)
-	Linklinehist(ctxt, 7, "linedir", 2)
-	Linklinehist(ctxt, 9, "", 0)
-	Linklinehist(ctxt, 11, "b.c", 0)
-	Linklinehist(ctxt, 13, "", 0)
+	ctxt.LineHist.Push(1, "a.c")
+	ctxt.LineHist.Push(3, "a.h")
+	ctxt.LineHist.Pop(5)
+	ctxt.LineHist.Update(7, "linedir", 2)
+	ctxt.LineHist.Pop(9)
+	ctxt.LineHist.Push(11, "b.c")
+	ctxt.LineHist.Pop(13)
 
 	var expect = []string{
 		0:  "??:0",
diff --git a/src/cmd/internal/obj/obj.go b/src/cmd/internal/obj/obj.go
index 39db2396e7..af3290d3a5 100644
--- a/src/cmd/internal/obj/obj.go
+++ b/src/cmd/internal/obj/obj.go
@@ -241,12 +241,6 @@ func (h *LineHist) LineString(lineno int) string {
 	return text
 }
 
-// TODO(rsc): Replace call sites with use of ctxt.LineHist.
-// Note that all call sites use showAll=false, showFullPath=false.
-func Linklinefmt(ctxt *Link, lineno int, showAll, showFullPath bool) string {
-	return ctxt.LineHist.LineString(lineno)
-}
-
 // FileLine returns the file name and line number
 // at the top of the stack for the given lineno.
 func (h *LineHist) FileLine(lineno int) (file string, line int) {
@@ -287,30 +281,3 @@ func linkgetline(ctxt *Link, lineno int32, f **LSym, l *int32) {
 func Linkprfile(ctxt *Link, line int) {
 	fmt.Printf("%s ", ctxt.LineHist.LineString(line))
 }
-
-// Linklinehist pushes, amends, or pops an entry on the line history stack.
-// If f != "" and n == 0, the call pushes the start of a new file named f at lineno.
-// If f != "" and n > 0, the call amends the top of the stack to record that lineno
-// now corresponds to f at line n.
-// If f == "", the call pops the topmost entry from the stack, picking up
-// the parent file at the line following the one where the corresponding push occurred.
-//
-// If n < 0, linklinehist records f as a package required by the current compilation
-// (nothing to do with line numbers).
-//
-// TODO(rsc): Replace uses with direct calls to ctxt.Hist methods.
-func Linklinehist(ctxt *Link, lineno int, f string, n int) {
-	switch {
-	case n < 0:
-		ctxt.AddImport(f)
-
-	case f == "":
-		ctxt.LineHist.Pop(lineno)
-
-	case n == 0:
-		ctxt.LineHist.Push(lineno, f)
-
-	default:
-		ctxt.LineHist.Update(lineno, f, n)
-	}
-}
diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go
index ac49543fdf..317ee4f14d 100644
--- a/src/cmd/internal/obj/util.go
+++ b/src/cmd/internal/obj/util.go
@@ -241,7 +241,7 @@ func Atoi(s string) int {
 }
 
 func (p *Prog) Line() string {
-	return Linklinefmt(p.Ctxt, int(p.Lineno), false, false)
+	return p.Ctxt.LineHist.LineString(int(p.Lineno))
 }
 
 var armCondCode = []string{
@@ -340,7 +340,7 @@ func (ctxt *Link) NewProg() *Prog {
 }
 
 func (ctxt *Link) Line(n int) string {
-	return Linklinefmt(ctxt, n, false, false)
+	return ctxt.LineHist.LineString(n)
 }
 
 func Getcallerpc(interface{}) uintptr {

From 40fad6c286ca57317e94aeca50b75fa3444ca1fa Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 14 May 2015 16:47:53 -0700
Subject: [PATCH 114/232] go/parser: better error message for missing ',' in
 lists

Fixes #8940.

Change-Id: Ie9e5149983518ba8d56ddd82ac8f4cde6b644167
Reviewed-on: https://go-review.googlesource.com/10089
Reviewed-by: Alan Donovan 
---
 src/go/parser/parser.go     | 21 ++++++++++++---------
 src/go/parser/short_test.go |  5 +++--
 2 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index 0095d7facf..fb6ca76a77 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -412,14 +412,17 @@ func (p *parser) expectSemi() {
 	}
 }
 
-func (p *parser) atComma(context string) bool {
+func (p *parser) atComma(context string, follow token.Token) bool {
 	if p.tok == token.COMMA {
 		return true
 	}
-	if p.tok == token.SEMICOLON && p.lit == "\n" {
-		p.error(p.pos, "missing ',' before newline in "+context)
-		return true // "insert" the comma and continue
-
+	if p.tok != follow {
+		msg := "missing ','"
+		if p.tok == token.SEMICOLON && p.lit == "\n" {
+			msg += " before newline"
+		}
+		p.error(p.pos, msg+" in "+context)
+		return true // "insert" comma and continue
 	}
 	return false
 }
@@ -825,7 +828,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
 		// parameter or result variable is the function body.
 		p.declare(field, nil, scope, ast.Var, idents...)
 		p.resolve(typ)
-		if !p.atComma("parameter list") {
+		if !p.atComma("parameter list", token.RPAREN) {
 			return
 		}
 		p.next()
@@ -838,7 +841,7 @@ func (p *parser) parseParameterList(scope *ast.Scope, ellipsisOk bool) (params [
 			// parameter or result variable is the function body.
 			p.declare(field, nil, scope, ast.Var, idents...)
 			p.resolve(typ)
-			if !p.atComma("parameter list") {
+			if !p.atComma("parameter list", token.RPAREN) {
 				break
 			}
 			p.next()
@@ -1248,7 +1251,7 @@ func (p *parser) parseCallOrConversion(fun ast.Expr) *ast.CallExpr {
 			ellipsis = p.pos
 			p.next()
 		}
-		if !p.atComma("argument list") {
+		if !p.atComma("argument list", token.RPAREN) {
 			break
 		}
 		p.next()
@@ -1323,7 +1326,7 @@ func (p *parser) parseElementList() (list []ast.Expr) {
 
 	for p.tok != token.RBRACE && p.tok != token.EOF {
 		list = append(list, p.parseElement())
-		if !p.atComma("composite literal") {
+		if !p.atComma("composite literal", token.RBRACE) {
 			break
 		}
 		p.next()
diff --git a/src/go/parser/short_test.go b/src/go/parser/short_test.go
index 970ef2d3fa..ef2ffadbd9 100644
--- a/src/go/parser/short_test.go
+++ b/src/go/parser/short_test.go
@@ -99,8 +99,9 @@ var invalids = []string{
 	`package p; func f() { for i /* ERROR "boolean or range expression" */ , x := []string {} }`,
 	`package p; func f() { go f /* ERROR HERE "function must be invoked" */ }`,
 	`package p; func f() { defer func() {} /* ERROR HERE "function must be invoked" */ }`,
-	`package p; func f() { go func() { func() { f(x func /* ERROR "expected '\)'" */ (){}) } } }`,
-	`package p; func f() (a b string /* ERROR "expected '\)'" */ , ok bool)`,         // issue 8656
+	`package p; func f() { go func() { func() { f(x func /* ERROR "missing ','" */ (){}) } } }`,
+	`package p; func f(x func(), u v func /* ERROR "missing ','" */ ()){}`,
+	`package p; func f() (a b string /* ERROR "missing ','" */ , ok bool)`,           // issue 8656
 	`package p; var x /* ERROR "missing variable type or initialization" */ , y, z;`, // issue 9639
 	`package p; const x /* ERROR "missing constant value" */ ;`,                      // issue 9639
 	`package p; const x /* ERROR "missing constant value" */ int;`,                   // issue 9639

From ef7e1085658de69b6a2e365e71a955105b3a4feb Mon Sep 17 00:00:00 2001
From: Alex A Skinner 
Date: Tue, 12 May 2015 23:56:56 -0400
Subject: [PATCH 115/232] net: redo resolv.conf recheck implementation

The previous implementation spawned an extra goroutine to handle
rechecking resolv.conf for changes.

This change eliminates the extra goroutine, and has rechecking
done as part of a lookup.  A side effect of this change is that the
first lookup after a resolv.conf change will now succeed, whereas
previously it would have failed.  It also fixes rechecking logic to
ignore resolv.conf parsing errors as it should.

Fixes #8652
Fixes #10576
Fixes #10649
Fixes #10650
Fixes #10845

Change-Id: I502b587c445fa8eca5207ca4f2c8ec8c339fec7f
Reviewed-on: https://go-review.googlesource.com/9991
Run-TryBot: Matthew Dempsky 
TryBot-Result: Gobot Gobot 
Reviewed-by: Josh Bleecher Snyder 
Reviewed-by: Mikio Hara 
Reviewed-by: Brad Fitzpatrick 
---
 src/net/dnsclient_unix.go      | 119 ++++++++++++++++++---------------
 src/net/dnsclient_unix_test.go |  38 +++--------
 2 files changed, 74 insertions(+), 83 deletions(-)

diff --git a/src/net/dnsclient_unix.go b/src/net/dnsclient_unix.go
index 5a4411f5c7..fab515f5c2 100644
--- a/src/net/dnsclient_unix.go
+++ b/src/net/dnsclient_unix.go
@@ -214,95 +214,106 @@ func convertRR_AAAA(records []dnsRR) []IP {
 	return addrs
 }
 
+// cfg is used for the storage and reparsing of /etc/resolv.conf
 var cfg struct {
-	ch        chan struct{}
+	// ch is used as a semaphore that only allows one lookup at a time to
+	// recheck resolv.conf.  It acts as guard for lastChecked and modTime.
+	ch          chan struct{}
+	lastChecked time.Time // last time resolv.conf was checked
+	modTime     time.Time // time of resolv.conf modification
+
 	mu        sync.RWMutex // protects dnsConfig
-	dnsConfig *dnsConfig
+	dnsConfig *dnsConfig   // parsed resolv.conf structure used in lookups
 }
 
 var onceLoadConfig sync.Once
 
-// Assume dns config file is /etc/resolv.conf here
-func loadDefaultConfig() {
-	loadConfig("/etc/resolv.conf", 5*time.Second, nil)
-}
-
-func loadConfig(resolvConfPath string, reloadTime time.Duration, quit <-chan chan struct{}) {
-	var mtime time.Time
-	cfg.ch = make(chan struct{}, 1)
-	if fi, err := os.Stat(resolvConfPath); err == nil {
-		mtime = fi.ModTime()
+func initCfg() {
+	// Set dnsConfig, modTime, and lastChecked so we don't parse
+	// resolv.conf twice the first time.
+	cfg.dnsConfig = systemConf().resolv
+	if cfg.dnsConfig == nil {
+		cfg.dnsConfig = dnsReadConfig("/etc/resolv.conf")
 	}
 
-	cfg.dnsConfig = dnsReadConfig(resolvConfPath)
+	if fi, err := os.Stat("/etc/resolv.conf"); err == nil {
+		cfg.modTime = fi.ModTime()
+	}
+	cfg.lastChecked = time.Now()
 
-	go func() {
-		for {
-			time.Sleep(reloadTime)
-			select {
-			case qresp := <-quit:
-				qresp <- struct{}{}
-				return
-			case <-cfg.ch:
-			}
+	// Prepare ch so that only one loadConfig may run at once
+	cfg.ch = make(chan struct{}, 1)
+	cfg.ch <- struct{}{}
+}
 
-			// In case of error, we keep the previous config
-			fi, err := os.Stat(resolvConfPath)
-			if err != nil {
-				continue
-			}
-			// If the resolv.conf mtime didn't change, do not reload
-			m := fi.ModTime()
-			if m.Equal(mtime) {
-				continue
-			}
-			mtime = m
-			// In case of error, we keep the previous config
-			if ncfg := dnsReadConfig(resolvConfPath); ncfg.err == nil {
-				cfg.mu.Lock()
-				cfg.dnsConfig = ncfg
-				cfg.mu.Unlock()
-			}
+func loadConfig(resolvConfPath string) {
+	onceLoadConfig.Do(initCfg)
+
+	// ensure only one loadConfig at a time checks /etc/resolv.conf
+	select {
+	case <-cfg.ch:
+		defer func() { cfg.ch <- struct{}{} }()
+	default:
+		return
+	}
+
+	now := time.Now()
+	if cfg.lastChecked.After(now.Add(-5 * time.Second)) {
+		return
+	}
+	cfg.lastChecked = now
+
+	if fi, err := os.Stat(resolvConfPath); err == nil {
+		if fi.ModTime().Equal(cfg.modTime) {
+			return
 		}
-	}()
+		cfg.modTime = fi.ModTime()
+	} else {
+		// If modTime wasn't set prior, assume nothing has changed.
+		if cfg.modTime.IsZero() {
+			return
+		}
+		cfg.modTime = time.Time{}
+	}
+
+	ncfg := dnsReadConfig(resolvConfPath)
+	cfg.mu.Lock()
+	cfg.dnsConfig = ncfg
+	cfg.mu.Unlock()
 }
 
 func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
 	if !isDomainName(name) {
 		return name, nil, &DNSError{Err: "invalid domain name", Name: name}
 	}
-	onceLoadConfig.Do(loadDefaultConfig)
-
-	select {
-	case cfg.ch <- struct{}{}:
-	default:
-	}
 
+	loadConfig("/etc/resolv.conf")
 	cfg.mu.RLock()
-	defer cfg.mu.RUnlock()
+	resolv := cfg.dnsConfig
+	cfg.mu.RUnlock()
 
 	// If name is rooted (trailing dot) or has enough dots,
 	// try it by itself first.
 	rooted := len(name) > 0 && name[len(name)-1] == '.'
-	if rooted || count(name, '.') >= cfg.dnsConfig.ndots {
+	if rooted || count(name, '.') >= resolv.ndots {
 		rname := name
 		if !rooted {
 			rname += "."
 		}
 		// Can try as ordinary name.
-		cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+		cname, rrs, err = tryOneName(resolv, rname, qtype)
 		if rooted || err == nil {
 			return
 		}
 	}
 
 	// Otherwise, try suffixes.
-	for i := 0; i < len(cfg.dnsConfig.search); i++ {
-		rname := name + "." + cfg.dnsConfig.search[i]
+	for _, suffix := range resolv.search {
+		rname := name + "." + suffix
 		if rname[len(rname)-1] != '.' {
 			rname += "."
 		}
-		cname, rrs, err = tryOneName(cfg.dnsConfig, rname, qtype)
+		cname, rrs, err = tryOneName(resolv, rname, qtype)
 		if err == nil {
 			return
 		}
@@ -310,8 +321,8 @@ func lookup(name string, qtype uint16) (cname string, rrs []dnsRR, err error) {
 
 	// Last ditch effort: try unsuffixed only if we haven't already,
 	// that is, name is not rooted and has less than ndots dots.
-	if count(name, '.') < cfg.dnsConfig.ndots {
-		cname, rrs, err = tryOneName(cfg.dnsConfig, name+".", qtype)
+	if count(name, '.') < resolv.ndots {
+		cname, rrs, err = tryOneName(resolv, name+".", qtype)
 		if err == nil {
 			return
 		}
diff --git a/src/net/dnsclient_unix_test.go b/src/net/dnsclient_unix_test.go
index 4ea24b6014..06c9ad3134 100644
--- a/src/net/dnsclient_unix_test.go
+++ b/src/net/dnsclient_unix_test.go
@@ -94,10 +94,8 @@ func TestSpecialDomainName(t *testing.T) {
 
 type resolvConfTest struct {
 	*testing.T
-	dir     string
-	path    string
-	started bool
-	quitc   chan chan struct{}
+	dir  string
+	path string
 }
 
 func newResolvConfTest(t *testing.T) *resolvConfTest {
@@ -106,24 +104,15 @@ func newResolvConfTest(t *testing.T) *resolvConfTest {
 		t.Fatal(err)
 	}
 
-	// Disable the default loadConfig
-	onceLoadConfig.Do(func() {})
-
 	r := &resolvConfTest{
-		T:     t,
-		dir:   dir,
-		path:  path.Join(dir, "resolv.conf"),
-		quitc: make(chan chan struct{}),
+		T:    t,
+		dir:  dir,
+		path: path.Join(dir, "resolv.conf"),
 	}
 
 	return r
 }
 
-func (r *resolvConfTest) Start() {
-	loadConfig(r.path, 100*time.Millisecond, r.quitc)
-	r.started = true
-}
-
 func (r *resolvConfTest) SetConf(s string) {
 	// Make sure the file mtime will be different once we're done here,
 	// even on systems with coarse (1s) mtime resolution.
@@ -138,12 +127,8 @@ func (r *resolvConfTest) SetConf(s string) {
 		r.Fatalf("failed to write temp file: %v", err)
 	}
 	f.Close()
-
-	if r.started {
-		cfg.ch <- struct{}{} // fill buffer
-		cfg.ch <- struct{}{} // wait for reload to begin
-		cfg.ch <- struct{}{} // wait for reload to complete
-	}
+	cfg.lastChecked = time.Time{}
+	loadConfig(r.path)
 }
 
 func (r *resolvConfTest) WantServers(want []string) {
@@ -155,9 +140,6 @@ func (r *resolvConfTest) WantServers(want []string) {
 }
 
 func (r *resolvConfTest) Close() {
-	resp := make(chan struct{})
-	r.quitc <- resp
-	<-resp
 	if err := os.RemoveAll(r.dir); err != nil {
 		r.Logf("failed to remove temp dir %s: %v", r.dir, err)
 	}
@@ -171,7 +153,6 @@ func TestReloadResolvConfFail(t *testing.T) {
 	r := newResolvConfTest(t)
 	defer r.Close()
 
-	r.Start()
 	r.SetConf("nameserver 8.8.8.8")
 
 	if _, err := goLookupIP("golang.org"); err != nil {
@@ -200,7 +181,6 @@ func TestReloadResolvConfChange(t *testing.T) {
 	r := newResolvConfTest(t)
 	defer r.Close()
 
-	r.Start()
 	r.SetConf("nameserver 8.8.8.8")
 
 	if _, err := goLookupIP("golang.org"); err != nil {
@@ -245,14 +225,14 @@ func BenchmarkGoLookupIPNoSuchHost(b *testing.B) {
 func BenchmarkGoLookupIPWithBrokenNameServer(b *testing.B) {
 	testHookUninstaller.Do(uninstallTestHooks)
 
-	onceLoadConfig.Do(loadDefaultConfig)
-
 	// This looks ugly but it's safe as long as benchmarks are run
 	// sequentially in package testing.
+	<-cfg.ch // keep config from being reloaded upon lookup
 	orig := cfg.dnsConfig
 	cfg.dnsConfig.servers = append([]string{"203.0.113.254"}, cfg.dnsConfig.servers...) // use TEST-NET-3 block, see RFC 5737
 	for i := 0; i < b.N; i++ {
 		goLookupIP("www.example.com")
 	}
 	cfg.dnsConfig = orig
+	cfg.ch <- struct{}{}
 }

From 8fa14ea8b4744576bd28073901154c15813e29de Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Fri, 15 May 2015 10:02:19 -0700
Subject: [PATCH 116/232] cmd/internal/gc: unembed Name field

This is an automated follow-up to CL 10120.
It was generated with a combination of eg and gofmt -r.

No functional changes. Passes toolstash -cmp.

Change-Id: I0dc6d146372012b4cce9cc4064066daa6694eee6
Reviewed-on: https://go-review.googlesource.com/10144
Reviewed-by: Brad Fitzpatrick 
---
 src/cmd/5g/ggen.go               |  2 +-
 src/cmd/5g/gsubr.go              |  2 +-
 src/cmd/6g/ggen.go               |  2 +-
 src/cmd/7g/ggen.go               |  2 +-
 src/cmd/8g/ggen.go               |  2 +-
 src/cmd/8g/gsubr.go              |  2 +-
 src/cmd/9g/ggen.go               |  2 +-
 src/cmd/internal/gc/cgen.go      |  4 ++--
 src/cmd/internal/gc/closure.go   | 28 ++++++++++++++--------------
 src/cmd/internal/gc/esc.go       |  2 +-
 src/cmd/internal/gc/gen.go       |  8 ++++----
 src/cmd/internal/gc/gsubr.go     |  4 ++--
 src/cmd/internal/gc/inl.go       | 22 +++++++++++-----------
 src/cmd/internal/gc/pgen.go      |  4 ++--
 src/cmd/internal/gc/plive.go     |  4 ++--
 src/cmd/internal/gc/sinit.go     |  2 +-
 src/cmd/internal/gc/syntax.go    |  2 +-
 src/cmd/internal/gc/typecheck.go |  6 +++---
 src/cmd/internal/gc/walk.go      |  2 +-
 19 files changed, 51 insertions(+), 51 deletions(-)

diff --git a/src/cmd/5g/ggen.go b/src/cmd/5g/ggen.go
index e4612362a2..2ab5d521bb 100644
--- a/src/cmd/5g/ggen.go
+++ b/src/cmd/5g/ggen.go
@@ -30,7 +30,7 @@ func defframe(ptxt *obj.Prog) {
 	r0 := uint32(0)
 	for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
 		n = l.N
-		if !n.Needzero {
+		if !n.Name.Needzero {
 			continue
 		}
 		if n.Class != gc.PAUTO {
diff --git a/src/cmd/5g/gsubr.go b/src/cmd/5g/gsubr.go
index 2f70bfd468..2a23580b58 100644
--- a/src/cmd/5g/gsubr.go
+++ b/src/cmd/5g/gsubr.go
@@ -89,7 +89,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
 		case gc.ONAME:
 			if n.Class == gc.PPARAMREF {
 				var n1 gc.Node
-				gc.Cgen(n.Heapaddr, &n1)
+				gc.Cgen(n.Name.Heapaddr, &n1)
 				sclean[nsclean-1] = n1
 				n = &n1
 			}
diff --git a/src/cmd/6g/ggen.go b/src/cmd/6g/ggen.go
index 12198d7187..7282ac53e0 100644
--- a/src/cmd/6g/ggen.go
+++ b/src/cmd/6g/ggen.go
@@ -32,7 +32,7 @@ func defframe(ptxt *obj.Prog) {
 	// iterate through declarations - they are sorted in decreasing xoffset order.
 	for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
 		n = l.N
-		if !n.Needzero {
+		if !n.Name.Needzero {
 			continue
 		}
 		if n.Class != gc.PAUTO {
diff --git a/src/cmd/7g/ggen.go b/src/cmd/7g/ggen.go
index af51c31648..ec2eb09e38 100644
--- a/src/cmd/7g/ggen.go
+++ b/src/cmd/7g/ggen.go
@@ -32,7 +32,7 @@ func defframe(ptxt *obj.Prog) {
 	// iterate through declarations - they are sorted in decreasing xoffset order.
 	for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
 		n = l.N
-		if !n.Needzero {
+		if !n.Name.Needzero {
 			continue
 		}
 		if n.Class != gc.PAUTO {
diff --git a/src/cmd/8g/ggen.go b/src/cmd/8g/ggen.go
index baa1b64d1e..bd2c13e867 100644
--- a/src/cmd/8g/ggen.go
+++ b/src/cmd/8g/ggen.go
@@ -30,7 +30,7 @@ func defframe(ptxt *obj.Prog) {
 	ax := uint32(0)
 	for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
 		n = l.N
-		if !n.Needzero {
+		if !n.Name.Needzero {
 			continue
 		}
 		if n.Class != gc.PAUTO {
diff --git a/src/cmd/8g/gsubr.go b/src/cmd/8g/gsubr.go
index b0b0aedabc..99bce6eaba 100644
--- a/src/cmd/8g/gsubr.go
+++ b/src/cmd/8g/gsubr.go
@@ -678,7 +678,7 @@ func split64(n *gc.Node, lo *gc.Node, hi *gc.Node) {
 		case gc.ONAME:
 			if n.Class == gc.PPARAMREF {
 				var n1 gc.Node
-				gc.Cgen(n.Heapaddr, &n1)
+				gc.Cgen(n.Name.Heapaddr, &n1)
 				sclean[nsclean-1] = n1
 				n = &n1
 			}
diff --git a/src/cmd/9g/ggen.go b/src/cmd/9g/ggen.go
index 265536921a..3a10a2a760 100644
--- a/src/cmd/9g/ggen.go
+++ b/src/cmd/9g/ggen.go
@@ -32,7 +32,7 @@ func defframe(ptxt *obj.Prog) {
 	// iterate through declarations - they are sorted in decreasing xoffset order.
 	for l := gc.Curfn.Func.Dcl; l != nil; l = l.Next {
 		n = l.N
-		if !n.Needzero {
+		if !n.Name.Needzero {
 			continue
 		}
 		if n.Class != gc.PAUTO {
diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go
index 7237e863ca..bb022b8351 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/internal/gc/cgen.go
@@ -1579,7 +1579,7 @@ func Agen(n *Node, res *Node) {
 			Fatal("agen: bad ONAME class %#x", n.Class)
 		}
 
-		Cgen(n.Heapaddr, res)
+		Cgen(n.Name.Heapaddr, res)
 		if n.Xoffset != 0 {
 			addOffset(res, n.Xoffset)
 		}
@@ -2517,7 +2517,7 @@ func cgen_call(n *Node, proc int) {
 	}
 
 	// call direct
-	n.Left.Method = true
+	n.Left.Name.Method = true
 
 	Ginscall(n.Left, proc)
 }
diff --git a/src/cmd/internal/gc/closure.go b/src/cmd/internal/gc/closure.go
index 8d5fd5a600..b51e74b77d 100644
--- a/src/cmd/internal/gc/closure.go
+++ b/src/cmd/internal/gc/closure.go
@@ -84,15 +84,15 @@ func typecheckclosure(func_ *Node, top int) {
 
 	for l := func_.Func.Cvars; l != nil; l = l.Next {
 		n = l.N.Closure
-		if !n.Captured {
-			n.Captured = true
-			if n.Decldepth == 0 {
+		if !n.Name.Captured {
+			n.Name.Captured = true
+			if n.Name.Decldepth == 0 {
 				Fatal("typecheckclosure: var %v does not have decldepth assigned", Nconv(n, obj.FmtShort))
 			}
 
 			// Ignore assignments to the variable in straightline code
 			// preceding the first capturing by a closure.
-			if n.Decldepth == decldepth {
+			if n.Name.Decldepth == decldepth {
 				n.Assigned = false
 			}
 		}
@@ -100,7 +100,7 @@ func typecheckclosure(func_ *Node, top int) {
 
 	for l := func_.Func.Dcl; l != nil; l = l.Next {
 		if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) {
-			l.N.Decldepth = 1
+			l.N.Name.Decldepth = 1
 		}
 	}
 
@@ -254,7 +254,7 @@ func capturevars(xfunc *Node) {
 
 		// out parameters will be assigned to implicitly upon return.
 		if outer.Class != PPARAMOUT && !v.Closure.Addrtaken && !v.Closure.Assigned && v.Type.Width <= 128 {
-			v.Byval = true
+			v.Name.Byval = true
 		} else {
 			v.Closure.Addrtaken = true
 			outer = Nod(OADDR, outer, nil)
@@ -266,7 +266,7 @@ func capturevars(xfunc *Node) {
 				name = v.Curfn.Nname.Sym
 			}
 			how := "ref"
-			if v.Byval {
+			if v.Name.Byval {
 				how = "value"
 			}
 			Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Closure.Addrtaken, v.Closure.Assigned, int32(v.Type.Width))
@@ -321,7 +321,7 @@ func transformclosure(xfunc *Node) {
 			}
 			fld = typ(TFIELD)
 			fld.Funarg = 1
-			if v.Byval {
+			if v.Name.Byval {
 				// If v is captured by value, we merely downgrade it to PPARAM.
 				v.Class = PPARAM
 
@@ -335,7 +335,7 @@ func transformclosure(xfunc *Node) {
 				addr = newname(Lookupf("&%s", v.Sym.Name))
 				addr.Type = Ptrto(v.Type)
 				addr.Class = PPARAM
-				v.Heapaddr = addr
+				v.Name.Heapaddr = addr
 				fld.Nname = addr
 			}
 
@@ -375,14 +375,14 @@ func transformclosure(xfunc *Node) {
 			cv = Nod(OCLOSUREVAR, nil, nil)
 
 			cv.Type = v.Type
-			if !v.Byval {
+			if !v.Name.Byval {
 				cv.Type = Ptrto(v.Type)
 			}
 			offset = Rnd(offset, int64(cv.Type.Align))
 			cv.Xoffset = offset
 			offset += cv.Type.Width
 
-			if v.Byval && v.Type.Width <= int64(2*Widthptr) && Thearch.Thechar == '6' {
+			if v.Name.Byval && v.Type.Width <= int64(2*Widthptr) && Thearch.Thechar == '6' {
 				//  If it is a small variable captured by value, downgrade it to PAUTO.
 				// This optimization is currently enabled only for amd64, see:
 				// https://github.com/golang/go/issues/9865
@@ -400,8 +400,8 @@ func transformclosure(xfunc *Node) {
 				addr.Used = true
 				addr.Curfn = xfunc
 				xfunc.Func.Dcl = list(xfunc.Func.Dcl, addr)
-				v.Heapaddr = addr
-				if v.Byval {
+				v.Name.Heapaddr = addr
+				if v.Name.Byval {
 					cv = Nod(OADDR, cv, nil)
 				}
 				body = list(body, Nod(OAS, addr, cv))
@@ -448,7 +448,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node {
 			continue
 		}
 		typ1 = typenod(v.Type)
-		if !v.Byval {
+		if !v.Name.Byval {
 			typ1 = Nod(OIND, typ1, nil)
 		}
 		typ.List = list(typ.List, Nod(ODCLFIELD, newname(v.Sym), typ1))
diff --git a/src/cmd/internal/gc/esc.go b/src/cmd/internal/gc/esc.go
index c816feaa7f..5fb2095bda 100644
--- a/src/cmd/internal/gc/esc.go
+++ b/src/cmd/internal/gc/esc.go
@@ -834,7 +834,7 @@ func esc(e *EscState, n *Node, up *Node) {
 				continue
 			}
 			a = v.Closure
-			if !v.Byval {
+			if !v.Name.Byval {
 				a = Nod(OADDR, a, nil)
 				a.Lineno = v.Lineno
 				a.Escloopdepth = e.loopdepth
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/internal/gc/gen.go
index cd0e650ca9..d3c6387a4e 100644
--- a/src/cmd/internal/gc/gen.go
+++ b/src/cmd/internal/gc/gen.go
@@ -78,10 +78,10 @@ func addrescapes(n *Node) {
 			oldfn := Curfn
 
 			Curfn = n.Curfn
-			n.Heapaddr = temp(Ptrto(n.Type))
+			n.Name.Heapaddr = temp(Ptrto(n.Type))
 			buf := fmt.Sprintf("&%v", n.Sym)
-			n.Heapaddr.Sym = Lookup(buf)
-			n.Heapaddr.Orig.Sym = n.Heapaddr.Sym
+			n.Name.Heapaddr.Sym = Lookup(buf)
+			n.Name.Heapaddr.Orig.Sym = n.Name.Heapaddr.Sym
 			n.Esc = EscHeap
 			if Debug['m'] != 0 {
 				fmt.Printf("%v: moved to heap: %v\n", n.Line(), n)
@@ -262,7 +262,7 @@ func cgen_dcl(n *Node) {
 	if n.Alloc == nil {
 		n.Alloc = callnew(n.Type)
 	}
-	Cgen_as(n.Heapaddr, n.Alloc)
+	Cgen_as(n.Name.Heapaddr, n.Alloc)
 }
 
 /*
diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/internal/gc/gsubr.go
index 98d6346e2a..5ec4587e74 100644
--- a/src/cmd/internal/gc/gsubr.go
+++ b/src/cmd/internal/gc/gsubr.go
@@ -214,7 +214,7 @@ func ggloblnod(nam *Node) {
 	p.To.Sym = nil
 	p.To.Type = obj.TYPE_CONST
 	p.To.Offset = nam.Type.Width
-	if nam.Readonly {
+	if nam.Name.Readonly {
 		p.From3.Offset = obj.RODATA
 	}
 	if nam.Type != nil && !haspointers(nam.Type) {
@@ -369,7 +369,7 @@ func Naddr(a *obj.Addr, n *Node) {
 		if s == nil {
 			s = Lookup(".noname")
 		}
-		if n.Method {
+		if n.Name.Method {
 			if n.Type != nil {
 				if n.Type.Sym != nil {
 					if n.Type.Sym.Pkg != nil {
diff --git a/src/cmd/internal/gc/inl.go b/src/cmd/internal/gc/inl.go
index dd2087dec3..22a5d3d9fe 100644
--- a/src/cmd/internal/gc/inl.go
+++ b/src/cmd/internal/gc/inl.go
@@ -511,10 +511,10 @@ func mkinlcall(np **Node, fn *Node, isddd bool) {
 
 func tinlvar(t *Type) *Node {
 	if t.Nname != nil && !isblank(t.Nname) {
-		if t.Nname.Inlvar == nil {
+		if t.Nname.Name.Inlvar == nil {
 			Fatal("missing inlvar for %v\n", t.Nname)
 		}
-		return t.Nname.Inlvar
+		return t.Nname.Name.Inlvar
 	}
 
 	typecheck(&nblank, Erv|Easgn)
@@ -577,13 +577,13 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
 			continue
 		}
 		if ll.N.Op == ONAME {
-			ll.N.Inlvar = inlvar(ll.N)
+			ll.N.Name.Inlvar = inlvar(ll.N)
 
 			// Typecheck because inlvar is not necessarily a function parameter.
-			typecheck(&ll.N.Inlvar, Erv)
+			typecheck(&ll.N.Name.Inlvar, Erv)
 
 			if ll.N.Class&^PHEAP != PAUTO {
-				ninit = list(ninit, Nod(ODCL, ll.N.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
+				ninit = list(ninit, Nod(ODCL, ll.N.Name.Inlvar, nil)) // otherwise gen won't emit the allocations for heapallocs
 			}
 		}
 	}
@@ -594,7 +594,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
 		if t != nil && t.Nname != nil && !isblank(t.Nname) {
 			m = inlvar(t.Nname)
 			typecheck(&m, Erv)
-			t.Nname.Inlvar = m
+			t.Nname.Name.Inlvar = m
 		} else {
 			// anonymous return values, synthesize names for use in assignment that replaces return
 			m = retvar(t, i)
@@ -611,7 +611,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
 		// method call with a receiver.
 		t := getthisx(fn.Type).Type
 
-		if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Inlvar == nil {
+		if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
 			Fatal("missing inlvar for %v\n", t.Nname)
 		}
 		if n.Left.Left == nil {
@@ -680,7 +680,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
 		// append receiver inlvar to LHS.
 		t := getthisx(fn.Type).Type
 
-		if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Inlvar == nil {
+		if t != nil && t.Nname != nil && !isblank(t.Nname) && t.Nname.Name.Inlvar == nil {
 			Fatal("missing inlvar for %v\n", t.Nname)
 		}
 		if t == nil {
@@ -907,11 +907,11 @@ func inlsubst(n *Node) *Node {
 
 	switch n.Op {
 	case ONAME:
-		if n.Inlvar != nil { // These will be set during inlnode
+		if n.Name.Inlvar != nil { // These will be set during inlnode
 			if Debug['m'] > 2 {
-				fmt.Printf("substituting name %v  ->  %v\n", Nconv(n, obj.FmtSign), Nconv(n.Inlvar, obj.FmtSign))
+				fmt.Printf("substituting name %v  ->  %v\n", Nconv(n, obj.FmtSign), Nconv(n.Name.Inlvar, obj.FmtSign))
 			}
-			return n.Inlvar
+			return n.Name.Inlvar
 		}
 
 		if Debug['m'] > 2 {
diff --git a/src/cmd/internal/gc/pgen.go b/src/cmd/internal/gc/pgen.go
index f247a685ca..1b67cf2c3e 100644
--- a/src/cmd/internal/gc/pgen.go
+++ b/src/cmd/internal/gc/pgen.go
@@ -200,8 +200,8 @@ func cmpstackvar(a *Node, b *Node) int {
 		return bp - ap
 	}
 
-	ap = obj.Bool2int(a.Needzero)
-	bp = obj.Bool2int(b.Needzero)
+	ap = obj.Bool2int(a.Name.Needzero)
+	bp = obj.Bool2int(b.Name.Needzero)
 	if ap != bp {
 		return bp - ap
 	}
diff --git a/src/cmd/internal/gc/plive.go b/src/cmd/internal/gc/plive.go
index 040a77814e..977789f3e4 100644
--- a/src/cmd/internal/gc/plive.go
+++ b/src/cmd/internal/gc/plive.go
@@ -1281,8 +1281,8 @@ func livenessepilogue(lv *Liveness) {
 						}
 						bvset(all, pos) // silence future warnings in this block
 						n = lv.vars[pos]
-						if !n.Needzero {
-							n.Needzero = true
+						if !n.Name.Needzero {
+							n.Name.Needzero = true
 							if debuglive >= 1 {
 								Warnl(int(p.Lineno), "%v: %v is ambiguously live", Curfn.Nname, Nconv(n, obj.FmtLong))
 							}
diff --git a/src/cmd/internal/gc/sinit.go b/src/cmd/internal/gc/sinit.go
index 4fdb2e9223..dfaec74de2 100644
--- a/src/cmd/internal/gc/sinit.go
+++ b/src/cmd/internal/gc/sinit.go
@@ -510,7 +510,7 @@ func staticname(t *Type, ctxt int) *Node {
 	n := newname(Lookupf("statictmp_%.4d", statuniqgen))
 	statuniqgen++
 	if ctxt == 0 {
-		n.Readonly = true
+		n.Name.Readonly = true
 	}
 	addvar(n, t, PEXTERN)
 	return n
diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go
index 9ef00a09cb..818d546970 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/internal/gc/syntax.go
@@ -64,7 +64,7 @@ type Node struct {
 	Reg int16
 
 	// ONAME
-	*Name
+	Name     *Name
 	Ntype    *Node
 	Defn     *Node // ONAME: initializing assignment; OLABEL: labeled statement
 	Pack     *Node // real package for import . names
diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go
index 6ad8c82c32..06f8b34305 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/internal/gc/typecheck.go
@@ -311,8 +311,8 @@ OpSwitch:
 		break OpSwitch
 
 	case ONAME:
-		if n.Decldepth == 0 {
-			n.Decldepth = decldepth
+		if n.Name.Decldepth == 0 {
+			n.Name.Decldepth = decldepth
 		}
 		if n.Etype != 0 {
 			ok |= Ecall
@@ -3521,7 +3521,7 @@ func typecheckfunc(n *Node) {
 
 	for l := n.Func.Dcl; l != nil; l = l.Next {
 		if l.N.Op == ONAME && (l.N.Class == PPARAM || l.N.Class == PPARAMOUT) {
-			l.N.Decldepth = 1
+			l.N.Name.Decldepth = 1
 		}
 	}
 }
diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go
index 81bb8524b3..a7f5256b19 100644
--- a/src/cmd/internal/gc/walk.go
+++ b/src/cmd/internal/gc/walk.go
@@ -2719,7 +2719,7 @@ func paramstoheap(argin **Type, out int) *NodeList {
 		if v.Alloc == nil {
 			v.Alloc = callnew(v.Type)
 		}
-		nn = list(nn, Nod(OAS, v.Heapaddr, v.Alloc))
+		nn = list(nn, Nod(OAS, v.Name.Heapaddr, v.Alloc))
 		if v.Class&^PHEAP != PPARAMOUT {
 			as = Nod(OAS, v, v.Stackparam)
 			v.Stackparam.Typecheck = 1

From c3c047a6a32ab1f5344e7c0b074fb9d1ce365bbc Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Fri, 15 May 2015 14:23:23 -0400
Subject: [PATCH 117/232] runtime: test and fix heap bitmap for 1-pointer
 allocation on 32-bit system

Change-Id: Ic064fe7c6bd3304dcc8c3f7b3b5393870b5387c2
Reviewed-on: https://go-review.googlesource.com/10119
Run-TryBot: Austin Clements 
Reviewed-by: Austin Clements 
---
 src/runtime/export_test.go |  2 ++
 src/runtime/gcinfo_test.go | 30 ++++++++++++++++++++++++------
 src/runtime/mbitmap.go     | 27 ++++++++++++++++++++++-----
 3 files changed, 48 insertions(+), 11 deletions(-)

diff --git a/src/runtime/export_test.go b/src/runtime/export_test.go
index 2f8df78e13..3fddcc868f 100644
--- a/src/runtime/export_test.go
+++ b/src/runtime/export_test.go
@@ -150,3 +150,5 @@ func BenchSetType(n int, x interface{}) {
 		}
 	})
 }
+
+const PtrSize = ptrSize
diff --git a/src/runtime/gcinfo_test.go b/src/runtime/gcinfo_test.go
index 7618d86a45..f330bf2430 100644
--- a/src/runtime/gcinfo_test.go
+++ b/src/runtime/gcinfo_test.go
@@ -13,11 +13,11 @@ import (
 const (
 	typeScalar  = 0
 	typePointer = 1
-	typeDead    = 255
 )
 
 // TestGCInfo tests that various objects in heap, data and bss receive correct GC pointer type info.
 func TestGCInfo(t *testing.T) {
+	verifyGCInfo(t, "bss Ptr", &bssPtr, infoPtr)
 	verifyGCInfo(t, "bss ScalarPtr", &bssScalarPtr, infoScalarPtr)
 	verifyGCInfo(t, "bss PtrScalar", &bssPtrScalar, infoPtrScalar)
 	verifyGCInfo(t, "bss BigStruct", &bssBigStruct, infoBigStruct())
@@ -26,6 +26,7 @@ func TestGCInfo(t *testing.T) {
 	verifyGCInfo(t, "bss eface", &bssEface, infoEface)
 	verifyGCInfo(t, "bss iface", &bssIface, infoIface)
 
+	verifyGCInfo(t, "data Ptr", &dataPtr, infoPtr)
 	verifyGCInfo(t, "data ScalarPtr", &dataScalarPtr, infoScalarPtr)
 	verifyGCInfo(t, "data PtrScalar", &dataPtrScalar, infoPtrScalar)
 	verifyGCInfo(t, "data BigStruct", &dataBigStruct, infoBigStruct())
@@ -34,6 +35,7 @@ func TestGCInfo(t *testing.T) {
 	verifyGCInfo(t, "data eface", &dataEface, infoEface)
 	verifyGCInfo(t, "data iface", &dataIface, infoIface)
 
+	verifyGCInfo(t, "stack Ptr", new(Ptr), infoPtr)
 	verifyGCInfo(t, "stack ScalarPtr", new(ScalarPtr), infoScalarPtr)
 	verifyGCInfo(t, "stack PtrScalar", new(PtrScalar), infoPtrScalar)
 	verifyGCInfo(t, "stack BigStruct", new(BigStruct), infoBigStruct())
@@ -43,6 +45,7 @@ func TestGCInfo(t *testing.T) {
 	verifyGCInfo(t, "stack iface", new(Iface), infoIface)
 
 	for i := 0; i < 10; i++ {
+		verifyGCInfo(t, "heap Ptr", escape(new(Ptr)), trimDead(padDead(infoPtr)))
 		verifyGCInfo(t, "heap PtrSlice", escape(&make([]*byte, 10)[0]), trimDead(infoPtr10))
 		verifyGCInfo(t, "heap ScalarPtr", escape(new(ScalarPtr)), trimDead(infoScalarPtr))
 		verifyGCInfo(t, "heap ScalarPtrSlice", escape(&make([]ScalarPtr, 4)[0]), trimDead(infoScalarPtr4))
@@ -52,21 +55,28 @@ func TestGCInfo(t *testing.T) {
 		verifyGCInfo(t, "heap eface", escape(new(interface{})), trimDead(infoEface))
 		verifyGCInfo(t, "heap iface", escape(new(Iface)), trimDead(infoIface))
 	}
-
 }
 
 func verifyGCInfo(t *testing.T, name string, p interface{}, mask0 []byte) {
 	mask := runtime.GCMask(p)
-	if len(mask) > len(mask0) {
-		mask0 = append(mask0, typeDead)
-		mask = mask[:len(mask0)]
-	}
 	if bytes.Compare(mask, mask0) != 0 {
 		t.Errorf("bad GC program for %v:\nwant %+v\ngot  %+v", name, mask0, mask)
 		return
 	}
 }
 
+func padDead(mask []byte) []byte {
+	// Because the dead bit isn't encoded until the third word,
+	// and because on 32-bit systems a one-word allocation
+	// uses a two-word block, the pointer info for a one-word
+	// object needs to be expanded to include an extra scalar
+	// on 32-bit systems to match the heap bitmap.
+	if runtime.PtrSize == 4 && len(mask) == 1 {
+		return []byte{mask[0], 0}
+	}
+	return mask
+}
+
 func trimDead(mask []byte) []byte {
 	for len(mask) > 2 && mask[len(mask)-1] == typeScalar {
 		mask = mask[:len(mask)-1]
@@ -81,6 +91,12 @@ func escape(p interface{}) interface{} {
 	return p
 }
 
+var infoPtr = []byte{typePointer}
+
+type Ptr struct {
+	*byte
+}
+
 var infoPtr10 = []byte{typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer, typePointer}
 
 type ScalarPtr struct {
@@ -160,6 +176,7 @@ func (IfaceImpl) f() {
 
 var (
 	// BSS
+	bssPtr       Ptr
 	bssScalarPtr ScalarPtr
 	bssPtrScalar PtrScalar
 	bssBigStruct BigStruct
@@ -169,6 +186,7 @@ var (
 	bssIface     Iface
 
 	// DATA
+	dataPtr                   = Ptr{new(byte)}
 	dataScalarPtr             = ScalarPtr{q: 1}
 	dataPtrScalar             = PtrScalar{w: 1}
 	dataBigStruct             = BigStruct{w: 1}
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index fcfcc7261c..546c331614 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -583,7 +583,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 	// The checks for size == ptrSize and size == 2*ptrSize can therefore
 	// assume that dataSize == size without checking it explicitly.
 
-	if size == ptrSize {
+	if ptrSize == 8 && size == ptrSize {
 		// It's one word and it has pointers, it must be a pointer.
 		// In general we'd need an atomic update here if the
 		// concurrent GC were marking objects in this span,
@@ -635,11 +635,28 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 	// are 4-word aligned (because they're all 16-byte aligned).
 	if size == 2*ptrSize {
 		if typ.size == ptrSize {
-			// 2-element slice of pointer.
-			if gcphase == _GCoff {
-				*h.bitp |= (bitPointer | bitPointer<
Date: Tue, 12 May 2015 15:40:13 +1200
Subject: [PATCH 118/232] cmd/5l, etc, cmd/internal/ld: consolidate
 implementations of adddynlib

They were all essentially the same.

Change-Id: I6e0b548cda6e4bbe2ec3b3025b746d1f6d332d48
Reviewed-on: https://go-review.googlesource.com/10000
Run-TryBot: Michael Hudson-Doyle 
TryBot-Result: Gobot Gobot 
Reviewed-by: Russ Cox 
---
 src/cmd/5l/asm.go          | 36 ------------------------------------
 src/cmd/5l/obj.go          |  1 -
 src/cmd/6l/asm.go          | 38 +-------------------------------------
 src/cmd/6l/obj.go          |  1 -
 src/cmd/7l/asm.go          | 36 ------------------------------------
 src/cmd/7l/obj.go          |  1 -
 src/cmd/8l/asm.go          | 36 ------------------------------------
 src/cmd/8l/obj.go          |  1 -
 src/cmd/9l/asm.go          | 34 ----------------------------------
 src/cmd/9l/obj.go          |  1 -
 src/cmd/internal/ld/go.go  | 23 ++++++++++++++++++++++-
 src/cmd/internal/ld/lib.go |  1 -
 12 files changed, 23 insertions(+), 186 deletions(-)

diff --git a/src/cmd/5l/asm.go b/src/cmd/5l/asm.go
index 85ea684fc7..14302a5a38 100644
--- a/src/cmd/5l/asm.go
+++ b/src/cmd/5l/asm.go
@@ -37,24 +37,6 @@ import (
 	"log"
 )
 
-func needlib(name string) int {
-	if name[0] == '\x00' {
-		return 0
-	}
-
-	/* reuse hash code in symbol table */
-	p := fmt.Sprintf(".dynlib.%s", name)
-
-	s := ld.Linklookup(ld.Ctxt, p, 0)
-
-	if s.Type == 0 {
-		s.Type = 100 // avoid SDATA, etc.
-		return 1
-	}
-
-	return 0
-}
-
 func gentext() {
 }
 
@@ -557,24 +539,6 @@ func adddynsym(ctxt *ld.Link, s *ld.LSym) {
 	}
 }
 
-func adddynlib(lib string) {
-	if needlib(lib) == 0 {
-		return
-	}
-
-	if ld.Iself {
-		s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
-		if s.Size == 0 {
-			ld.Addstring(s, "")
-		}
-		ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Machoadddynlib(lib)
-	} else {
-		ld.Diag("adddynlib: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/5l/obj.go b/src/cmd/5l/obj.go
index e4fffdec6a..d9485521ad 100644
--- a/src/cmd/5l/obj.go
+++ b/src/cmd/5l/obj.go
@@ -58,7 +58,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfregsp = DWARFREGSP
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
-	ld.Thearch.Adddynlib = adddynlib
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
diff --git a/src/cmd/6l/asm.go b/src/cmd/6l/asm.go
index a025ce6ea6..9b471a04ac 100644
--- a/src/cmd/6l/asm.go
+++ b/src/cmd/6l/asm.go
@@ -44,24 +44,6 @@ func PADDR(x uint32) uint32 {
 
 var zeroes string
 
-func needlib(name string) int {
-	if name[0] == '\x00' {
-		return 0
-	}
-
-	/* reuse hash code in symbol table */
-	p := fmt.Sprintf(".elfload.%s", name)
-
-	s := ld.Linklookup(ld.Ctxt, p, 0)
-
-	if s.Type == 0 {
-		s.Type = 100 // avoid SDATA, etc.
-		return 1
-	}
-
-	return 0
-}
-
 func Addcall(ctxt *ld.Link, s *ld.LSym, t *ld.LSym) int64 {
 	s.Reachable = true
 	i := s.Size
@@ -673,7 +655,7 @@ func adddynsym(ctxt *ld.Link, s *ld.LSym) {
 		/* size of object */
 		ld.Adduint64(ctxt, d, uint64(s.Size))
 
-		if s.Cgoexport&ld.CgoExportDynamic == 0 && s.Dynimplib != "" && needlib(s.Dynimplib) != 0 {
+		if s.Cgoexport&ld.CgoExportDynamic == 0 && s.Dynimplib != "" && !ld.Seenlib[s.Dynimplib] {
 			ld.Elfwritedynent(ld.Linklookup(ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), s.Dynimplib)))
 		}
 	} else if ld.HEADTYPE == obj.Hdarwin {
@@ -685,24 +667,6 @@ func adddynsym(ctxt *ld.Link, s *ld.LSym) {
 	}
 }
 
-func adddynlib(lib string) {
-	if needlib(lib) == 0 {
-		return
-	}
-
-	if ld.Iself {
-		s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
-		if s.Size == 0 {
-			ld.Addstring(s, "")
-		}
-		ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Machoadddynlib(lib)
-	} else {
-		ld.Diag("adddynlib: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/6l/obj.go b/src/cmd/6l/obj.go
index 8ee7bb28db..38ac0783b6 100644
--- a/src/cmd/6l/obj.go
+++ b/src/cmd/6l/obj.go
@@ -61,7 +61,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfregsp = DWARFREGSP
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
-	ld.Thearch.Adddynlib = adddynlib
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
diff --git a/src/cmd/7l/asm.go b/src/cmd/7l/asm.go
index a17899dcf0..a0e813cfa5 100644
--- a/src/cmd/7l/asm.go
+++ b/src/cmd/7l/asm.go
@@ -40,24 +40,6 @@ import (
 
 func gentext() {}
 
-func needlib(name string) int {
-	if name[0] == '\x00' {
-		return 0
-	}
-
-	/* reuse hash code in symbol table */
-	p := fmt.Sprintf(".dynlib.%s", name)
-
-	s := ld.Linklookup(ld.Ctxt, p, 0)
-
-	if s.Type == 0 {
-		s.Type = 100 // avoid SDATA, etc.
-		return 1
-	}
-
-	return 0
-}
-
 func adddynrela(rel *ld.LSym, s *ld.LSym, r *ld.Reloc) {
 	log.Fatalf("adddynrela not implemented")
 }
@@ -297,24 +279,6 @@ func adddynsym(ctxt *ld.Link, s *ld.LSym) {
 	// TODO(minux): implement when needed.
 }
 
-func adddynlib(lib string) {
-	if needlib(lib) == 0 {
-		return
-	}
-
-	if ld.Iself {
-		s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
-		if s.Size == 0 {
-			ld.Addstring(s, "")
-		}
-		ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Machoadddynlib(lib)
-	} else {
-		ld.Diag("adddynlib: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/7l/obj.go b/src/cmd/7l/obj.go
index aeea421bc2..7d0500387b 100644
--- a/src/cmd/7l/obj.go
+++ b/src/cmd/7l/obj.go
@@ -58,7 +58,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfregsp = DWARFREGSP
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
-	ld.Thearch.Adddynlib = adddynlib
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
diff --git a/src/cmd/8l/asm.go b/src/cmd/8l/asm.go
index 7231379108..873fd16470 100644
--- a/src/cmd/8l/asm.go
+++ b/src/cmd/8l/asm.go
@@ -37,24 +37,6 @@ import (
 	"log"
 )
 
-func needlib(name string) int {
-	if name[0] == '\x00' {
-		return 0
-	}
-
-	/* reuse hash code in symbol table */
-	p := fmt.Sprintf(".dynlib.%s", name)
-
-	s := ld.Linklookup(ld.Ctxt, p, 0)
-
-	if s.Type == 0 {
-		s.Type = 100 // avoid SDATA, etc.
-		return 1
-	}
-
-	return 0
-}
-
 func gentext() {
 }
 
@@ -548,24 +530,6 @@ func adddynsym(ctxt *ld.Link, s *ld.LSym) {
 	}
 }
 
-func adddynlib(lib string) {
-	if needlib(lib) == 0 {
-		return
-	}
-
-	if ld.Iself {
-		s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
-		if s.Size == 0 {
-			ld.Addstring(s, "")
-		}
-		ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Machoadddynlib(lib)
-	} else if ld.HEADTYPE != obj.Hwindows {
-		ld.Diag("adddynlib: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/8l/obj.go b/src/cmd/8l/obj.go
index 5af3f9249b..9bbaa7ee1b 100644
--- a/src/cmd/8l/obj.go
+++ b/src/cmd/8l/obj.go
@@ -58,7 +58,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfregsp = DWARFREGSP
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
-	ld.Thearch.Adddynlib = adddynlib
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
diff --git a/src/cmd/9l/asm.go b/src/cmd/9l/asm.go
index 257f23e2ab..702ba2bb7c 100644
--- a/src/cmd/9l/asm.go
+++ b/src/cmd/9l/asm.go
@@ -38,24 +38,6 @@ import (
 	"log"
 )
 
-func needlib(name string) int {
-	if name[0] == '\x00' {
-		return 0
-	}
-
-	/* reuse hash code in symbol table */
-	p := fmt.Sprintf(".dynlib.%s", name)
-
-	s := ld.Linklookup(ld.Ctxt, p, 0)
-
-	if s.Type == 0 {
-		s.Type = 100 // avoid SDATA, etc.
-		return 1
-	}
-
-	return 0
-}
-
 func gentext() {
 	var s *ld.LSym
 	var stub *ld.LSym
@@ -670,22 +652,6 @@ func adddynsym(ctxt *ld.Link, s *ld.LSym) {
 	}
 }
 
-func adddynlib(lib string) {
-	if needlib(lib) == 0 {
-		return
-	}
-
-	if ld.Iself {
-		s := ld.Linklookup(ld.Ctxt, ".dynstr", 0)
-		if s.Size == 0 {
-			ld.Addstring(s, "")
-		}
-		ld.Elfwritedynent(ld.Linklookup(ld.Ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(s, lib)))
-	} else {
-		ld.Diag("adddynlib: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/9l/obj.go b/src/cmd/9l/obj.go
index 2da37561e9..f584ca43cd 100644
--- a/src/cmd/9l/obj.go
+++ b/src/cmd/9l/obj.go
@@ -62,7 +62,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfregsp = DWARFREGSP
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
-	ld.Thearch.Adddynlib = adddynlib
 	ld.Thearch.Adddynrel = adddynrel
 	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go
index 0223bfae9d..c1defeb8a2 100644
--- a/src/cmd/internal/ld/go.go
+++ b/src/cmd/internal/ld/go.go
@@ -416,7 +416,7 @@ func loadcgo(file string, pkg string, p string) {
 				// to force a link of foo.so.
 				havedynamic = 1
 
-				Thearch.Adddynlib(lib)
+				adddynlib(lib)
 				continue
 			}
 
@@ -534,6 +534,27 @@ err:
 	nerrors++
 }
 
+var Seenlib = make(map[string]bool)
+
+func adddynlib(lib string) {
+	if Seenlib[lib] {
+		return
+	}
+	Seenlib[lib] = true
+
+	if Iself {
+		s := Linklookup(Ctxt, ".dynstr", 0)
+		if s.Size == 0 {
+			Addstring(s, "")
+		}
+		Elfwritedynent(Linklookup(Ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
+	} else if HEADTYPE == obj.Hdarwin {
+		Machoadddynlib(lib)
+	} else {
+		Diag("adddynlib: unsupported binary format")
+	}
+}
+
 var markq *LSym
 
 var emarkq *LSym
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index d4e67800d2..5ab5f653f3 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -94,7 +94,6 @@ type Arch struct {
 	Openbsddynld     string
 	Dragonflydynld   string
 	Solarisdynld     string
-	Adddynlib        func(string)
 	Adddynrel        func(*LSym, *Reloc)
 	Adddynsym        func(*Link, *LSym)
 	Archinit         func()

From 4cfff271c2aacd4ef23f7eacd9adf61605c45e74 Mon Sep 17 00:00:00 2001
From: Michael Hudson-Doyle 
Date: Tue, 12 May 2015 15:59:15 +1200
Subject: [PATCH 119/232] cmd/5l, etc, cmd/internal/ld: consolidate
 implementations of adddynsym

The only essential difference is elf32 vs elf64, I assume the other differences
are bugs in one version or another...

Change-Id: Ie6ff33d5574a6592b543df9983eff8fdf88c97a1
Reviewed-on: https://go-review.googlesource.com/10001
Run-TryBot: Michael Hudson-Doyle 
Reviewed-by: Russ Cox 
---
 src/cmd/5l/asm.go          | 54 ++---------------------
 src/cmd/5l/obj.go          |  1 -
 src/cmd/6l/asm.go          | 64 ++--------------------------
 src/cmd/6l/obj.go          |  1 -
 src/cmd/7l/asm.go          |  4 --
 src/cmd/7l/obj.go          |  1 -
 src/cmd/8l/asm.go          | 60 ++------------------------
 src/cmd/8l/obj.go          |  1 -
 src/cmd/9l/asm.go          | 52 +----------------------
 src/cmd/9l/obj.go          |  1 -
 src/cmd/internal/ld/elf.go | 87 ++++++++++++++++++++++++++++++++++++++
 src/cmd/internal/ld/go.go  | 24 +++++++++--
 src/cmd/internal/ld/lib.go |  1 -
 13 files changed, 120 insertions(+), 231 deletions(-)

diff --git a/src/cmd/5l/asm.go b/src/cmd/5l/asm.go
index 14302a5a38..1b69671b9f 100644
--- a/src/cmd/5l/asm.go
+++ b/src/cmd/5l/asm.go
@@ -176,7 +176,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 			break
 		}
 		if ld.Iself {
-			adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ld.Ctxt, targ)
 			rel := ld.Linklookup(ld.Ctxt, ".rel", 0)
 			ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off))
 			ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_ARM_GLOB_DAT)) // we need a nil + A dynmic reloc
@@ -422,7 +422,7 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 		return
 	}
 
-	adddynsym(ctxt, s)
+	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
 		plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -477,7 +477,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
 		return
 	}
 
-	adddynsym(ctxt, s)
+	ld.Adddynsym(ctxt, s)
 	got := ld.Linklookup(ctxt, ".got", 0)
 	s.Got = int32(got.Size)
 	ld.Adduint32(ctxt, got, 0)
@@ -491,54 +491,6 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
 	}
 }
 
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
-	if s.Dynid >= 0 {
-		return
-	}
-
-	if ld.Iself {
-		s.Dynid = int32(ld.Nelfsym)
-		ld.Nelfsym++
-
-		d := ld.Linklookup(ctxt, ".dynsym", 0)
-
-		/* name */
-		name := s.Extname
-
-		ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
-		/* value */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint32(ctxt, d, 0)
-		} else {
-			ld.Addaddr(ctxt, d, s)
-		}
-
-		/* size */
-		ld.Adduint32(ctxt, d, 0)
-
-		/* type */
-		t := ld.STB_GLOBAL << 4
-
-		if (s.Cgoexport&ld.CgoExportDynamic != 0) && s.Type&obj.SMASK == obj.STEXT {
-			t |= ld.STT_FUNC
-		} else {
-			t |= ld.STT_OBJECT
-		}
-		ld.Adduint8(ctxt, d, uint8(t))
-		ld.Adduint8(ctxt, d, 0)
-
-		/* shndx */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
-		} else {
-			ld.Adduint16(ctxt, d, 1)
-		}
-	} else {
-		ld.Diag("adddynsym: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/5l/obj.go b/src/cmd/5l/obj.go
index d9485521ad..9c9578343e 100644
--- a/src/cmd/5l/obj.go
+++ b/src/cmd/5l/obj.go
@@ -59,7 +59,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
 	ld.Thearch.Adddynrel = adddynrel
-	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
 	ld.Thearch.Archreloc = archreloc
 	ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/6l/asm.go b/src/cmd/6l/asm.go
index 9b471a04ac..5520a5acf1 100644
--- a/src/cmd/6l/asm.go
+++ b/src/cmd/6l/asm.go
@@ -274,7 +274,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 			break
 		}
 		if ld.Iself {
-			adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ld.Ctxt, targ)
 			rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
 			ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off))
 			if r.Siz == 8 {
@@ -298,7 +298,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 			// just in case the C code assigns to the variable,
 			// and of course it only works for single pointers,
 			// but we only need to support cgo and that's all it needs.
-			adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ld.Ctxt, targ)
 
 			got := ld.Linklookup(ld.Ctxt, ".got", 0)
 			s.Type = got.Type | obj.SSUB
@@ -526,7 +526,7 @@ func addpltsym(s *ld.LSym) {
 		return
 	}
 
-	adddynsym(ld.Ctxt, s)
+	ld.Adddynsym(ld.Ctxt, s)
 
 	if ld.Iself {
 		plt := ld.Linklookup(ld.Ctxt, ".plt", 0)
@@ -594,7 +594,7 @@ func addgotsym(s *ld.LSym) {
 		return
 	}
 
-	adddynsym(ld.Ctxt, s)
+	ld.Adddynsym(ld.Ctxt, s)
 	got := ld.Linklookup(ld.Ctxt, ".got", 0)
 	s.Got = int32(got.Size)
 	ld.Adduint64(ld.Ctxt, got, 0)
@@ -611,62 +611,6 @@ func addgotsym(s *ld.LSym) {
 	}
 }
 
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
-	if s.Dynid >= 0 {
-		return
-	}
-
-	if ld.Iself {
-		s.Dynid = int32(ld.Nelfsym)
-		ld.Nelfsym++
-
-		d := ld.Linklookup(ctxt, ".dynsym", 0)
-
-		name := s.Extname
-		ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
-		/* type */
-		t := ld.STB_GLOBAL << 4
-
-		if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
-			t |= ld.STT_FUNC
-		} else {
-			t |= ld.STT_OBJECT
-		}
-		ld.Adduint8(ctxt, d, uint8(t))
-
-		/* reserved */
-		ld.Adduint8(ctxt, d, 0)
-
-		/* section where symbol is defined */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
-		} else {
-			ld.Adduint16(ctxt, d, 1)
-		}
-
-		/* value */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint64(ctxt, d, 0)
-		} else {
-			ld.Addaddr(ctxt, d, s)
-		}
-
-		/* size of object */
-		ld.Adduint64(ctxt, d, uint64(s.Size))
-
-		if s.Cgoexport&ld.CgoExportDynamic == 0 && s.Dynimplib != "" && !ld.Seenlib[s.Dynimplib] {
-			ld.Elfwritedynent(ld.Linklookup(ctxt, ".dynamic", 0), ld.DT_NEEDED, uint64(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), s.Dynimplib)))
-		}
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
-	} else if ld.HEADTYPE == obj.Hwindows {
-	} else // already taken care of
-	{
-		ld.Diag("adddynsym: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/6l/obj.go b/src/cmd/6l/obj.go
index 38ac0783b6..1dc9e02a8b 100644
--- a/src/cmd/6l/obj.go
+++ b/src/cmd/6l/obj.go
@@ -62,7 +62,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
 	ld.Thearch.Adddynrel = adddynrel
-	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
 	ld.Thearch.Archreloc = archreloc
 	ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/7l/asm.go b/src/cmd/7l/asm.go
index a0e813cfa5..3dfb8c666d 100644
--- a/src/cmd/7l/asm.go
+++ b/src/cmd/7l/asm.go
@@ -275,10 +275,6 @@ func archrelocvariant(r *ld.Reloc, s *ld.LSym, t int64) int64 {
 	return -1
 }
 
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
-	// TODO(minux): implement when needed.
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/7l/obj.go b/src/cmd/7l/obj.go
index 7d0500387b..f88584b938 100644
--- a/src/cmd/7l/obj.go
+++ b/src/cmd/7l/obj.go
@@ -59,7 +59,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
 	ld.Thearch.Adddynrel = adddynrel
-	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
 	ld.Thearch.Archreloc = archreloc
 	ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/8l/asm.go b/src/cmd/8l/asm.go
index 873fd16470..a63c51f58d 100644
--- a/src/cmd/8l/asm.go
+++ b/src/cmd/8l/asm.go
@@ -184,7 +184,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 			break
 		}
 		if ld.Iself {
-			adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ld.Ctxt, targ)
 			rel := ld.Linklookup(ld.Ctxt, ".rel", 0)
 			ld.Addaddrplus(ld.Ctxt, rel, s, int64(r.Off))
 			ld.Adduint32(ld.Ctxt, rel, ld.ELF32_R_INFO(uint32(targ.Dynid), ld.R_386_32))
@@ -204,7 +204,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 			// just in case the C code assigns to the variable,
 			// and of course it only works for single pointers,
 			// but we only need to support cgo and that's all it needs.
-			adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ld.Ctxt, targ)
 
 			got := ld.Linklookup(ld.Ctxt, ".got", 0)
 			s.Type = got.Type | obj.SSUB
@@ -402,7 +402,7 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 		return
 	}
 
-	adddynsym(ctxt, s)
+	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
 		plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -462,7 +462,7 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
 		return
 	}
 
-	adddynsym(ctxt, s)
+	ld.Adddynsym(ctxt, s)
 	got := ld.Linklookup(ctxt, ".got", 0)
 	s.Got = int32(got.Size)
 	ld.Adduint32(ctxt, got, 0)
@@ -478,58 +478,6 @@ func addgotsym(ctxt *ld.Link, s *ld.LSym) {
 	}
 }
 
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
-	if s.Dynid >= 0 {
-		return
-	}
-
-	if ld.Iself {
-		s.Dynid = int32(ld.Nelfsym)
-		ld.Nelfsym++
-
-		d := ld.Linklookup(ctxt, ".dynsym", 0)
-
-		/* name */
-		name := s.Extname
-
-		ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
-		/* value */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint32(ctxt, d, 0)
-		} else {
-			ld.Addaddr(ctxt, d, s)
-		}
-
-		/* size */
-		ld.Adduint32(ctxt, d, 0)
-
-		/* type */
-		t := ld.STB_GLOBAL << 4
-
-		if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
-			t |= ld.STT_FUNC
-		} else {
-			t |= ld.STT_OBJECT
-		}
-		ld.Adduint8(ctxt, d, uint8(t))
-		ld.Adduint8(ctxt, d, 0)
-
-		/* shndx */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
-		} else {
-			ld.Adduint16(ctxt, d, 1)
-		}
-	} else if ld.HEADTYPE == obj.Hdarwin {
-		ld.Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
-	} else if ld.HEADTYPE == obj.Hwindows {
-	} else // already taken care of
-	{
-		ld.Diag("adddynsym: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/8l/obj.go b/src/cmd/8l/obj.go
index 9bbaa7ee1b..bea0d03cfe 100644
--- a/src/cmd/8l/obj.go
+++ b/src/cmd/8l/obj.go
@@ -59,7 +59,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
 	ld.Thearch.Adddynrel = adddynrel
-	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
 	ld.Thearch.Archreloc = archreloc
 	ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/9l/asm.go b/src/cmd/9l/asm.go
index 702ba2bb7c..45aa3f84c2 100644
--- a/src/cmd/9l/asm.go
+++ b/src/cmd/9l/asm.go
@@ -222,7 +222,7 @@ func adddynrel(s *ld.LSym, r *ld.Reloc) {
 		r.Type = obj.R_ADDR
 		if targ.Type == obj.SDYNIMPORT {
 			// These happen in .toc sections
-			adddynsym(ld.Ctxt, targ)
+			ld.Adddynsym(ld.Ctxt, targ)
 
 			rela := ld.Linklookup(ld.Ctxt, ".rela", 0)
 			ld.Addaddrplus(ld.Ctxt, rela, s, int64(r.Off))
@@ -502,7 +502,7 @@ func addpltsym(ctxt *ld.Link, s *ld.LSym) {
 		return
 	}
 
-	adddynsym(ctxt, s)
+	ld.Adddynsym(ctxt, s)
 
 	if ld.Iself {
 		plt := ld.Linklookup(ctxt, ".plt", 0)
@@ -604,54 +604,6 @@ func ensureglinkresolver() *ld.LSym {
 	return glink
 }
 
-func adddynsym(ctxt *ld.Link, s *ld.LSym) {
-	if s.Dynid >= 0 {
-		return
-	}
-
-	if ld.Iself {
-		s.Dynid = int32(ld.Nelfsym)
-		ld.Nelfsym++
-
-		d := ld.Linklookup(ctxt, ".dynsym", 0)
-
-		name := s.Extname
-		ld.Adduint32(ctxt, d, uint32(ld.Addstring(ld.Linklookup(ctxt, ".dynstr", 0), name)))
-
-		/* type */
-		t := ld.STB_GLOBAL << 4
-
-		if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
-			t |= ld.STT_FUNC
-		} else {
-			t |= ld.STT_OBJECT
-		}
-		ld.Adduint8(ctxt, d, uint8(t))
-
-		/* reserved */
-		ld.Adduint8(ctxt, d, 0)
-
-		/* section where symbol is defined */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint16(ctxt, d, ld.SHN_UNDEF)
-		} else {
-			ld.Adduint16(ctxt, d, 1)
-		}
-
-		/* value */
-		if s.Type == obj.SDYNIMPORT {
-			ld.Adduint64(ctxt, d, 0)
-		} else {
-			ld.Addaddr(ctxt, d, s)
-		}
-
-		/* size of object */
-		ld.Adduint64(ctxt, d, uint64(s.Size))
-	} else {
-		ld.Diag("adddynsym: unsupported binary format")
-	}
-}
-
 func asmb() {
 	if ld.Debug['v'] != 0 {
 		fmt.Fprintf(&ld.Bso, "%5.2f asmb\n", obj.Cputime())
diff --git a/src/cmd/9l/obj.go b/src/cmd/9l/obj.go
index f584ca43cd..011f290298 100644
--- a/src/cmd/9l/obj.go
+++ b/src/cmd/9l/obj.go
@@ -63,7 +63,6 @@ func linkarchinit() {
 	ld.Thearch.Dwarfreglr = DWARFREGLR
 
 	ld.Thearch.Adddynrel = adddynrel
-	ld.Thearch.Adddynsym = adddynsym
 	ld.Thearch.Archinit = archinit
 	ld.Thearch.Archreloc = archreloc
 	ld.Thearch.Archrelocvariant = archrelocvariant
diff --git a/src/cmd/internal/ld/elf.go b/src/cmd/internal/ld/elf.go
index 5c17b2da6f..b73a75b59b 100644
--- a/src/cmd/internal/ld/elf.go
+++ b/src/cmd/internal/ld/elf.go
@@ -2349,6 +2349,93 @@ elfobj:
 	}
 }
 
+func Elfadddynsym(ctxt *Link, s *LSym) {
+	if elf64 {
+		s.Dynid = int32(Nelfsym)
+		Nelfsym++
+
+		d := Linklookup(ctxt, ".dynsym", 0)
+
+		name := s.Extname
+		Adduint32(ctxt, d, uint32(Addstring(Linklookup(ctxt, ".dynstr", 0), name)))
+
+		/* type */
+		t := STB_GLOBAL << 4
+
+		if s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
+			t |= STT_FUNC
+		} else {
+			t |= STT_OBJECT
+		}
+		Adduint8(ctxt, d, uint8(t))
+
+		/* reserved */
+		Adduint8(ctxt, d, 0)
+
+		/* section where symbol is defined */
+		if s.Type == obj.SDYNIMPORT {
+			Adduint16(ctxt, d, SHN_UNDEF)
+		} else {
+			Adduint16(ctxt, d, 1)
+		}
+
+		/* value */
+		if s.Type == obj.SDYNIMPORT {
+			Adduint64(ctxt, d, 0)
+		} else {
+			Addaddr(ctxt, d, s)
+		}
+
+		/* size of object */
+		Adduint64(ctxt, d, uint64(s.Size))
+
+		if Thearch.Thechar == '6' && s.Cgoexport&CgoExportDynamic == 0 && s.Dynimplib != "" && !seenlib[s.Dynimplib] {
+			Elfwritedynent(Linklookup(ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(Linklookup(ctxt, ".dynstr", 0), s.Dynimplib)))
+		}
+	} else {
+		s.Dynid = int32(Nelfsym)
+		Nelfsym++
+
+		d := Linklookup(ctxt, ".dynsym", 0)
+
+		/* name */
+		name := s.Extname
+
+		Adduint32(ctxt, d, uint32(Addstring(Linklookup(ctxt, ".dynstr", 0), name)))
+
+		/* value */
+		if s.Type == obj.SDYNIMPORT {
+			Adduint32(ctxt, d, 0)
+		} else {
+			Addaddr(ctxt, d, s)
+		}
+
+		/* size */
+		Adduint32(ctxt, d, 0)
+
+		/* type */
+		t := STB_GLOBAL << 4
+
+		// TODO(mwhudson): presumably the behaviour should actually be the same on both arm and 386.
+		if Thearch.Thechar == '8' && s.Cgoexport != 0 && s.Type&obj.SMASK == obj.STEXT {
+			t |= STT_FUNC
+		} else if Thearch.Thechar == '5' && s.Cgoexport&CgoExportDynamic != 0 && s.Type&obj.SMASK == obj.STEXT {
+			t |= STT_FUNC
+		} else {
+			t |= STT_OBJECT
+		}
+		Adduint8(ctxt, d, uint8(t))
+		Adduint8(ctxt, d, 0)
+
+		/* shndx */
+		if s.Type == obj.SDYNIMPORT {
+			Adduint16(ctxt, d, SHN_UNDEF)
+		} else {
+			Adduint16(ctxt, d, 1)
+		}
+	}
+}
+
 func ELF32_R_SYM(info uint32) uint32 {
 	return info >> 8
 }
diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go
index c1defeb8a2..a5b09202e8 100644
--- a/src/cmd/internal/ld/go.go
+++ b/src/cmd/internal/ld/go.go
@@ -534,13 +534,13 @@ err:
 	nerrors++
 }
 
-var Seenlib = make(map[string]bool)
+var seenlib = make(map[string]bool)
 
 func adddynlib(lib string) {
-	if Seenlib[lib] {
+	if seenlib[lib] {
 		return
 	}
-	Seenlib[lib] = true
+	seenlib[lib] = true
 
 	if Iself {
 		s := Linklookup(Ctxt, ".dynstr", 0)
@@ -555,6 +555,22 @@ func adddynlib(lib string) {
 	}
 }
 
+func Adddynsym(ctxt *Link, s *LSym) {
+	if s.Dynid >= 0 {
+		return
+	}
+
+	if Iself {
+		Elfadddynsym(ctxt, s)
+	} else if HEADTYPE == obj.Hdarwin {
+		Diag("adddynsym: missed symbol %s (%s)", s.Name, s.Extname)
+	} else if HEADTYPE == obj.Hwindows {
+		// already taken care of
+	} else {
+		Diag("adddynsym: unsupported binary format")
+	}
+}
+
 var markq *LSym
 
 var emarkq *LSym
@@ -759,7 +775,7 @@ func addexport() {
 	}
 
 	for i := 0; i < len(dynexp); i++ {
-		Thearch.Adddynsym(Ctxt, dynexp[i])
+		Adddynsym(Ctxt, dynexp[i])
 	}
 }
 
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index 5ab5f653f3..cc0840c04a 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -95,7 +95,6 @@ type Arch struct {
 	Dragonflydynld   string
 	Solarisdynld     string
 	Adddynrel        func(*LSym, *Reloc)
-	Adddynsym        func(*Link, *LSym)
 	Archinit         func()
 	Archreloc        func(*Reloc, *LSym, *int64) int
 	Archrelocvariant func(*Reloc, *LSym, int64) int64

From ddc4c146a46cd8ae3a4f1f9b7f0cd14f4bb2aca4 Mon Sep 17 00:00:00 2001
From: Michael Hudson-Doyle 
Date: Tue, 12 May 2015 16:07:05 +1200
Subject: [PATCH 120/232] cmd/internal/ld: prevent creation of .dynamic and
 .dynsym symbols when externally linking

This allows the removal of a fudge in data.go.

We have to defer the calls to adddynlib on non-Darwin until after we have
decided whether we are externally or internally linking.  The Macho/ELF
separation could do with some cleaning up, but: code freeze.

Fixing this once rather than per-arch is what inspired the previous CLs.

Change-Id: I0166f7078a045dc09827745479211247466c0c54
Reviewed-on: https://go-review.googlesource.com/10002
Run-TryBot: Michael Hudson-Doyle 
TryBot-Result: Gobot Gobot 
Reviewed-by: Russ Cox 
---
 src/cmd/internal/ld/data.go |  8 +-------
 src/cmd/internal/ld/go.go   | 19 ++++++++++++-------
 src/cmd/internal/ld/lib.go  |  1 +
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go
index b0157547c3..b65b667f98 100644
--- a/src/cmd/internal/ld/data.go
+++ b/src/cmd/internal/ld/data.go
@@ -1127,13 +1127,7 @@ func proggenaddsym(g *ProgGen, s *LSym) {
 	proggenskip(g, g.pos, s.Value-g.pos)
 	g.pos = s.Value
 
-	// The test for names beginning with . here is meant
-	// to keep .dynamic and .dynsym from turning up as
-	// conservative symbols. They should be marked SELFSECT
-	// and not SDATA, but sometimes that doesn't happen.
-	// Leave debugging the SDATA issue for the Go rewrite.
-
-	if s.Gotype == nil && s.Size >= int64(Thearch.Ptrsize) && s.Name[0] != '.' {
+	if s.Gotype == nil && s.Size >= int64(Thearch.Ptrsize) {
 		Diag("missing Go type information for global symbol: %s size %d", s.Name, int(s.Size))
 		return
 	}
diff --git a/src/cmd/internal/ld/go.go b/src/cmd/internal/ld/go.go
index a5b09202e8..875b8d2e17 100644
--- a/src/cmd/internal/ld/go.go
+++ b/src/cmd/internal/ld/go.go
@@ -416,7 +416,11 @@ func loadcgo(file string, pkg string, p string) {
 				// to force a link of foo.so.
 				havedynamic = 1
 
-				adddynlib(lib)
+				if HEADTYPE == obj.Hdarwin {
+					Machoadddynlib(lib)
+				} else {
+					dynlib = append(dynlib, lib)
+				}
 				continue
 			}
 
@@ -537,7 +541,7 @@ err:
 var seenlib = make(map[string]bool)
 
 func adddynlib(lib string) {
-	if seenlib[lib] {
+	if seenlib[lib] || Linkmode == LinkExternal {
 		return
 	}
 	seenlib[lib] = true
@@ -548,15 +552,13 @@ func adddynlib(lib string) {
 			Addstring(s, "")
 		}
 		Elfwritedynent(Linklookup(Ctxt, ".dynamic", 0), DT_NEEDED, uint64(Addstring(s, lib)))
-	} else if HEADTYPE == obj.Hdarwin {
-		Machoadddynlib(lib)
 	} else {
 		Diag("adddynlib: unsupported binary format")
 	}
 }
 
 func Adddynsym(ctxt *Link, s *LSym) {
-	if s.Dynid >= 0 {
+	if s.Dynid >= 0 || Linkmode == LinkExternal {
 		return
 	}
 
@@ -774,8 +776,11 @@ func addexport() {
 		return
 	}
 
-	for i := 0; i < len(dynexp); i++ {
-		Adddynsym(Ctxt, dynexp[i])
+	for _, exp := range dynexp {
+		Adddynsym(Ctxt, exp)
+	}
+	for _, lib := range dynlib {
+		adddynlib(lib)
 	}
 }
 
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index cc0840c04a..a0d03ef22d 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -178,6 +178,7 @@ var (
 	Thelinkarch        *LinkArch
 	outfile            string
 	dynexp             []*LSym
+	dynlib             []string
 	ldflag             []string
 	havedynamic        int
 	Funcalign          int

From d820d5f3ab49bec0fb5f8a177ed48b99502a0be1 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Fri, 15 May 2015 16:05:52 -0400
Subject: [PATCH 121/232] runtime: make mapzero not crash on arm

Change-Id: I40e8a4a2e62253233b66f6a2e61e222437292c31
Reviewed-on: https://go-review.googlesource.com/10151
Reviewed-by: Minux Ma 
---
 src/runtime/hashmap.go | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/runtime/hashmap.go b/src/runtime/hashmap.go
index 2b3af301b3..b199330a1e 100644
--- a/src/runtime/hashmap.go
+++ b/src/runtime/hashmap.go
@@ -1008,6 +1008,18 @@ var zerotiny [1024]byte
 // Types allocated by package reflect are in writable memory and
 // start out with zero set to nil; we initialize those on demand.
 func mapzero(t *_type) {
+	// On ARM, atomicloadp is implemented as xadd(p, 0),
+	// so we cannot use atomicloadp on read-only memory.
+	// Check whether the pointer is in the heap; if not, it's not writable
+	// so the zero value must already be set.
+	if GOARCH == "arm" && !inheap(uintptr(unsafe.Pointer(t))) {
+		if t.zero == nil {
+			print("runtime: map element ", *t._string, " missing zero value\n")
+			throw("mapzero")
+		}
+		return
+	}
+
 	// Already done?
 	// Check without lock, so must use atomicload to sync with atomicstore in allocation case below.
 	if atomicloadp(unsafe.Pointer(&t.zero)) != nil {

From ebe733cb40c49148b2fe53d27ce9b1f76993591e Mon Sep 17 00:00:00 2001
From: Didier Spezia 
Date: Thu, 14 May 2015 16:44:58 +0000
Subject: [PATCH 122/232] text/template: fix race condition on function maps

The Template objects are supposed to be goroutine-safe once they
have been parsed. This includes the text and html ones.

For html/template, the escape mechanism is triggered at execution
time. It may alter the internal structures of the template, so
a mutex protects them against concurrent accesses.

The text/template package is free of any synchronization primitive.

A race condition may occur when nested templates are escaped:
the escape algorithm alters the function maps of the associated
text templates, while a concurrent template execution may access
the function maps in read mode.

The less invasive fix I have found is to introduce a RWMutex in
text/template to protect the function maps. This is unfortunate
but it should be effective.

Fixes #9945

Change-Id: I1edb73c0ed0f1fcddd2f1516230b548b92ab1269
Reviewed-on: https://go-review.googlesource.com/10101
Reviewed-by: Rob Pike 
---
 src/text/template/funcs.go    |  2 ++
 src/text/template/template.go | 12 ++++++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/text/template/funcs.go b/src/text/template/funcs.go
index cdd187bda2..ccd0dfc80d 100644
--- a/src/text/template/funcs.go
+++ b/src/text/template/funcs.go
@@ -92,6 +92,8 @@ func goodFunc(typ reflect.Type) bool {
 // findFunction looks for a function in the template, and global map.
 func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
 	if tmpl != nil && tmpl.common != nil {
+		tmpl.muFuncs.RLock()
+		defer tmpl.muFuncs.RUnlock()
 		if fn := tmpl.execFuncs[name]; fn.IsValid() {
 			return fn, true
 		}
diff --git a/src/text/template/template.go b/src/text/template/template.go
index 8611faad9f..a7c5c8cd2c 100644
--- a/src/text/template/template.go
+++ b/src/text/template/template.go
@@ -7,18 +7,20 @@ package template
 import (
 	"fmt"
 	"reflect"
+	"sync"
 	"text/template/parse"
 )
 
 // common holds the information shared by related templates.
 type common struct {
-	tmpl map[string]*Template
+	tmpl   map[string]*Template
+	option option
 	// We use two maps, one for parsing and one for execution.
 	// This separation makes the API cleaner since it doesn't
 	// expose reflection to the client.
+	muFuncs    sync.RWMutex // protects parseFuncs and execFuncs
 	parseFuncs FuncMap
 	execFuncs  map[string]reflect.Value
-	option     option
 }
 
 // Template is the representation of a parsed template. The *parse.Tree
@@ -84,6 +86,8 @@ func (t *Template) Clone() (*Template, error) {
 		tmpl := v.copy(nt.common)
 		nt.tmpl[k] = tmpl
 	}
+	t.muFuncs.RLock()
+	defer t.muFuncs.RUnlock()
 	for k, v := range t.parseFuncs {
 		nt.parseFuncs[k] = v
 	}
@@ -146,6 +150,8 @@ func (t *Template) Delims(left, right string) *Template {
 // value is the template, so calls can be chained.
 func (t *Template) Funcs(funcMap FuncMap) *Template {
 	t.init()
+	t.muFuncs.Lock()
+	defer t.muFuncs.Unlock()
 	addValueFuncs(t.execFuncs, funcMap)
 	addFuncs(t.parseFuncs, funcMap)
 	return t
@@ -169,7 +175,9 @@ func (t *Template) Lookup(name string) *Template {
 // can contain text other than space, comments, and template definitions.)
 func (t *Template) Parse(text string) (*Template, error) {
 	t.init()
+	t.muFuncs.RLock()
 	trees, err := parse.Parse(t.name, text, t.leftDelim, t.rightDelim, t.parseFuncs, builtins)
+	t.muFuncs.RUnlock()
 	if err != nil {
 		return nil, err
 	}

From 512f75e8dfc7653c873971480c23ba308307b85a Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Fri, 8 May 2015 01:43:18 -0400
Subject: [PATCH 123/232] runtime: replace GC programs with simpler encoding,
 faster decoder
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Small types record the location of pointers in their memory layout
by using a simple bitmap. In Go 1.4 the bitmap held 4-bit entries,
and in Go 1.5 the bitmap holds 1-bit entries, but in both cases using
a bitmap for a large type containing arrays does not make sense:
if someone refers to the type [1<<28]*byte in a program in such
a way that the type information makes it into the binary, it would be
a waste of space to write a 128 MB (for 4-bit entries) or even 32 MB
(for 1-bit entries) bitmap full of 1s into the binary or even to keep
one in memory during the execution of the program.

For large types containing arrays, it is much more compact to describe
the locations of pointers using a notation that can express repetition
than to lay out a bitmap of pointers. Go 1.4 included such a notation,
called ``GC programs'' but it was complex, required recursion during
decoding, and was generally slow. Dmitriy measured the execution of
these programs writing directly to the heap bitmap as being 7x slower
than copying from a preunrolled 4-bit mask (and frankly that code was
not terribly fast either). For some tests, unrollgcprog1 was seen costing
as much as 3x more than the rest of malloc combined.

This CL introduces a different form for the GC programs. They use a
simple Lempel-Ziv-style encoding of the 1-bit pointer information,
in which the only operations are (1) emit the following n bits
and (2) repeat the last n bits c more times. This encoding can be
generated directly from the Go type information (using repetition
only for arrays or large runs of non-pointer data) and it can be decoded
very efficiently. In particular the decoding requires little state and
no recursion, so that the entire decoding can run without any memory
accesses other than the reads of the encoding and the writes of the
decoded form to the heap bitmap. For recursive types like arrays of
arrays of arrays, the inner instructions are only executed once, not
n times, so that large repetitions run at full speed. (In contrast, large
repetitions in the old programs repeated the individual bit-level layout
of the inner data over and over.) The result is as much as 25x faster
decoding compared to the old form.

Because the old decoder was so slow, Go 1.4 had three (or so) cases
for how to set the heap bitmap bits for an allocation of a given type:

(1) If the type had an even number of words up to 32 words, then
the 4-bit pointer mask for the type fit in no more than 16 bytes;
store the 4-bit pointer mask directly in the binary and copy from it.

(1b) If the type had an odd number of words up to 15 words, then
the 4-bit pointer mask for the type, doubled to end on a byte boundary,
fit in no more than 16 bytes; store that doubled mask directly in the
binary and copy from it.

(2) If the type had an even number of words up to 128 words,
or an odd number of words up to 63 words (again due to doubling),
then the 4-bit pointer mask would fit in a 64-byte unrolled mask.
Store a GC program in the binary, but leave space in the BSS for
the unrolled mask. Execute the GC program to construct the mask the
first time it is needed, and thereafter copy from the mask.

(3) Otherwise, store a GC program and execute it to write directly to
the heap bitmap each time an object of that type is allocated.
(This is the case that was 7x slower than the other two.)

Because the new pointer masks store 1-bit entries instead of 4-bit
entries and because using the decoder no longer carries a significant
overhead, after this CL (that is, for Go 1.5) there are only two cases:

(1) If the type is 128 words or less (no condition about odd or even),
store the 1-bit pointer mask directly in the binary and use it to
initialize the heap bitmap during malloc. (Implemented in CL 9702.)

(2) There is no case 2 anymore.

(3) Otherwise, store a GC program and execute it to write directly to
the heap bitmap each time an object of that type is allocated.

Executing the GC program directly into the heap bitmap (case (3) above)
was disabled for the Go 1.5 dev cycle, both to avoid needing to use
GC programs for typedmemmove and to avoid updating that code as
the heap bitmap format changed. Typedmemmove no longer uses this
type information; as of CL 9886 it uses the heap bitmap directly.
Now that the heap bitmap format is stable, we reintroduce GC programs
and their space savings.

Benchmarks for heapBitsSetType, before this CL vs this CL:

name                    old mean               new mean              delta
SetTypePtr              7.59ns × (0.99,1.02)   5.16ns × (1.00,1.00)  -32.05% (p=0.000)
SetTypePtr8             21.0ns × (0.98,1.05)   21.4ns × (1.00,1.00)     ~    (p=0.179)
SetTypePtr16            24.1ns × (0.99,1.01)   24.6ns × (1.00,1.00)   +2.41% (p=0.001)
SetTypePtr32            31.2ns × (0.99,1.01)   32.4ns × (0.99,1.02)   +3.72% (p=0.001)
SetTypePtr64            45.2ns × (1.00,1.00)   47.2ns × (1.00,1.00)   +4.42% (p=0.000)
SetTypePtr126           75.8ns × (0.99,1.01)   79.1ns × (1.00,1.00)   +4.25% (p=0.000)
SetTypePtr128           74.3ns × (0.99,1.01)   77.6ns × (1.00,1.01)   +4.55% (p=0.000)
SetTypePtrSlice          726ns × (1.00,1.01)    712ns × (1.00,1.00)   -1.95% (p=0.001)
SetTypeNode1            20.0ns × (0.99,1.01)   20.7ns × (1.00,1.00)   +3.71% (p=0.000)
SetTypeNode1Slice        112ns × (1.00,1.00)    113ns × (0.99,1.00)     ~    (p=0.070)
SetTypeNode8            23.9ns × (1.00,1.00)   24.7ns × (1.00,1.01)   +3.18% (p=0.000)
SetTypeNode8Slice        294ns × (0.99,1.02)    287ns × (0.99,1.01)   -2.38% (p=0.015)
SetTypeNode64           52.8ns × (0.99,1.03)   51.8ns × (0.99,1.01)     ~    (p=0.069)
SetTypeNode64Slice      1.13µs × (0.99,1.05)   1.14µs × (0.99,1.00)     ~    (p=0.767)
SetTypeNode64Dead       36.0ns × (1.00,1.01)   32.5ns × (0.99,1.00)   -9.67% (p=0.000)
SetTypeNode64DeadSlice  1.43µs × (0.99,1.01)   1.40µs × (1.00,1.00)   -2.39% (p=0.001)
SetTypeNode124          75.7ns × (1.00,1.01)   79.0ns × (1.00,1.00)   +4.44% (p=0.000)
SetTypeNode124Slice     1.94µs × (1.00,1.01)   2.04µs × (0.99,1.01)   +4.98% (p=0.000)
SetTypeNode126          75.4ns × (1.00,1.01)   77.7ns × (0.99,1.01)   +3.11% (p=0.000)
SetTypeNode126Slice     1.95µs × (0.99,1.01)   2.03µs × (1.00,1.00)   +3.74% (p=0.000)
SetTypeNode128          85.4ns × (0.99,1.01)  122.0ns × (1.00,1.00)  +42.89% (p=0.000)
SetTypeNode128Slice     2.20µs × (1.00,1.01)   2.36µs × (0.98,1.02)   +7.48% (p=0.001)
SetTypeNode130          83.3ns × (1.00,1.00)  123.0ns × (1.00,1.00)  +47.61% (p=0.000)
SetTypeNode130Slice     2.30µs × (0.99,1.01)   2.40µs × (0.98,1.01)   +4.37% (p=0.000)
SetTypeNode1024          498ns × (1.00,1.00)    537ns × (1.00,1.00)   +7.96% (p=0.000)
SetTypeNode1024Slice    15.5µs × (0.99,1.01)   17.8µs × (1.00,1.00)  +15.27% (p=0.000)

The above compares always using a cached pointer mask (and the
corresponding waste of memory) against using the programs directly.
Some slowdown is expected, in exchange for having a better general algorithm.
The GC programs kick in for SetTypeNode128, SetTypeNode130, SetTypeNode1024,
along with the slice variants of those.
It is possible that the cutoff of 128 words (bits) should be raised
in a followup CL, but even with this low cutoff the GC programs are
faster than Go 1.4's "fast path" non-GC program case.

Benchmarks for heapBitsSetType, Go 1.4 vs this CL:

name                    old mean              new mean              delta
SetTypePtr              6.89ns × (1.00,1.00)  5.17ns × (1.00,1.00)  -25.02% (p=0.000)
SetTypePtr8             25.8ns × (0.97,1.05)  21.5ns × (1.00,1.00)  -16.70% (p=0.000)
SetTypePtr16            39.8ns × (0.97,1.02)  24.7ns × (0.99,1.01)  -37.81% (p=0.000)
SetTypePtr32            68.8ns × (0.98,1.01)  32.2ns × (1.00,1.01)  -53.18% (p=0.000)
SetTypePtr64             130ns × (1.00,1.00)    47ns × (1.00,1.00)  -63.67% (p=0.000)
SetTypePtr126            241ns × (0.99,1.01)    79ns × (1.00,1.01)  -67.25% (p=0.000)
SetTypePtr128           2.07µs × (1.00,1.00)  0.08µs × (1.00,1.00)  -96.27% (p=0.000)
SetTypePtrSlice         1.05µs × (0.99,1.01)  0.72µs × (0.99,1.02)  -31.70% (p=0.000)
SetTypeNode1            16.0ns × (0.99,1.01)  20.8ns × (0.99,1.03)  +29.91% (p=0.000)
SetTypeNode1Slice        184ns × (0.99,1.01)   112ns × (0.99,1.01)  -39.26% (p=0.000)
SetTypeNode8            29.5ns × (0.97,1.02)  24.6ns × (1.00,1.00)  -16.50% (p=0.000)
SetTypeNode8Slice        624ns × (0.98,1.02)   285ns × (1.00,1.00)  -54.31% (p=0.000)
SetTypeNode64            135ns × (0.96,1.08)    52ns × (0.99,1.02)  -61.32% (p=0.000)
SetTypeNode64Slice      3.83µs × (1.00,1.00)  1.14µs × (0.99,1.01)  -70.16% (p=0.000)
SetTypeNode64Dead        134ns × (0.99,1.01)    32ns × (1.00,1.01)  -75.74% (p=0.000)
SetTypeNode64DeadSlice  3.83µs × (0.99,1.00)  1.40µs × (1.00,1.01)  -63.42% (p=0.000)
SetTypeNode124           240ns × (0.99,1.01)    79ns × (1.00,1.01)  -67.05% (p=0.000)
SetTypeNode124Slice     7.27µs × (1.00,1.00)  2.04µs × (1.00,1.00)  -71.95% (p=0.000)
SetTypeNode126          2.06µs × (0.99,1.01)  0.08µs × (0.99,1.01)  -96.23% (p=0.000)
SetTypeNode126Slice     64.4µs × (1.00,1.00)   2.0µs × (1.00,1.00)  -96.85% (p=0.000)
SetTypeNode128          2.09µs × (1.00,1.01)  0.12µs × (1.00,1.00)  -94.15% (p=0.000)
SetTypeNode128Slice     65.4µs × (1.00,1.00)   2.4µs × (0.99,1.03)  -96.39% (p=0.000)
SetTypeNode130          2.11µs × (1.00,1.00)  0.12µs × (1.00,1.00)  -94.18% (p=0.000)
SetTypeNode130Slice     66.3µs × (1.00,1.00)   2.4µs × (0.97,1.08)  -96.34% (p=0.000)
SetTypeNode1024         16.0µs × (1.00,1.01)   0.5µs × (1.00,1.00)  -96.65% (p=0.000)
SetTypeNode1024Slice     512µs × (1.00,1.00)    18µs × (0.98,1.04)  -96.45% (p=0.000)

SetTypeNode124 uses a 124 data + 2 ptr = 126-word allocation.
Both Go 1.4 and this CL are using pointer bitmaps for this case,
so that's an overall 3x speedup for using pointer bitmaps.

SetTypeNode128 uses a 128 data + 2 ptr = 130-word allocation.
Both Go 1.4 and this CL are running the GC program for this case,
so that's an overall 17x speedup when using GC programs (and
I've seen >20x on other systems).

Comparing Go 1.4's SetTypeNode124 (pointer bitmap) against
this CL's SetTypeNode128 (GC program), the slow path in the
code in this CL is 2x faster than the fast path in Go 1.4.

The Go 1 benchmarks are basically unaffected compared to just before this CL.

Go 1 benchmarks, before this CL vs this CL:

name                   old mean              new mean              delta
BinaryTree17            5.87s × (0.97,1.04)   5.91s × (0.96,1.04)    ~    (p=0.306)
Fannkuch11              4.38s × (1.00,1.00)   4.37s × (1.00,1.01)  -0.22% (p=0.006)
FmtFprintfEmpty        90.7ns × (0.97,1.10)  89.3ns × (0.96,1.09)    ~    (p=0.280)
FmtFprintfString        282ns × (0.98,1.04)   287ns × (0.98,1.07)  +1.72% (p=0.039)
FmtFprintfInt           269ns × (0.99,1.03)   282ns × (0.97,1.04)  +4.87% (p=0.000)
FmtFprintfIntInt        478ns × (0.99,1.02)   481ns × (0.99,1.02)  +0.61% (p=0.048)
FmtFprintfPrefixedInt   399ns × (0.98,1.03)   400ns × (0.98,1.05)    ~    (p=0.533)
FmtFprintfFloat         563ns × (0.99,1.01)   570ns × (1.00,1.01)  +1.37% (p=0.000)
FmtManyArgs            1.89µs × (0.99,1.01)  1.92µs × (0.99,1.02)  +1.88% (p=0.000)
GobDecode              15.2ms × (0.99,1.01)  15.2ms × (0.98,1.05)    ~    (p=0.609)
GobEncode              11.6ms × (0.98,1.03)  11.9ms × (0.98,1.04)  +2.17% (p=0.000)
Gzip                    648ms × (0.99,1.01)   648ms × (1.00,1.01)    ~    (p=0.835)
Gunzip                  142ms × (1.00,1.00)   143ms × (1.00,1.01)    ~    (p=0.169)
HTTPClientServer       90.5µs × (0.98,1.03)  91.5µs × (0.98,1.04)  +1.04% (p=0.045)
JSONEncode             31.5ms × (0.98,1.03)  31.4ms × (0.98,1.03)    ~    (p=0.549)
JSONDecode              111ms × (0.99,1.01)   107ms × (0.99,1.01)  -3.21% (p=0.000)
Mandelbrot200          6.01ms × (1.00,1.00)  6.01ms × (1.00,1.00)    ~    (p=0.878)
GoParse                6.54ms × (0.99,1.02)  6.61ms × (0.99,1.03)  +1.08% (p=0.004)
RegexpMatchEasy0_32     160ns × (1.00,1.01)   161ns × (1.00,1.00)  +0.40% (p=0.000)
RegexpMatchEasy0_1K     560ns × (0.99,1.01)   559ns × (0.99,1.01)    ~    (p=0.088)
RegexpMatchEasy1_32     138ns × (0.99,1.01)   138ns × (1.00,1.00)    ~    (p=0.380)
RegexpMatchEasy1_1K     877ns × (1.00,1.00)   878ns × (1.00,1.00)    ~    (p=0.157)
RegexpMatchMedium_32    251ns × (0.99,1.00)   251ns × (1.00,1.01)  +0.28% (p=0.021)
RegexpMatchMedium_1K   72.6µs × (1.00,1.00)  72.6µs × (1.00,1.00)    ~    (p=0.539)
RegexpMatchHard_32     3.84µs × (1.00,1.00)  3.84µs × (1.00,1.00)    ~    (p=0.378)
RegexpMatchHard_1K      117µs × (1.00,1.00)   117µs × (1.00,1.00)    ~    (p=0.067)
Revcomp                 904ms × (0.99,1.02)   904ms × (0.99,1.01)    ~    (p=0.943)
Template                125ms × (0.99,1.02)   127ms × (0.99,1.01)  +1.79% (p=0.000)
TimeParse               627ns × (0.99,1.01)   622ns × (0.99,1.01)  -0.88% (p=0.000)
TimeFormat              655ns × (0.99,1.02)   655ns × (0.99,1.02)    ~    (p=0.976)

For the record, Go 1 benchmarks, Go 1.4 vs this CL:

name                   old mean              new mean              delta
BinaryTree17            4.61s × (0.97,1.05)   5.91s × (0.98,1.03)  +28.35% (p=0.000)
Fannkuch11              4.40s × (0.99,1.03)   4.41s × (0.99,1.01)     ~    (p=0.212)
FmtFprintfEmpty         102ns × (0.99,1.01)    84ns × (0.99,1.02)  -18.38% (p=0.000)
FmtFprintfString        302ns × (0.98,1.01)   303ns × (0.99,1.02)     ~    (p=0.203)
FmtFprintfInt           313ns × (0.97,1.05)   270ns × (0.99,1.01)  -13.69% (p=0.000)
FmtFprintfIntInt        524ns × (0.98,1.02)   477ns × (0.99,1.00)   -8.87% (p=0.000)
FmtFprintfPrefixedInt   424ns × (0.98,1.02)   386ns × (0.99,1.01)   -8.96% (p=0.000)
FmtFprintfFloat         652ns × (0.98,1.02)   594ns × (0.97,1.05)   -8.97% (p=0.000)
FmtManyArgs            2.13µs × (0.99,1.02)  1.94µs × (0.99,1.01)   -8.92% (p=0.000)
GobDecode              17.1ms × (0.99,1.02)  14.9ms × (0.98,1.03)  -13.07% (p=0.000)
GobEncode              13.5ms × (0.98,1.03)  11.5ms × (0.98,1.03)  -15.25% (p=0.000)
Gzip                    656ms × (0.99,1.02)   647ms × (0.99,1.01)   -1.29% (p=0.000)
Gunzip                  143ms × (0.99,1.02)   144ms × (0.99,1.01)     ~    (p=0.204)
HTTPClientServer       88.2µs × (0.98,1.02)  90.8µs × (0.98,1.01)   +2.93% (p=0.000)
JSONEncode             32.2ms × (0.98,1.02)  30.9ms × (0.97,1.04)   -4.06% (p=0.001)
JSONDecode              121ms × (0.98,1.02)   110ms × (0.98,1.05)   -8.95% (p=0.000)
Mandelbrot200          6.06ms × (0.99,1.01)  6.11ms × (0.98,1.04)     ~    (p=0.184)
GoParse                6.76ms × (0.97,1.04)  6.58ms × (0.98,1.05)   -2.63% (p=0.003)
RegexpMatchEasy0_32     195ns × (1.00,1.01)   155ns × (0.99,1.01)  -20.43% (p=0.000)
RegexpMatchEasy0_1K     479ns × (0.98,1.03)   535ns × (0.99,1.02)  +11.59% (p=0.000)
RegexpMatchEasy1_32     169ns × (0.99,1.02)   131ns × (0.99,1.03)  -22.44% (p=0.000)
RegexpMatchEasy1_1K    1.53µs × (0.99,1.01)  0.87µs × (0.99,1.02)  -43.07% (p=0.000)
RegexpMatchMedium_32    334ns × (0.99,1.01)   242ns × (0.99,1.01)  -27.53% (p=0.000)
RegexpMatchMedium_1K    125µs × (1.00,1.01)    72µs × (0.99,1.03)  -42.53% (p=0.000)
RegexpMatchHard_32     6.03µs × (0.99,1.01)  3.79µs × (0.99,1.01)  -37.12% (p=0.000)
RegexpMatchHard_1K      189µs × (0.99,1.02)   115µs × (0.99,1.01)  -39.20% (p=0.000)
Revcomp                 935ms × (0.96,1.03)   926ms × (0.98,1.02)     ~    (p=0.083)
Template                146ms × (0.97,1.05)   119ms × (0.99,1.01)  -18.37% (p=0.000)
TimeParse               660ns × (0.99,1.01)   624ns × (0.99,1.02)   -5.43% (p=0.000)
TimeFormat              670ns × (0.98,1.02)   710ns × (1.00,1.01)   +5.97% (p=0.000)

This CL is a bit larger than I would like, but the compiler, linker, runtime,
and package reflect all need to be in sync about the format of these programs,
so there is no easy way to split this into independent changes (at least
while keeping the build working at each change).

Fixes #9625.
Fixes #10524.

Change-Id: I9e3e20d6097099d0f8532d1cb5b1af528804989a
Reviewed-on: https://go-review.googlesource.com/9888
Reviewed-by: Austin Clements 
Run-TryBot: Russ Cox 
---
 src/cmd/dist/buildtool.go         |   1 +
 src/cmd/internal/gc/lex.go        |   9 +-
 src/cmd/internal/gc/plive.go      |   2 +-
 src/cmd/internal/gc/reflect.go    | 384 ++++++---------
 src/cmd/internal/gcprog/gcprog.go | 298 ++++++++++++
 src/cmd/internal/ld/data.go       | 191 +++-----
 src/cmd/internal/ld/decodesym.go  |   4 +-
 src/cmd/internal/ld/lib.go        |   2 +-
 src/cmd/internal/ld/link.go       |   8 +
 src/cmd/internal/ld/objfile.go    |   2 +-
 src/reflect/all_test.go           | 135 +++++-
 src/reflect/export_test.go        |  18 +-
 src/reflect/type.go               | 389 ++++++++-------
 src/reflect/value.go              |   2 +-
 src/runtime/mbitmap.go            | 766 +++++++++++++++++++++---------
 src/runtime/mgc.go                |   4 +-
 src/runtime/symtab.go             |   2 +
 src/runtime/traceback.go          |   2 +-
 src/runtime/type.go               |  15 +-
 19 files changed, 1418 insertions(+), 816 deletions(-)
 create mode 100644 src/cmd/internal/gcprog/gcprog.go

diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index e25367b25c..946229d827 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -39,6 +39,7 @@ var bootstrapDirs = []string{
 	"asm/internal/flags",
 	"asm/internal/lex",
 	"internal/asm",
+	"internal/gcprog",
 	"internal/gc/big",
 	"internal/gc",
 	"internal/ld",
diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/internal/gc/lex.go
index 92c079e154..f9211407fb 100644
--- a/src/cmd/internal/gc/lex.go
+++ b/src/cmd/internal/gc/lex.go
@@ -48,12 +48,13 @@ var debugtab = []struct {
 	name string
 	val  *int
 }{
-	{"nil", &Debug_checknil},          // print information about nil checks
-	{"typeassert", &Debug_typeassert}, // print information about type assertion inlining
-	{"disablenil", &Disable_checknil}, // disable nil checks
-	{"wb", &Debug_wb},                 // print information about write barriers
 	{"append", &Debug_append},         // print information about append compilation
+	{"disablenil", &Disable_checknil}, // disable nil checks
+	{"gcprog", &Debug_gcprog},         // print dump of GC programs
+	{"nil", &Debug_checknil},          // print information about nil checks
 	{"slice", &Debug_slice},           // print information about slice compilation
+	{"typeassert", &Debug_typeassert}, // print information about type assertion inlining
+	{"wb", &Debug_wb},                 // print information about write barriers
 }
 
 // Our own isdigit, isspace, isalpha, isalnum that take care
diff --git a/src/cmd/internal/gc/plive.go b/src/cmd/internal/gc/plive.go
index 977789f3e4..b4d0699d1f 100644
--- a/src/cmd/internal/gc/plive.go
+++ b/src/cmd/internal/gc/plive.go
@@ -944,7 +944,7 @@ func onebitwalktype1(t *Type, xoffset *int64, bv Bvec) {
 		*xoffset += t.Width
 
 	case TARRAY:
-		// The value of t->bound is -1 for slices types and >0 for
+		// The value of t->bound is -1 for slices types and >=0 for
 		// for fixed array types.  All other values are invalid.
 		if t.Bound < -1 {
 			Fatal("onebitwalktype1: invalid bound, %v", t)
diff --git a/src/cmd/internal/gc/reflect.go b/src/cmd/internal/gc/reflect.go
index 061b17b3ae..6c0962f258 100644
--- a/src/cmd/internal/gc/reflect.go
+++ b/src/cmd/internal/gc/reflect.go
@@ -5,8 +5,10 @@
 package gc
 
 import (
+	"cmd/internal/gcprog"
 	"cmd/internal/obj"
 	"fmt"
+	"os"
 )
 
 /*
@@ -771,6 +773,8 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 	// The linker magically takes the max of all the sizes.
 	zero := Pkglookup("zerovalue", Runtimepkg)
 
+	gcsym, useGCProg, ptrdata := dgcsym(t)
+
 	// We use size 0 here so we get the pointer to the zero value,
 	// but don't allocate space for the zero value unless we need it.
 	// TODO: how do we get this symbol into bss?  We really want
@@ -787,14 +791,14 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 	//		fieldAlign    uint8
 	//		kind          uint8
 	//		alg           unsafe.Pointer
-	//		gc            unsafe.Pointer
+	//		gcdata        unsafe.Pointer
 	//		string        *string
 	//		*extraType
 	//		ptrToThis     *Type
 	//		zero          unsafe.Pointer
 	//	}
 	ot = duintptr(s, ot, uint64(t.Width))
-	ot = duintptr(s, ot, uint64(typeptrdata(t)))
+	ot = duintptr(s, ot, uint64(ptrdata))
 
 	ot = duint32(s, ot, typehash(t))
 	ot = duint8(s, ot, 0) // unused
@@ -811,8 +815,6 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 	ot = duint8(s, ot, t.Align) // align
 	ot = duint8(s, ot, t.Align) // fieldAlign
 
-	gcprog := usegcprog(t)
-
 	i = kinds[t.Etype]
 	if t.Etype == TARRAY && t.Bound < 0 {
 		i = obj.KindSlice
@@ -823,7 +825,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 	if isdirectiface(t) {
 		i |= obj.KindDirectIface
 	}
-	if gcprog {
+	if useGCProg {
 		i |= obj.KindGCProg
 	}
 	ot = duint8(s, ot, uint8(i)) // kind
@@ -832,48 +834,7 @@ func dcommontype(s *Sym, ot int, t *Type) int {
 	} else {
 		ot = dsymptr(s, ot, algsym, 0)
 	}
-
-	// gc
-	if gcprog {
-		var gcprog1 *Sym
-		var gcprog0 *Sym
-		gengcprog(t, &gcprog0, &gcprog1)
-		if gcprog0 != nil {
-			ot = dsymptr(s, ot, gcprog0, 0)
-		} else {
-			ot = duintptr(s, ot, 0)
-		}
-		ot = dsymptr(s, ot, gcprog1, 0)
-	} else {
-		var gcmask [16]uint8
-		gengcmask(t, gcmask[:])
-		x1 := uint64(0)
-		for i := 0; i < 8; i++ {
-			x1 = x1<<8 | uint64(gcmask[i])
-		}
-		var p string
-		if Widthptr == 4 {
-			p = fmt.Sprintf("gcbits.0x%016x", x1)
-		} else {
-			x2 := uint64(0)
-			for i := 0; i < 8; i++ {
-				x2 = x2<<8 | uint64(gcmask[i+8])
-			}
-			p = fmt.Sprintf("gcbits.0x%016x%016x", x1, x2)
-		}
-
-		sbits := Pkglookup(p, Runtimepkg)
-		if sbits.Flags&SymUniq == 0 {
-			sbits.Flags |= SymUniq
-			for i := 0; i < 2*Widthptr; i++ {
-				duint8(sbits, i, gcmask[i])
-			}
-			ggloblsym(sbits, 2*int32(Widthptr), obj.DUPOK|obj.RODATA|obj.LOCAL)
-		}
-
-		ot = dsymptr(s, ot, sbits, 0)
-		ot = duintptr(s, ot, 0)
-	}
+	ot = dsymptr(s, ot, gcsym, 0)
 
 	p := Tconv(t, obj.FmtLeft|obj.FmtUnsigned)
 
@@ -1419,228 +1380,193 @@ func dalgsym(t *Type) *Sym {
 	return s
 }
 
-func usegcprog(t *Type) bool {
-	if !haspointers(t) {
-		return false
-	}
-	if t.Width == BADWIDTH {
-		dowidth(t)
+// maxPtrmaskBytes is the maximum length of a GC ptrmask bitmap,
+// which holds 1-bit entries describing where pointers are in a given type.
+// 16 bytes is enough to describe 128 pointer-sized words, 512 or 1024 bytes
+// depending on the system. Above this length, the GC information is
+// recorded as a GC program, which can express repetition compactly.
+// In either form, the information is used by the runtime to initialize the
+// heap bitmap, and for large types (like 128 or more words), they are
+// roughly the same speed. GC programs are never much larger and often
+// more compact. (If large arrays are involved, they can be arbitrarily more
+// compact.)
+//
+// The cutoff must be large enough that any allocation large enough to
+// use a GC program is large enough that it does not share heap bitmap
+// bytes with any other objects, allowing the GC program execution to
+// assume an aligned start and not use atomic operations. In the current
+// runtime, this means all malloc size classes larger than the cutoff must
+// be multiples of four words. On 32-bit systems that's 16 bytes, and
+// all size classes >= 16 bytes are 16-byte aligned, so no real constraint.
+// On 64-bit systems, that's 32 bytes, and 32-byte alignment is guaranteed
+// for size classes >= 256 bytes. On a 64-bit sytem, 256 bytes allocated
+// is 32 pointers, the bits for which fit in 4 bytes. So maxPtrmaskBytes
+// must be >= 4.
+//
+// We use 16 because the GC programs do have some constant overhead
+// to get started, and processing 128 pointers seems to be enough to
+// amortize that overhead well.
+const maxPtrmaskBytes = 16
+
+// dgcsym emits and returns a data symbol containing GC information for type t,
+// along with a boolean reporting whether the UseGCProg bit should be set in
+// the type kind, and the ptrdata field to record in the reflect type information.
+func dgcsym(t *Type) (sym *Sym, useGCProg bool, ptrdata int64) {
+	ptrdata = typeptrdata(t)
+	if ptrdata/int64(Widthptr) <= maxPtrmaskBytes*8 {
+		sym = dgcptrmask(t)
+		return
 	}
 
-	// Calculate size of the unrolled GC mask.
-	nptr := typeptrdata(t) / int64(Widthptr)
-
-	// Decide whether to use unrolled GC mask or GC program.
-	// We could use a more elaborate condition, but this seems to work well in practice.
-	// For small objects, the GC program can't give significant reduction.
-	return nptr > int64(2*Widthptr*8)
+	useGCProg = true
+	sym, ptrdata = dgcprog(t)
+	return
 }
 
-// Generates GC bitmask (1 bit per word).
-func gengcmask(t *Type, gcmask []byte) {
-	for i := int64(0); i < 16; i++ {
-		gcmask[i] = 0
+// dgcptrmask emits and returns the symbol containing a pointer mask for type t.
+func dgcptrmask(t *Type) *Sym {
+	ptrmask := make([]byte, (typeptrdata(t)/int64(Widthptr)+7)/8)
+	fillptrmask(t, ptrmask)
+	p := fmt.Sprintf("gcbits.%x", ptrmask)
+
+	sym := Pkglookup(p, Runtimepkg)
+	if sym.Flags&SymUniq == 0 {
+		sym.Flags |= SymUniq
+		for i, x := range ptrmask {
+			duint8(sym, i, x)
+		}
+		ggloblsym(sym, int32(len(ptrmask)), obj.DUPOK|obj.RODATA|obj.LOCAL)
+	}
+	return sym
+}
+
+// fillptrmask fills in ptrmask with 1s corresponding to the
+// word offsets in t that hold pointers.
+// ptrmask is assumed to fit at least typeptrdata(t)/Widthptr bits.
+func fillptrmask(t *Type, ptrmask []byte) {
+	for i := range ptrmask {
+		ptrmask[i] = 0
 	}
 	if !haspointers(t) {
 		return
 	}
 
-	vec := bvalloc(int32(2 * Widthptr * 8))
+	vec := bvalloc(8 * int32(len(ptrmask)))
 	xoffset := int64(0)
 	onebitwalktype1(t, &xoffset, vec)
 
 	nptr := typeptrdata(t) / int64(Widthptr)
 	for i := int64(0); i < nptr; i++ {
 		if bvget(vec, int32(i)) == 1 {
-			gcmask[i/8] |= 1 << (uint(i) % 8)
+			ptrmask[i/8] |= 1 << (uint(i) % 8)
 		}
 	}
 }
 
-// Helper object for generation of GC programs.
-type ProgGen struct {
-	s        *Sym
-	datasize int32
-	data     [256 / 8]uint8
-	ot       int64
+// dgcprog emits and returns the symbol containing a GC program for type t
+// along with the size of the data described by the program (in the range [typeptrdata(t), t.Width]).
+// In practice, the size is typeptrdata(t) except for non-trivial arrays.
+// For non-trivial arrays, the program describes the full t.Width size.
+func dgcprog(t *Type) (*Sym, int64) {
+	dowidth(t)
+	if t.Width == BADWIDTH {
+		Fatal("dgcprog: %v badwidth", t)
+	}
+	sym := typesymprefix(".gcprog", t)
+	var p GCProg
+	p.init(sym)
+	p.emit(t, 0)
+	offset := p.w.BitIndex() * int64(Widthptr)
+	p.end()
+	if ptrdata := typeptrdata(t); offset < ptrdata || offset > t.Width {
+		Fatal("dgcprog: %v: offset=%d but ptrdata=%d size=%d", t, offset, ptrdata, t.Width)
+	}
+	return sym, offset
 }
 
-func proggeninit(g *ProgGen, s *Sym) {
-	g.s = s
-	g.datasize = 0
-	g.ot = 0
-	g.data = [256 / 8]uint8{}
+type GCProg struct {
+	sym    *Sym
+	symoff int
+	w      gcprog.Writer
 }
 
-func proggenemit(g *ProgGen, v uint8) {
-	g.ot = int64(duint8(g.s, int(g.ot), v))
+var Debug_gcprog int // set by -d gcprog
+
+func (p *GCProg) init(sym *Sym) {
+	p.sym = sym
+	p.symoff = 4 // first 4 bytes hold program length
+	p.w.Init(p.writeByte)
+	if Debug_gcprog > 0 {
+		fmt.Fprintf(os.Stderr, "compile: start GCProg for %v\n", sym)
+		p.w.Debug(os.Stderr)
+	}
 }
 
-// Emits insData block from g->data.
-func proggendataflush(g *ProgGen) {
-	if g.datasize == 0 {
+func (p *GCProg) writeByte(x byte) {
+	p.symoff = duint8(p.sym, p.symoff, x)
+}
+
+func (p *GCProg) end() {
+	p.w.End()
+	duint32(p.sym, 0, uint32(p.symoff-4))
+	ggloblsym(p.sym, int32(p.symoff), obj.DUPOK|obj.RODATA|obj.LOCAL)
+	if Debug_gcprog > 0 {
+		fmt.Fprintf(os.Stderr, "compile: end GCProg for %v\n", p.sym)
+	}
+}
+
+func (p *GCProg) emit(t *Type, offset int64) {
+	dowidth(t)
+	if !haspointers(t) {
 		return
 	}
-	proggenemit(g, obj.InsData)
-	proggenemit(g, uint8(g.datasize))
-	s := (g.datasize + 7) / 8
-	for i := int32(0); i < s; i++ {
-		proggenemit(g, g.data[i])
+	if t.Width == int64(Widthptr) {
+		p.w.Ptr(offset / int64(Widthptr))
+		return
 	}
-	g.datasize = 0
-	g.data = [256 / 8]uint8{}
-}
-
-func proggendata(g *ProgGen, d uint8) {
-	g.data[g.datasize/8] |= d << uint(g.datasize%8)
-	g.datasize++
-	if g.datasize == 255 {
-		proggendataflush(g)
-	}
-}
-
-// Skip v bytes due to alignment, etc.
-func proggenskip(g *ProgGen, off int64, v int64) {
-	for i := off; i < off+v; i++ {
-		if (i % int64(Widthptr)) == 0 {
-			proggendata(g, 0)
-		}
-	}
-}
-
-// Emit insArray instruction.
-func proggenarray(g *ProgGen, len int64) {
-	proggendataflush(g)
-	proggenemit(g, obj.InsArray)
-	for i := int32(0); i < int32(Widthptr); i, len = i+1, len>>8 {
-		proggenemit(g, uint8(len))
-	}
-}
-
-func proggenarrayend(g *ProgGen) {
-	proggendataflush(g)
-	proggenemit(g, obj.InsArrayEnd)
-}
-
-func proggenfini(g *ProgGen) int64 {
-	proggendataflush(g)
-	proggenemit(g, obj.InsEnd)
-	return g.ot
-}
-
-// Generates GC program for large types.
-func gengcprog(t *Type, pgc0 **Sym, pgc1 **Sym) {
-	nptr := (t.Width + int64(Widthptr) - 1) / int64(Widthptr)
-	size := nptr + 1 // unroll flag in the beginning, used by runtime (see runtime.markallocated)
-
-	// emity space in BSS for unrolled program
-	*pgc0 = nil
-
-	// Don't generate it if it's too large, runtime will unroll directly into GC bitmap.
-	if size <= obj.MaxGCMask {
-		gc0 := typesymprefix(".gc", t)
-		ggloblsym(gc0, int32(size), obj.DUPOK|obj.NOPTR)
-		*pgc0 = gc0
-	}
-
-	// program in RODATA
-	gc1 := typesymprefix(".gcprog", t)
-
-	var g ProgGen
-	proggeninit(&g, gc1)
-	xoffset := int64(0)
-	gengcprog1(&g, t, &xoffset)
-	ot := proggenfini(&g)
-	ggloblsym(gc1, int32(ot), obj.DUPOK|obj.RODATA)
-	*pgc1 = gc1
-}
-
-// Recursively walks type t and writes GC program into g.
-func gengcprog1(g *ProgGen, t *Type, xoffset *int64) {
 	switch t.Etype {
-	case TINT8,
-		TUINT8,
-		TINT16,
-		TUINT16,
-		TINT32,
-		TUINT32,
-		TINT64,
-		TUINT64,
-		TINT,
-		TUINT,
-		TUINTPTR,
-		TBOOL,
-		TFLOAT32,
-		TFLOAT64,
-		TCOMPLEX64,
-		TCOMPLEX128:
-		proggenskip(g, *xoffset, t.Width)
-		*xoffset += t.Width
-
-	case TPTR32,
-		TPTR64,
-		TUNSAFEPTR,
-		TFUNC,
-		TCHAN,
-		TMAP:
-		proggendata(g, 1)
-		*xoffset += t.Width
+	default:
+		Fatal("GCProg.emit: unexpected type %v", t)
 
 	case TSTRING:
-		proggendata(g, 1)
-		proggendata(g, 0)
-		*xoffset += t.Width
+		p.w.Ptr(offset / int64(Widthptr))
 
-		// Assuming IfacePointerOnly=1.
 	case TINTER:
-		proggendata(g, 1)
-		proggendata(g, 1)
-		*xoffset += t.Width
+		p.w.Ptr(offset / int64(Widthptr))
+		p.w.Ptr(offset/int64(Widthptr) + 1)
 
 	case TARRAY:
 		if Isslice(t) {
-			proggendata(g, 1)
-			proggendata(g, 0)
-			proggendata(g, 0)
-		} else {
-			t1 := t.Type
-			if t1.Width == 0 {
-			}
-			// ignore
-			if t.Bound <= 1 || t.Bound*t1.Width < int64(32*Widthptr) {
-				for i := int64(0); i < t.Bound; i++ {
-					gengcprog1(g, t1, xoffset)
-				}
-			} else if !haspointers(t1) {
-				n := t.Width
-				n -= -*xoffset & (int64(Widthptr) - 1) // skip to next ptr boundary
-				proggenarray(g, (n+int64(Widthptr)-1)/int64(Widthptr))
-				proggendata(g, 0)
-				proggenarrayend(g)
-				*xoffset -= (n+int64(Widthptr)-1)/int64(Widthptr)*int64(Widthptr) - t.Width
-			} else {
-				proggenarray(g, t.Bound)
-				gengcprog1(g, t1, xoffset)
-				*xoffset += (t.Bound - 1) * t1.Width
-				proggenarrayend(g)
-			}
+			p.w.Ptr(offset / int64(Widthptr))
+			return
 		}
+		if t.Bound == 0 {
+			// should have been handled by haspointers check above
+			Fatal("GCProg.emit: empty array")
+		}
+
+		// Flatten array-of-array-of-array to just a big array by multiplying counts.
+		count := t.Bound
+		elem := t.Type
+		for Isfixedarray(elem) {
+			count *= elem.Bound
+			elem = elem.Type
+		}
+
+		if !p.w.ShouldRepeat(elem.Width/int64(Widthptr), count) {
+			// Cheaper to just emit the bits.
+			for i := int64(0); i < count; i++ {
+				p.emit(elem, offset+i*elem.Width)
+			}
+			return
+		}
+		p.emit(elem, offset)
+		p.w.ZeroUntil((offset + elem.Width) / int64(Widthptr))
+		p.w.Repeat(elem.Width/int64(Widthptr), count-1)
 
 	case TSTRUCT:
-		o := int64(0)
-		var fieldoffset int64
 		for t1 := t.Type; t1 != nil; t1 = t1.Down {
-			fieldoffset = t1.Width
-			proggenskip(g, *xoffset, fieldoffset-o)
-			*xoffset += fieldoffset - o
-			gengcprog1(g, t1.Type, xoffset)
-			o = fieldoffset + t1.Type.Width
+			p.emit(t1.Type, offset+t1.Width)
 		}
-
-		proggenskip(g, *xoffset, t.Width-o)
-		*xoffset += t.Width - o
-
-	default:
-		Fatal("gengcprog1: unexpected type, %v", t)
 	}
 }
diff --git a/src/cmd/internal/gcprog/gcprog.go b/src/cmd/internal/gcprog/gcprog.go
new file mode 100644
index 0000000000..5845f7d65e
--- /dev/null
+++ b/src/cmd/internal/gcprog/gcprog.go
@@ -0,0 +1,298 @@
+// Copyright 2015 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package gcprog implements an encoder for packed GC pointer bitmaps,
+// known as GC programs.
+//
+// Program Format
+//
+// The GC program encodes a sequence of 0 and 1 bits indicating scalar or pointer words in an object.
+// The encoding is a simple Lempel-Ziv program, with codes to emit literal bits and to repeat the
+// last n bits c times.
+//
+// The possible codes are:
+//
+//	00000000: stop
+//	0nnnnnnn: emit n bits copied from the next (n+7)/8 bytes, least significant bit first
+//	10000000 n c: repeat the previous n bits c times; n, c are varints
+//	1nnnnnnn c: repeat the previous n bits c times; c is a varint
+//
+// The numbers n and c, when they follow a code, are encoded as varints
+// using the same encoding as encoding/binary's Uvarint.
+//
+package gcprog
+
+import (
+	"fmt"
+	"io"
+)
+
+const progMaxLiteral = 127 // maximum n for literal n bit code
+
+// A Writer is an encoder for GC programs.
+//
+// The typical use of a Writer is to call Init, maybe call Debug,
+// make a sequence of Ptr, Advance, Repeat, and Append calls
+// to describe the data type, and then finally call End.
+type Writer struct {
+	writeByte func(byte)
+	symoff    int
+	index     int64
+	b         [progMaxLiteral]byte
+	nb        int
+	debug     io.Writer
+	debugBuf  []byte
+}
+
+// Init initializes w to write a new GC program
+// by calling writeByte for each byte in the program.
+func (w *Writer) Init(writeByte func(byte)) {
+	w.writeByte = writeByte
+}
+
+// Debug causes the writer to print a debugging trace to out
+// during future calls to methods like Ptr, Advance, and End.
+// It also enables debugging checks during the encoding.
+func (w *Writer) Debug(out io.Writer) {
+	w.debug = out
+}
+
+// BitIndex returns the number of bits written to the bit stream so far.
+func (w *Writer) BitIndex() int64 {
+	return w.index
+}
+
+// byte writes the byte x to the output.
+func (w *Writer) byte(x byte) {
+	if w.debug != nil {
+		w.debugBuf = append(w.debugBuf, x)
+	}
+	w.writeByte(x)
+}
+
+// End marks the end of the program, writing any remaining bytes.
+func (w *Writer) End() {
+	w.flushlit()
+	w.byte(0)
+	if w.debug != nil {
+		index := progbits(w.debugBuf)
+		if index != w.index {
+			println("gcprog: End wrote program for", index, "bits, but current index is", w.index)
+			panic("gcprog: out of sync")
+		}
+	}
+}
+
+// Ptr emits a 1 into the bit stream at the given bit index.
+// that is, it records that the index'th word in the object memory is a pointer.
+// Any bits between the current index and the new index
+// are set to zero, meaning the corresponding words are scalars.
+func (w *Writer) Ptr(index int64) {
+	if index < w.index {
+		println("gcprog: Ptr at index", index, "but current index is", w.index)
+		panic("gcprog: invalid Ptr index")
+	}
+	w.ZeroUntil(index)
+	if w.debug != nil {
+		fmt.Fprintf(w.debug, "gcprog: ptr at %d\n", index)
+	}
+	w.lit(1)
+}
+
+// ShouldRepeat reports whether it would be worthwhile to
+// use a Repeat to describe c elements of n bits each,
+// compared to just emitting c copies of the n-bit description.
+func (w *Writer) ShouldRepeat(n, c int64) bool {
+	// Should we lay out the bits directly instead of
+	// encoding them as a repetition? Certainly if count==1,
+	// since there's nothing to repeat, but also if the total
+	// size of the plain pointer bits for the type will fit in
+	// 4 or fewer bytes, since using a repetition will require
+	// flushing the current bits plus at least one byte for
+	// the repeat size and one for the repeat count.
+	return c > 1 && c*n > 4*8
+}
+
+// Repeat emits an instruction to repeat the description
+// of the last n words c times (including the initial description, c+1 times in total).
+func (w *Writer) Repeat(n, c int64) {
+	if n == 0 || c == 0 {
+		return
+	}
+	w.flushlit()
+	if w.debug != nil {
+		fmt.Fprintf(w.debug, "gcprog: repeat %d × %d\n", n, c)
+	}
+	if n < 128 {
+		w.byte(0x80 | byte(n))
+	} else {
+		w.byte(0x80)
+		w.varint(n)
+	}
+	w.varint(c)
+	w.index += n * c
+}
+
+// ZeroUntil adds zeros to the bit stream until reaching the given index;
+// that is, it records that the words from the most recent pointer until
+// the index'th word are scalars.
+// ZeroUntil is usually called in preparation for a call to Repeat, Append, or End.
+func (w *Writer) ZeroUntil(index int64) {
+	if index < w.index {
+		println("gcprog: Advance", index, "but index is", w.index)
+		panic("gcprog: invalid Advance index")
+	}
+	skip := (index - w.index)
+	if skip == 0 {
+		return
+	}
+	if skip < 4*8 {
+		if w.debug != nil {
+			fmt.Fprintf(w.debug, "gcprog: advance to %d by literals\n", index)
+		}
+		for i := int64(0); i < skip; i++ {
+			w.lit(0)
+		}
+		return
+	}
+
+	if w.debug != nil {
+		fmt.Fprintf(w.debug, "gcprog: advance to %d by repeat\n", index)
+	}
+	w.lit(0)
+	w.flushlit()
+	w.Repeat(1, skip-1)
+}
+
+// Append emits the given GC program into the current output.
+// The caller asserts that the program emits n bits (describes n words),
+// and Append panics if that is not true.
+func (w *Writer) Append(prog []byte, n int64) {
+	w.flushlit()
+	if w.debug != nil {
+		fmt.Fprintf(w.debug, "gcprog: append prog for %d ptrs\n", n)
+		fmt.Fprintf(w.debug, "\t")
+	}
+	n1 := progbits(prog)
+	if n1 != n {
+		panic("gcprog: wrong bit count in append")
+	}
+	// The last byte of the prog terminates the program.
+	// Don't emit that, or else our own program will end.
+	for i, x := range prog[:len(prog)-1] {
+		if w.debug != nil {
+			if i > 0 {
+				fmt.Fprintf(w.debug, " ")
+			}
+			fmt.Fprintf(w.debug, "%02x", x)
+		}
+		w.byte(x)
+	}
+	if w.debug != nil {
+		fmt.Fprintf(w.debug, "\n")
+	}
+	w.index += n
+}
+
+// progbits returns the length of the bit stream encoded by the program p.
+func progbits(p []byte) int64 {
+	var n int64
+	for len(p) > 0 {
+		x := p[0]
+		p = p[1:]
+		if x == 0 {
+			break
+		}
+		if x&0x80 == 0 {
+			count := x &^ 0x80
+			n += int64(count)
+			p = p[(count+7)/8:]
+			continue
+		}
+		nbit := int64(x &^ 0x80)
+		if nbit == 0 {
+			nbit, p = readvarint(p)
+		}
+		var count int64
+		count, p = readvarint(p)
+		n += nbit * count
+	}
+	if len(p) > 0 {
+		println("gcprog: found end instruction after", n, "ptrs, with", len(p), "bytes remaining")
+		panic("gcprog: extra data at end of program")
+	}
+	return n
+}
+
+// readvarint reads a varint from p, returning the value and the remainder of p.
+func readvarint(p []byte) (int64, []byte) {
+	var v int64
+	var nb uint
+	for {
+		c := p[0]
+		p = p[1:]
+		v |= int64(c&^0x80) << nb
+		nb += 7
+		if c&0x80 == 0 {
+			break
+		}
+	}
+	return v, p
+}
+
+// lit adds a single literal bit to w.
+func (w *Writer) lit(x byte) {
+	if w.nb == progMaxLiteral {
+		w.flushlit()
+	}
+	w.b[w.nb] = x
+	w.nb++
+	w.index++
+}
+
+// varint emits the varint encoding of x.
+func (w *Writer) varint(x int64) {
+	if x < 0 {
+		panic("gcprog: negative varint")
+	}
+	for x >= 0x80 {
+		w.byte(byte(0x80 | x))
+		x >>= 7
+	}
+	w.byte(byte(x))
+}
+
+// flushlit flushes any pending literal bits.
+func (w *Writer) flushlit() {
+	if w.nb == 0 {
+		return
+	}
+	if w.debug != nil {
+		fmt.Fprintf(w.debug, "gcprog: flush %d literals\n", w.nb)
+		fmt.Fprintf(w.debug, "\t%v\n", w.b[:w.nb])
+		fmt.Fprintf(w.debug, "\t%02x", byte(w.nb))
+	}
+	w.byte(byte(w.nb))
+	var bits uint8
+	for i := 0; i < w.nb; i++ {
+		bits |= w.b[i] << uint(i%8)
+		if (i+1)%8 == 0 {
+			if w.debug != nil {
+				fmt.Fprintf(w.debug, " %02x", bits)
+			}
+			w.byte(bits)
+			bits = 0
+		}
+	}
+	if w.nb%8 != 0 {
+		if w.debug != nil {
+			fmt.Fprintf(w.debug, " %02x", bits)
+		}
+		w.byte(bits)
+	}
+	if w.debug != nil {
+		fmt.Fprintf(w.debug, "\n")
+	}
+	w.nb = 0
+}
diff --git a/src/cmd/internal/ld/data.go b/src/cmd/internal/ld/data.go
index b65b667f98..f9aacf0e19 100644
--- a/src/cmd/internal/ld/data.go
+++ b/src/cmd/internal/ld/data.go
@@ -32,9 +32,11 @@
 package ld
 
 import (
+	"cmd/internal/gcprog"
 	"cmd/internal/obj"
 	"fmt"
 	"log"
+	"os"
 	"strings"
 )
 
@@ -1044,141 +1046,65 @@ func maxalign(s *LSym, type_ int) int32 {
 	return max
 }
 
-// Helper object for building GC type programs.
-type ProgGen struct {
-	s        *LSym
-	datasize int32
-	data     [256 / 8]uint8
-	pos      int64
+const debugGCProg = false
+
+type GCProg struct {
+	sym *LSym
+	w   gcprog.Writer
 }
 
-func proggeninit(g *ProgGen, s *LSym) {
-	g.s = s
-	g.datasize = 0
-	g.pos = 0
-	g.data = [256 / 8]uint8{}
-}
-
-func proggenemit(g *ProgGen, v uint8) {
-	Adduint8(Ctxt, g.s, v)
-}
-
-// Writes insData block from g->data.
-func proggendataflush(g *ProgGen) {
-	if g.datasize == 0 {
-		return
-	}
-	proggenemit(g, obj.InsData)
-	proggenemit(g, uint8(g.datasize))
-	s := (g.datasize + 7) / 8
-	for i := int32(0); i < s; i++ {
-		proggenemit(g, g.data[i])
-	}
-	g.datasize = 0
-	g.data = [256 / 8]uint8{}
-}
-
-func proggendata(g *ProgGen, d uint8) {
-	g.data[g.datasize/8] |= d << uint(g.datasize%8)
-	g.datasize++
-	if g.datasize == 255 {
-		proggendataflush(g)
+func (p *GCProg) Init(name string) {
+	p.sym = Linklookup(Ctxt, name, 0)
+	p.w.Init(p.writeByte)
+	if debugGCProg {
+		fmt.Fprintf(os.Stderr, "ld: start GCProg %s\n", name)
+		p.w.Debug(os.Stderr)
 	}
 }
 
-// Skip v bytes due to alignment, etc.
-func proggenskip(g *ProgGen, off int64, v int64) {
-	for i := off; i < off+v; i++ {
-		if (i % int64(Thearch.Ptrsize)) == 0 {
-			proggendata(g, 0)
-		}
+func (p *GCProg) writeByte(x byte) {
+	Adduint8(Ctxt, p.sym, x)
+}
+
+func (p *GCProg) End(size int64) {
+	p.w.ZeroUntil(size / int64(Thearch.Ptrsize))
+	p.w.End()
+	if debugGCProg {
+		fmt.Fprintf(os.Stderr, "ld: end GCProg\n")
 	}
 }
 
-// Emit insArray instruction.
-func proggenarray(g *ProgGen, length int64) {
-	var i int32
-
-	proggendataflush(g)
-	proggenemit(g, obj.InsArray)
-	for i = 0; i < int32(Thearch.Ptrsize); i, length = i+1, length>>8 {
-		proggenemit(g, uint8(length))
-	}
-}
-
-func proggenarrayend(g *ProgGen) {
-	proggendataflush(g)
-	proggenemit(g, obj.InsArrayEnd)
-}
-
-func proggenfini(g *ProgGen, size int64) {
-	proggenskip(g, g.pos, size-g.pos)
-	proggendataflush(g)
-	proggenemit(g, obj.InsEnd)
-}
-
-// This function generates GC pointer info for global variables.
-func proggenaddsym(g *ProgGen, s *LSym) {
-	if s.Size == 0 {
-		return
-	}
-
-	// Skip alignment hole from the previous symbol.
-	proggenskip(g, g.pos, s.Value-g.pos)
-	g.pos = s.Value
-
-	if s.Gotype == nil && s.Size >= int64(Thearch.Ptrsize) {
+func (p *GCProg) AddSym(s *LSym) {
+	typ := s.Gotype
+	// Things without pointers should be in SNOPTRDATA or SNOPTRBSS;
+	// everything we see should have pointers and should therefore have a type.
+	if typ == nil {
 		Diag("missing Go type information for global symbol: %s size %d", s.Name, int(s.Size))
 		return
 	}
 
-	if s.Gotype == nil || decodetype_noptr(s.Gotype) != 0 || s.Size < int64(Thearch.Ptrsize) || s.Name[0] == '.' {
-		// no scan
-		if s.Size < int64(32*Thearch.Ptrsize) {
-			// Emit small symbols as data.
-			// This case also handles unaligned and tiny symbols, so tread carefully.
-			for i := s.Value; i < s.Value+s.Size; i++ {
-				if (i % int64(Thearch.Ptrsize)) == 0 {
-					proggendata(g, 0)
-				}
-			}
-		} else {
-			// Emit large symbols as array.
-			if (s.Size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) {
-				Diag("proggenaddsym: unaligned noscan symbol %s: size=%d pos=%d", s.Name, s.Size, g.pos)
-			}
-			proggenarray(g, s.Size/int64(Thearch.Ptrsize))
-			proggendata(g, 0)
-			proggenarrayend(g)
-		}
-		g.pos = s.Value + s.Size
-	} else if decodetype_usegcprog(s.Gotype) != 0 {
-		// gc program, copy directly
-		// TODO(rsc): Maybe someday the gc program will only describe
-		// the first decodetype_ptrdata(s.Gotype) bytes instead of the full size.
-		proggendataflush(g)
-		gcprog := decodetype_gcprog(s.Gotype)
-		size := decodetype_size(s.Gotype)
-		if (size%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) {
-			Diag("proggenaddsym: unaligned gcprog symbol %s: size=%d pos=%d", s.Name, size, g.pos)
-		}
-		for i := int64(0); i < int64(len(gcprog.P)-1); i++ {
-			proggenemit(g, uint8(gcprog.P[i]))
-		}
-		g.pos = s.Value + size
-	} else {
-		// gc mask, it's small so emit as data
-		mask := decodetype_gcmask(s.Gotype)
-		ptrdata := decodetype_ptrdata(s.Gotype)
-		if (ptrdata%int64(Thearch.Ptrsize) != 0) || (g.pos%int64(Thearch.Ptrsize) != 0) {
-			Diag("proggenaddsym: unaligned gcmask symbol %s: size=%d pos=%d", s.Name, ptrdata, g.pos)
-		}
-		for i := int64(0); i < ptrdata; i += int64(Thearch.Ptrsize) {
-			word := uint(i / int64(Thearch.Ptrsize))
-			proggendata(g, (mask[word/8]>>(word%8))&1)
-		}
-		g.pos = s.Value + ptrdata
+	ptrsize := int64(Thearch.Ptrsize)
+	nptr := decodetype_ptrdata(typ) / ptrsize
+
+	if debugGCProg {
+		fmt.Fprintf(os.Stderr, "gcprog sym: %s at %d (ptr=%d+%d)\n", s.Name, s.Value, s.Value/ptrsize, nptr)
 	}
+
+	if decodetype_usegcprog(typ) == 0 {
+		// Copy pointers from mask into program.
+		mask := decodetype_gcmask(typ)
+		for i := int64(0); i < nptr; i++ {
+			if (mask[i/8]>>uint(i%8))&1 != 0 {
+				p.w.Ptr(s.Value/ptrsize + i)
+			}
+		}
+		return
+	}
+
+	// Copy program.
+	prog := decodetype_gcprog(typ)
+	p.w.ZeroUntil(s.Value / ptrsize)
+	p.w.Append(prog.P[4:prog.Size], nptr)
 }
 
 func growdatsize(datsizep *int64, s *LSym) {
@@ -1386,15 +1312,13 @@ func dodata() {
 
 	/* data */
 	sect = addsection(&Segdata, ".data", 06)
-
 	sect.Align = maxalign(s, obj.SBSS-1)
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
 	Linklookup(Ctxt, "runtime.data", 0).Sect = sect
 	Linklookup(Ctxt, "runtime.edata", 0).Sect = sect
-	gcdata := Linklookup(Ctxt, "runtime.gcdata", 0)
-	var gen ProgGen
-	proggeninit(&gen, gcdata)
+	var gc GCProg
+	gc.Init("runtime.gcdata")
 	for ; s != nil && s.Type < obj.SBSS; s = s.Next {
 		if s.Type == obj.SINITARR {
 			Ctxt.Cursym = s
@@ -1405,33 +1329,30 @@ func dodata() {
 		s.Type = obj.SDATA
 		datsize = aligndatsize(datsize, s)
 		s.Value = int64(uint64(datsize) - sect.Vaddr)
-		proggenaddsym(&gen, s) // gc
+		gc.AddSym(s)
 		growdatsize(&datsize, s)
 	}
-
 	sect.Length = uint64(datsize) - sect.Vaddr
-	proggenfini(&gen, int64(sect.Length)) // gc
+	gc.End(int64(sect.Length))
 
 	/* bss */
 	sect = addsection(&Segdata, ".bss", 06)
-
 	sect.Align = maxalign(s, obj.SNOPTRBSS-1)
 	datsize = Rnd(datsize, int64(sect.Align))
 	sect.Vaddr = uint64(datsize)
 	Linklookup(Ctxt, "runtime.bss", 0).Sect = sect
 	Linklookup(Ctxt, "runtime.ebss", 0).Sect = sect
-	gcbss := Linklookup(Ctxt, "runtime.gcbss", 0)
-	proggeninit(&gen, gcbss)
+	gc = GCProg{}
+	gc.Init("runtime.gcbss")
 	for ; s != nil && s.Type < obj.SNOPTRBSS; s = s.Next {
 		s.Sect = sect
 		datsize = aligndatsize(datsize, s)
 		s.Value = int64(uint64(datsize) - sect.Vaddr)
-		proggenaddsym(&gen, s) // gc
+		gc.AddSym(s)
 		growdatsize(&datsize, s)
 	}
-
 	sect.Length = uint64(datsize) - sect.Vaddr
-	proggenfini(&gen, int64(sect.Length)) // gc
+	gc.End(int64(sect.Length))
 
 	/* pointer-free bss */
 	sect = addsection(&Segdata, ".noptrbss", 06)
diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/internal/ld/decodesym.go
index b9333857fd..fcc664dde7 100644
--- a/src/cmd/internal/ld/decodesym.go
+++ b/src/cmd/internal/ld/decodesym.go
@@ -44,7 +44,7 @@ func decode_inuxi(p []byte, sz int) uint64 {
 // commonsize returns the size of the common prefix for all type
 // structures (runtime._type).
 func commonsize() int {
-	return 9*Thearch.Ptrsize + 8
+	return 8*Thearch.Ptrsize + 8
 }
 
 // Type.commonType.kind
@@ -79,7 +79,7 @@ func decodetype_gcprog(s *LSym) *LSym {
 		x := "type..gcprog." + s.Name[5:]
 		return Linklookup(Ctxt, x, 0)
 	}
-	return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+2*int32(Thearch.Ptrsize))
+	return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize))
 }
 
 func decodetype_gcprog_shlib(s *LSym) uint64 {
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index a0d03ef22d..753e8eebd8 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -1200,7 +1200,7 @@ func ldshlibsyms(shlib string) {
 		if strings.HasPrefix(s.Name, "_") {
 			continue
 		}
-		if strings.HasPrefix(s.Name, "runtime.gcbits.0x") {
+		if strings.HasPrefix(s.Name, "runtime.gcbits.") {
 			gcmasks[s.Value] = readelfsymboldata(f, &s)
 		}
 		if s.Name == "go.link.abihashbytes" {
diff --git a/src/cmd/internal/ld/link.go b/src/cmd/internal/ld/link.go
index a314ca1370..3098147819 100644
--- a/src/cmd/internal/ld/link.go
+++ b/src/cmd/internal/ld/link.go
@@ -34,6 +34,7 @@ import (
 	"cmd/internal/obj"
 	"debug/elf"
 	"encoding/binary"
+	"fmt"
 )
 
 type LSym struct {
@@ -86,6 +87,13 @@ type LSym struct {
 	gcmask      []byte
 }
 
+func (s *LSym) String() string {
+	if s.Version == 0 {
+		return s.Name
+	}
+	return fmt.Sprintf("%s<%d>", s.Name, s.Version)
+}
+
 type Reloc struct {
 	Off     int32
 	Siz     uint8
diff --git a/src/cmd/internal/ld/objfile.go b/src/cmd/internal/ld/objfile.go
index 3d59323dba..613fcb2a40 100644
--- a/src/cmd/internal/ld/objfile.go
+++ b/src/cmd/internal/ld/objfile.go
@@ -347,7 +347,7 @@ func rdsym(ctxt *Link, f *obj.Biobuf, pkg string) *LSym {
 			s.Reachable = false
 		}
 	}
-	if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.0x") {
+	if v == 0 && strings.HasPrefix(s.Name, "runtime.gcbits.") {
 		s.Local = true
 	}
 	return s
diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 373583d471..9214577c2e 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4395,11 +4395,9 @@ type funcLayoutTest struct {
 var funcLayoutTests []funcLayoutTest
 
 func init() {
-	var argAlign = PtrSize
-	var naclExtra []byte
+	var argAlign uintptr = PtrSize
 	if runtime.GOARCH == "amd64p32" {
 		argAlign = 2 * PtrSize
-		naclExtra = append(naclExtra, 0)
 	}
 	roundup := func(x uintptr, a uintptr) uintptr {
 		return (x + a - 1) / a * a
@@ -4413,16 +4411,14 @@ func init() {
 			4 * PtrSize,
 			4 * PtrSize,
 			[]byte{1, 0, 1},
-			[]byte{1, 0, 1, 0, 1, 0},
+			[]byte{1, 0, 1, 0, 1},
 		})
 
-	var r, s []byte
+	var r []byte
 	if PtrSize == 4 {
 		r = []byte{0, 0, 0, 1}
-		s = append([]byte{0, 0, 0, 1, 0}, naclExtra...)
 	} else {
 		r = []byte{0, 0, 1}
-		s = []byte{0, 0, 1, 0}
 	}
 	funcLayoutTests = append(funcLayoutTests,
 		funcLayoutTest{
@@ -4432,7 +4428,7 @@ func init() {
 			roundup(3*4, PtrSize) + PtrSize + 2,
 			roundup(roundup(3*4, PtrSize)+PtrSize+2, argAlign),
 			r,
-			s,
+			r,
 		})
 
 	funcLayoutTests = append(funcLayoutTests,
@@ -4469,7 +4465,7 @@ func init() {
 			3 * PtrSize,
 			roundup(3*PtrSize, argAlign),
 			[]byte{1, 0, 1},
-			append([]byte{1, 0, 1}, naclExtra...),
+			[]byte{1, 0, 1},
 		})
 
 	funcLayoutTests = append(funcLayoutTests,
@@ -4480,7 +4476,7 @@ func init() {
 			PtrSize,
 			roundup(PtrSize, argAlign),
 			[]byte{},
-			append([]byte{0}, naclExtra...),
+			[]byte{},
 		})
 
 	funcLayoutTests = append(funcLayoutTests,
@@ -4491,7 +4487,7 @@ func init() {
 			0,
 			0,
 			[]byte{},
-			[]byte{0},
+			[]byte{},
 		})
 
 	funcLayoutTests = append(funcLayoutTests,
@@ -4502,7 +4498,7 @@ func init() {
 			2 * PtrSize,
 			2 * PtrSize,
 			[]byte{1},
-			[]byte{1, 0},
+			[]byte{1},
 			// Note: this one is tricky, as the receiver is not a pointer.  But we
 			// pass the receiver by reference to the autogenerated pointer-receiver
 			// version of the function.
@@ -4532,3 +4528,118 @@ func TestFuncLayout(t *testing.T) {
 		}
 	}
 }
+
+func verifyGCBits(t *testing.T, typ Type, bits []byte) {
+	heapBits := GCBits(New(typ).Interface())
+	if !bytes.Equal(heapBits, bits) {
+		t.Errorf("heapBits incorrect for %v\nhave %v\nwant %v", typ, heapBits, bits)
+	}
+}
+
+func TestGCBits(t *testing.T) {
+	verifyGCBits(t, TypeOf((*byte)(nil)), []byte{1})
+
+	// Building blocks for types seen by the compiler (like [2]Xscalar).
+	// The compiler will create the type structures for the derived types,
+	// including their GC metadata.
+	type Xscalar struct{ x uintptr }
+	type Xptr struct{ x *byte }
+	type Xptrscalar struct {
+		*byte
+		uintptr
+	}
+	type Xscalarptr struct {
+		uintptr
+		*byte
+	}
+
+	var Tscalar, Tptr, Tscalarptr, Tptrscalar Type
+	{
+		// Building blocks for types constructed by reflect.
+		// This code is in a separate block so that code below
+		// cannot accidentally refer to these.
+		// The compiler must NOT see types derived from these
+		// (for example, [2]Scalar must NOT appear in the program),
+		// or else reflect will use it instead of having to construct one.
+		// The goal is to test the construction.
+		type Scalar struct{ x uintptr }
+		type Ptr struct{ x *byte }
+		type Ptrscalar struct {
+			*byte
+			uintptr
+		}
+		type Scalarptr struct {
+			uintptr
+			*byte
+		}
+		Tscalar = TypeOf(Scalar{})
+		Tptr = TypeOf(Ptr{})
+		Tscalarptr = TypeOf(Scalarptr{})
+		Tptrscalar = TypeOf(Ptrscalar{})
+	}
+
+	empty := []byte{}
+
+	verifyGCBits(t, TypeOf(Xscalar{}), empty)
+	verifyGCBits(t, Tscalar, empty)
+	verifyGCBits(t, TypeOf(Xptr{}), lit(1))
+	verifyGCBits(t, Tptr, lit(1))
+	verifyGCBits(t, TypeOf(Xscalarptr{}), lit(0, 1))
+	verifyGCBits(t, Tscalarptr, lit(0, 1))
+	verifyGCBits(t, TypeOf(Xptrscalar{}), lit(1))
+	verifyGCBits(t, Tptrscalar, lit(1))
+
+	verifyGCBits(t, TypeOf([0]Xptr{}), empty)
+	verifyGCBits(t, ArrayOf(0, Tptr), empty)
+	verifyGCBits(t, TypeOf([1]Xptrscalar{}), lit(1))
+	verifyGCBits(t, ArrayOf(1, Tptrscalar), lit(1))
+	verifyGCBits(t, TypeOf([2]Xscalar{}), empty)
+	verifyGCBits(t, ArrayOf(2, Tscalar), empty)
+	verifyGCBits(t, TypeOf([100]Xscalar{}), empty)
+	verifyGCBits(t, ArrayOf(100, Tscalar), empty)
+	verifyGCBits(t, TypeOf([2]Xptr{}), lit(1, 1))
+	verifyGCBits(t, ArrayOf(2, Tptr), lit(1, 1))
+	verifyGCBits(t, TypeOf([100]Xptr{}), rep(100, lit(1)))
+	verifyGCBits(t, ArrayOf(100, Tptr), rep(100, lit(1)))
+	verifyGCBits(t, TypeOf([2]Xscalarptr{}), lit(0, 1, 0, 1))
+	verifyGCBits(t, ArrayOf(2, Tscalarptr), lit(0, 1, 0, 1))
+	verifyGCBits(t, TypeOf([100]Xscalarptr{}), rep(100, lit(0, 1)))
+	verifyGCBits(t, ArrayOf(100, Tscalarptr), rep(100, lit(0, 1)))
+	verifyGCBits(t, TypeOf([2]Xptrscalar{}), lit(1, 0, 1))
+	verifyGCBits(t, ArrayOf(2, Tptrscalar), lit(1, 0, 1))
+	verifyGCBits(t, TypeOf([100]Xptrscalar{}), rep(100, lit(1, 0)))
+	verifyGCBits(t, ArrayOf(100, Tptrscalar), rep(100, lit(1, 0)))
+	verifyGCBits(t, TypeOf([1][100]Xptrscalar{}), rep(100, lit(1, 0)))
+	verifyGCBits(t, ArrayOf(1, ArrayOf(100, Tptrscalar)), rep(100, lit(1, 0)))
+	verifyGCBits(t, TypeOf([2][100]Xptrscalar{}), rep(200, lit(1, 0)))
+	verifyGCBits(t, ArrayOf(2, ArrayOf(100, Tptrscalar)), rep(200, lit(1, 0)))
+
+	verifyGCBits(t, TypeOf((chan [100]Xscalar)(nil)), lit(1))
+	verifyGCBits(t, ChanOf(BothDir, ArrayOf(100, Tscalar)), lit(1))
+
+	verifyGCBits(t, TypeOf((func([100]Xscalarptr))(nil)), lit(1))
+	//verifyGCBits(t, FuncOf([]Type{ArrayOf(100, Tscalarptr)}, nil, false), lit(1))
+
+	verifyGCBits(t, TypeOf((map[[100]Xscalarptr]Xscalar)(nil)), lit(1))
+	verifyGCBits(t, MapOf(ArrayOf(100, Tscalarptr), Tscalar), lit(1))
+
+	verifyGCBits(t, TypeOf((*[100]Xscalar)(nil)), lit(1))
+	verifyGCBits(t, PtrTo(ArrayOf(100, Tscalar)), lit(1))
+
+	verifyGCBits(t, TypeOf(([][100]Xscalar)(nil)), lit(1))
+	verifyGCBits(t, SliceOf(ArrayOf(100, Tscalar)), lit(1))
+
+	hdr := make([]byte, 8/PtrSize)
+	verifyGCBits(t, MapBucketOf(Tscalar, Tptr), join(hdr, rep(8, lit(0)), rep(8, lit(1)), lit(1)))
+	verifyGCBits(t, MapBucketOf(Tscalarptr, Tptr), join(hdr, rep(8, lit(0, 1)), rep(8, lit(1)), lit(1)))
+	verifyGCBits(t, MapBucketOf(Tscalar, Tscalar), empty)
+	verifyGCBits(t, MapBucketOf(ArrayOf(2, Tscalarptr), ArrayOf(3, Tptrscalar)), join(hdr, rep(8*2, lit(0, 1)), rep(8*3, lit(1, 0)), lit(1)))
+	verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize, Tscalarptr), ArrayOf(64/PtrSize, Tptrscalar)), join(hdr, rep(8*64/PtrSize, lit(0, 1)), rep(8*64/PtrSize, lit(1, 0)), lit(1)))
+	verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize+1, Tscalarptr), ArrayOf(64/PtrSize, Tptrscalar)), join(hdr, rep(8, lit(1)), rep(8*64/PtrSize, lit(1, 0)), lit(1)))
+	verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize, Tscalarptr), ArrayOf(64/PtrSize+1, Tptrscalar)), join(hdr, rep(8*64/PtrSize, lit(0, 1)), rep(8, lit(1)), lit(1)))
+	verifyGCBits(t, MapBucketOf(ArrayOf(64/PtrSize+1, Tscalarptr), ArrayOf(64/PtrSize+1, Tptrscalar)), join(hdr, rep(8, lit(1)), rep(8, lit(1)), lit(1)))
+}
+
+func rep(n int, b []byte) []byte { return bytes.Repeat(b, n) }
+func join(b ...[]byte) []byte    { return bytes.Join(b, nil) }
+func lit(x ...byte) []byte       { return x }
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index 6748eba3d1..a4e2e7e28c 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -4,6 +4,8 @@
 
 package reflect
 
+import "unsafe"
+
 // MakeRO returns a copy of v with the read-only flag set.
 func MakeRO(v Value) Value {
 	v.flag |= flagRO
@@ -28,14 +30,14 @@ func FuncLayout(t Type, rcvr Type) (frametype Type, argSize, retOffset uintptr,
 		ft, argSize, retOffset, s, _ = funcLayout(t.(*rtype), nil)
 	}
 	frametype = ft
-	for i := uint32(0); i < s.n; i += 2 {
-		stack = append(stack, s.data[i/8]>>(i%8)&3)
+	for i := uint32(0); i < s.n; i++ {
+		stack = append(stack, s.data[i/8]>>(i%8)&1)
 	}
 	if ft.kind&kindGCProg != 0 {
 		panic("can't handle gc programs")
 	}
-	gcdata := (*[1000]byte)(ft.gc[0])
-	for i := uintptr(0); i < ft.size/ptrSize; i++ {
+	gcdata := (*[1000]byte)(unsafe.Pointer(ft.gcdata))
+	for i := uintptr(0); i < ft.ptrdata/ptrSize; i++ {
 		gc = append(gc, gcdata[i/8]>>(i%8)&1)
 	}
 	ptrs = ft.kind&kindNoPointers == 0
@@ -51,3 +53,11 @@ func TypeLinks() []string {
 	}
 	return r
 }
+
+var GCBits = gcbits
+
+func gcbits(interface{}) []byte // provided by runtime
+
+func MapBucketOf(x, y Type) Type {
+	return bucketOf(x.(*rtype), y.(*rtype))
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index bffe2595dd..f39ba52a42 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -247,17 +247,17 @@ const (
 type rtype struct {
 	size          uintptr
 	ptrdata       uintptr
-	hash          uint32            // hash of type; avoids computation in hash tables
-	_             uint8             // unused/padding
-	align         uint8             // alignment of variable with this type
-	fieldAlign    uint8             // alignment of struct field with this type
-	kind          uint8             // enumeration for C
-	alg           *typeAlg          // algorithm table
-	gc            [2]unsafe.Pointer // garbage collection data
-	string        *string           // string form; unnecessary but undeniably useful
-	*uncommonType                   // (relatively) uncommon fields
-	ptrToThis     *rtype            // type for pointer to this type, if used in binary or has methods
-	zero          unsafe.Pointer    // pointer to zero value
+	hash          uint32         // hash of type; avoids computation in hash tables
+	_             uint8          // unused/padding
+	align         uint8          // alignment of variable with this type
+	fieldAlign    uint8          // alignment of struct field with this type
+	kind          uint8          // enumeration for C
+	alg           *typeAlg       // algorithm table
+	gcdata        *byte          // garbage collection data
+	string        *string        // string form; unnecessary but undeniably useful
+	*uncommonType                // (relatively) uncommon fields
+	ptrToThis     *rtype         // type for pointer to this type, if used in binary or has methods
+	zero          unsafe.Pointer // pointer to zero value
 }
 
 // a copy of runtime.typeAlg
@@ -1670,111 +1670,14 @@ func isReflexive(t *rtype) bool {
 	}
 }
 
-// gcProg is a helper type for generatation of GC pointer info.
-type gcProg struct {
-	gc       []byte
-	size     uintptr // size of type in bytes
-	hasPtr   bool
-	lastZero uintptr // largest offset of a zero-byte field
-}
-
-func (gc *gcProg) append(v byte) {
-	gc.align(unsafe.Sizeof(uintptr(0)))
-	gc.appendWord(v)
-}
-
-// Appends t's type info to the current program.
-func (gc *gcProg) appendProg(t *rtype) {
-	gc.align(uintptr(t.align))
-	if !t.pointers() {
-		gc.size += t.size
-		if t.size == 0 {
-			gc.lastZero = gc.size
-		}
-		return
-	}
-	switch t.Kind() {
-	default:
-		panic("reflect: non-pointer type marked as having pointers")
-	case Ptr, UnsafePointer, Chan, Func, Map:
-		gc.appendWord(1)
-	case Slice:
-		gc.appendWord(1)
-		gc.appendWord(0)
-		gc.appendWord(0)
-	case String:
-		gc.appendWord(1)
-		gc.appendWord(0)
-	case Array:
-		c := t.Len()
-		e := t.Elem().common()
-		for i := 0; i < c; i++ {
-			gc.appendProg(e)
-		}
-	case Interface:
-		gc.appendWord(1)
-		gc.appendWord(1)
-	case Struct:
-		oldsize := gc.size
-		c := t.NumField()
-		for i := 0; i < c; i++ {
-			gc.appendProg(t.Field(i).Type.common())
-		}
-		if gc.size > oldsize+t.size {
-			panic("reflect: struct components are larger than the struct itself")
-		}
-		gc.size = oldsize + t.size
-	}
-}
-
-func (gc *gcProg) appendWord(v byte) {
-	ptrsize := unsafe.Sizeof(uintptr(0))
-	if gc.size%ptrsize != 0 {
-		panic("reflect: unaligned GC program")
-	}
-	nptr := gc.size / ptrsize
-	for uintptr(len(gc.gc)) <= nptr/8 {
-		gc.gc = append(gc.gc, 0)
-	}
-	gc.gc[nptr/8] |= v << (nptr % 8)
-	gc.size += ptrsize
-	if v == 1 {
-		gc.hasPtr = true
-	}
-}
-
-func (gc *gcProg) finalize() (unsafe.Pointer, bool) {
-	if gc.size == 0 {
-		return nil, false
-	}
-	if gc.lastZero == gc.size {
-		gc.size++
-	}
-	ptrsize := unsafe.Sizeof(uintptr(0))
-	gc.align(ptrsize)
-	nptr := gc.size / ptrsize
-	for uintptr(len(gc.gc)) <= nptr/8 {
-		gc.gc = append(gc.gc, 0)
-	}
-	return unsafe.Pointer(&gc.gc[0]), gc.hasPtr
-}
-
-func extractGCWord(gc []byte, i uintptr) byte {
-	return gc[i/8] >> (i % 8) & 1
-}
-
-func (gc *gcProg) align(a uintptr) {
-	gc.size = align(gc.size, a)
-}
-
 // Make sure these routines stay in sync with ../../runtime/hashmap.go!
 // These types exist only for GC, so we only fill out GC relevant info.
 // Currently, that's just size and the GC program.  We also fill in string
 // for possible debugging use.
 const (
-	bucketSize = 8
-	maxKeySize = 128
-	maxValSize = 128
+	bucketSize uintptr = 8
+	maxKeySize uintptr = 128
+	maxValSize uintptr = 128
 )
 
 func bucketOf(ktyp, etyp *rtype) *rtype {
@@ -1791,33 +1694,70 @@ func bucketOf(ktyp, etyp *rtype) *rtype {
 	if etyp.size > maxValSize {
 		etyp = PtrTo(etyp).(*rtype)
 	}
-	ptrsize := unsafe.Sizeof(uintptr(0))
 
-	var gc gcProg
-	// topbits
-	for i := 0; i < int(bucketSize*unsafe.Sizeof(uint8(0))/ptrsize); i++ {
-		gc.append(0)
+	// Prepare GC data if any.
+	// A bucket is at most bucketSize*(1+maxKeySize+maxValSize)+2*ptrSize bytes,
+	// or 2072 bytes, or 259 pointer-size words, or 33 bytes of pointer bitmap.
+	// Normally the enforced limit on pointer maps is 16 bytes,
+	// but larger ones are acceptable, 33 bytes isn't too too big,
+	// and it's easier to generate a pointer bitmap than a GC program.
+	// Note that since the key and value are known to be <= 128 bytes,
+	// they're guaranteed to have bitmaps instead of GC programs.
+	var gcdata *byte
+	var ptrdata uintptr
+	if kind != kindNoPointers {
+		nptr := (bucketSize*(1+ktyp.size+etyp.size) + ptrSize) / ptrSize
+		mask := make([]byte, (nptr+7)/8)
+		base := bucketSize / ptrSize
+
+		if ktyp.kind&kindNoPointers == 0 {
+			if ktyp.kind&kindGCProg != 0 {
+				panic("reflect: unexpected GC program in MapOf")
+			}
+			kmask := (*[16]byte)(unsafe.Pointer(ktyp.gcdata))
+			for i := uintptr(0); i < ktyp.size/ptrSize; i++ {
+				if (kmask[i/8]>>(i%8))&1 != 0 {
+					for j := uintptr(0); j < bucketSize; j++ {
+						word := base + j*ktyp.size/ptrSize + i
+						mask[word/8] |= 1 << (word % 8)
+					}
+				}
+			}
+		}
+		base += bucketSize * ktyp.size / ptrSize
+
+		if etyp.kind&kindNoPointers == 0 {
+			if etyp.kind&kindGCProg != 0 {
+				panic("reflect: unexpected GC program in MapOf")
+			}
+			emask := (*[16]byte)(unsafe.Pointer(etyp.gcdata))
+			for i := uintptr(0); i < etyp.size/ptrSize; i++ {
+				if (emask[i/8]>>(i%8))&1 != 0 {
+					for j := uintptr(0); j < bucketSize; j++ {
+						word := base + j*etyp.size/ptrSize + i
+						mask[word/8] |= 1 << (word % 8)
+					}
+				}
+			}
+		}
+		base += bucketSize * etyp.size / ptrSize
+
+		word := base
+		mask[word/8] |= 1 << (word % 8)
+		gcdata = &mask[0]
+		ptrdata = (word + 1) * ptrSize
 	}
-	// keys
-	for i := 0; i < bucketSize; i++ {
-		gc.appendProg(ktyp)
-	}
-	// values
-	for i := 0; i < bucketSize; i++ {
-		gc.appendProg(etyp)
-	}
-	// overflow
-	gc.append(1)
-	ptrdata := gc.size
+
+	size := bucketSize*(1+ktyp.size+etyp.size) + ptrSize
 	if runtime.GOARCH == "amd64p32" {
-		gc.append(0)
+		size += ptrSize
 	}
 
 	b := new(rtype)
-	b.size = gc.size
+	b.size = size
 	b.ptrdata = ptrdata
 	b.kind = kind
-	b.gc[0], _ = gc.finalize()
+	b.gcdata = gcdata
 	s := "bucket(" + *ktyp.string + "," + *etyp.string + ")"
 	b.string = &s
 	return b
@@ -1911,19 +1851,83 @@ func ArrayOf(count int, elem Type) Type {
 	array.len = uintptr(count)
 	array.slice = slice.(*rtype)
 
-	var gc gcProg
-	// TODO(sbinet): count could be possibly very large.
-	// use insArray directives from ../runtime/mbitmap.go.
-	for i := 0; i < count; i++ {
-		gc.appendProg(typ)
-	}
-
-	var hasPtr bool
-	array.gc[0], hasPtr = gc.finalize()
-	if !hasPtr {
+	array.kind &^= kindNoPointers
+	switch {
+	case typ.kind&kindNoPointers != 0 || array.size == 0:
+		// No pointers.
 		array.kind |= kindNoPointers
-	} else {
-		array.kind &^= kindNoPointers
+		array.gcdata = nil
+		array.ptrdata = 0
+
+	case count == 1:
+		// In memory, 1-element array looks just like the element.
+		array.kind |= typ.kind & kindGCProg
+		array.gcdata = typ.gcdata
+		array.ptrdata = typ.ptrdata
+
+	case typ.kind&kindGCProg == 0 && array.size <= 16*8*ptrSize:
+		// Element is small with pointer mask; array is still small.
+		// Create direct pointer mask by turning each 1 bit in elem
+		// into count 1 bits in larger mask.
+		mask := make([]byte, (array.ptrdata/ptrSize+7)/8)
+		elemMask := (*[1 << 30]byte)(unsafe.Pointer(typ.gcdata))[:]
+		elemWords := typ.size / ptrSize
+		for j := uintptr(0); j < typ.ptrdata/ptrSize; j++ {
+			if (elemMask[j/8]>>(j%8))&1 != 0 {
+				for i := uintptr(0); i < array.len; i++ {
+					k := i*elemWords + j
+					mask[k/8] |= 1 << (k % 8)
+				}
+			}
+		}
+		array.gcdata = &mask[0]
+
+	default:
+		// Create program that emits one element
+		// and then repeats to make the array.
+		prog := []byte{0, 0, 0, 0} // will be length of prog
+		elemGC := (*[1 << 30]byte)(unsafe.Pointer(typ.gcdata))[:]
+		elemPtrs := typ.ptrdata / ptrSize
+		if typ.kind&kindGCProg == 0 {
+			// Element is small with pointer mask; use as literal bits.
+			mask := elemGC
+			// Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
+			var n uintptr
+			for n = elemPtrs; n > 120; n -= 120 {
+				prog = append(prog, 120)
+				prog = append(prog, mask[:15]...)
+				mask = mask[15:]
+			}
+			prog = append(prog, byte(n))
+			prog = append(prog, mask[:(n+7)/8]...)
+		} else {
+			// Element has GC program; emit one element.
+			elemProg := elemGC[4 : 4+*(*uint32)(unsafe.Pointer(&elemGC[0]))-1]
+			prog = append(prog, elemProg...)
+		}
+		// Pad from ptrdata to size.
+		elemWords := typ.size / ptrSize
+		if elemPtrs < elemWords {
+			// Emit literal 0 bit, then repeat as needed.
+			prog = append(prog, 0x01, 0x00)
+			if elemPtrs+1 < elemWords {
+				prog = append(prog, 0x81)
+				prog = appendVarint(prog, elemWords-elemPtrs-1)
+			}
+		}
+		// Repeat count-1 times.
+		if elemWords < 0x80 {
+			prog = append(prog, byte(elemWords|0x80))
+		} else {
+			prog = append(prog, 0x80)
+			prog = appendVarint(prog, elemWords)
+		}
+		prog = appendVarint(prog, uintptr(count)-1)
+		prog = append(prog, 0)
+		*(*uint32)(unsafe.Pointer(&prog[0])) = uint32(len(prog) - 4)
+		array.kind |= kindGCProg
+		array.gcdata = &prog[0]
+		array.ptrdata = array.size // overestimate but ok; must match program
 	}
 
 	etyp := typ.common()
@@ -1967,6 +1971,14 @@ func ArrayOf(count int, elem Type) Type {
 	return cachePut(ckey, &array.rtype)
 }
 
+func appendVarint(x []byte, v uintptr) []byte {
+	for ; v >= 0x80; v >>= 7 {
+		x = append(x, byte(v|0x80))
+	}
+	x = append(x, byte(v))
+	return x
+}
+
 // toType converts from a *rtype to a Type that can be returned
 // to the client of package reflect. In gc, the only concern is that
 // a nil *rtype must be replaced by a nil Type, but in gccgo this
@@ -2003,7 +2015,7 @@ var layoutCache struct {
 // The returned type exists only for GC, so we only fill out GC relevant info.
 // Currently, that's just size and the GC program.  We also fill in
 // the name for possible debugging use.
-func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr, stack *bitVector, framePool *sync.Pool) {
+func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uintptr, stk *bitVector, framePool *sync.Pool) {
 	if t.Kind() != Func {
 		panic("reflect: funcLayout of non-func type")
 	}
@@ -2026,53 +2038,47 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
 	tt := (*funcType)(unsafe.Pointer(t))
 
 	// compute gc program & stack bitmap for arguments
-	stack = new(bitVector)
-	var gc gcProg
+	ptrmap := new(bitVector)
 	var offset uintptr
 	if rcvr != nil {
 		// Reflect uses the "interface" calling convention for
 		// methods, where receivers take one word of argument
 		// space no matter how big they actually are.
-		if ifaceIndir(rcvr) {
-			// we pass a pointer to the receiver.
-			gc.append(1)
-			stack.append2(1)
-		} else if rcvr.pointers() {
-			// rcvr is a one-word pointer object.  Its gc program
-			// is just what we need here.
-			gc.append(1)
-			stack.append2(1)
-		} else {
-			gc.append(0)
-			stack.append2(0)
+		if ifaceIndir(rcvr) || rcvr.pointers() {
+			ptrmap.append(1)
 		}
 		offset += ptrSize
 	}
 	for _, arg := range tt.in {
-		gc.appendProg(arg)
-		addTypeBits(stack, &offset, arg)
+		offset += -offset & uintptr(arg.align-1)
+		addTypeBits(ptrmap, offset, arg)
+		offset += arg.size
 	}
-	argSize = gc.size
+	argN := ptrmap.n
+	argSize = offset
 	if runtime.GOARCH == "amd64p32" {
-		gc.align(8)
+		offset += -offset & (8 - 1)
 	}
-	gc.align(ptrSize)
-	retOffset = gc.size
+	offset += -offset & (ptrSize - 1)
+	retOffset = offset
 	for _, res := range tt.out {
-		gc.appendProg(res)
-		// stack map does not need result bits
+		offset += -offset & uintptr(res.align-1)
+		addTypeBits(ptrmap, offset, res)
+		offset += res.size
 	}
-	gc.align(ptrSize)
+	offset += -offset & (ptrSize - 1)
 
 	// build dummy rtype holding gc program
 	x := new(rtype)
-	x.size = gc.size
-	x.ptrdata = gc.size // over-approximation
-	var hasPtr bool
-	x.gc[0], hasPtr = gc.finalize()
-	if !hasPtr {
+	x.size = offset
+	x.ptrdata = uintptr(ptrmap.n) * ptrSize
+	if ptrmap.n > 0 {
+		x.gcdata = &ptrmap.data[0]
+	} else {
 		x.kind |= kindNoPointers
 	}
+	ptrmap.n = argN
+
 	var s string
 	if rcvr != nil {
 		s = "methodargs(" + *rcvr.string + ")(" + *t.string + ")"
@@ -2092,11 +2098,11 @@ func funcLayout(t *rtype, rcvr *rtype) (frametype *rtype, argSize, retOffset uin
 		t:         x,
 		argSize:   argSize,
 		retOffset: retOffset,
-		stack:     stack,
+		stack:     ptrmap,
 		framePool: framePool,
 	}
 	layoutCache.Unlock()
-	return x, argSize, retOffset, stack, framePool
+	return x, argSize, retOffset, ptrmap, framePool
 }
 
 // ifaceIndir reports whether t is stored indirectly in an interface value.
@@ -2110,56 +2116,49 @@ type bitVector struct {
 	data []byte
 }
 
-// append a bit pair to the bitmap.
-func (bv *bitVector) append2(bits uint8) {
-	// assume bv.n is a multiple of 2, since append2 is the only operation.
+// append a bit to the bitmap.
+func (bv *bitVector) append(bit uint8) {
 	if bv.n%8 == 0 {
 		bv.data = append(bv.data, 0)
 	}
-	bv.data[bv.n/8] |= bits << (bv.n % 8)
-	bv.n += 2
+	bv.data[bv.n/8] |= bit << (bv.n % 8)
+	bv.n++
 }
 
-func addTypeBits(bv *bitVector, offset *uintptr, t *rtype) {
-	*offset = align(*offset, uintptr(t.align))
-	if !t.pointers() {
-		*offset += t.size
+func addTypeBits(bv *bitVector, offset uintptr, t *rtype) {
+	if t.kind&kindNoPointers != 0 {
 		return
 	}
 
 	switch Kind(t.kind & kindMask) {
 	case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
 		// 1 pointer at start of representation
-		for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
-			bv.append2(0)
+		for bv.n < uint32(offset/uintptr(ptrSize)) {
+			bv.append(0)
 		}
-		bv.append2(1)
+		bv.append(1)
 
 	case Interface:
 		// 2 pointers
-		for bv.n < 2*uint32(*offset/uintptr(ptrSize)) {
-			bv.append2(0)
+		for bv.n < uint32(offset/uintptr(ptrSize)) {
+			bv.append(0)
 		}
-		bv.append2(1)
-		bv.append2(1)
+		bv.append(1)
+		bv.append(1)
 
 	case Array:
 		// repeat inner type
 		tt := (*arrayType)(unsafe.Pointer(t))
 		for i := 0; i < int(tt.len); i++ {
-			addTypeBits(bv, offset, tt.elem)
+			addTypeBits(bv, offset+uintptr(i)*tt.elem.size, tt.elem)
 		}
 
 	case Struct:
 		// apply fields
 		tt := (*structType)(unsafe.Pointer(t))
-		start := *offset
 		for i := range tt.fields {
 			f := &tt.fields[i]
-			off := start + f.offset
-			addTypeBits(bv, &off, f.typ)
+			addTypeBits(bv, offset+f.offset, f.typ)
 		}
 	}
-
-	*offset += t.size
 }
diff --git a/src/reflect/value.go b/src/reflect/value.go
index fb9e85a8cf..91c38c9ffc 100644
--- a/src/reflect/value.go
+++ b/src/reflect/value.go
@@ -10,7 +10,7 @@ import (
 	"unsafe"
 )
 
-const ptrSize = unsafe.Sizeof((*byte)(nil))
+const ptrSize = 4 << (^uintptr(0) >> 63) // unsafe.Sizeof(uintptr(0)) but an ideal const
 const cannotSet = "cannot set value obtained from unexported struct field"
 
 // Value is the reflection interface to a Go value.
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 546c331614..39bb4217b3 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -547,8 +547,6 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) {
 	}
 }
 
-// TODO(rsc): Clean up the next two functions.
-
 // heapBitsSetType records that the new allocation [x, x+size)
 // holds in [x, x+dataSize) one or more values of type typ.
 // (The number of values is given by dataSize / typ.size.)
@@ -569,11 +567,7 @@ func heapBitsSweepSpan(base, size, n uintptr, f func(uintptr)) {
 // but if the start or end of x shares a bitmap byte with an adjacent
 // object, the GC marker is racing with updates to those object's mark bits.
 func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
-	const doubleCheck = false // slow but helpful; enable to test modifications to this function
-
-	// From here till marked label marking the object as allocated
-	// and storing type info in the GC bitmap.
-	h := heapBitsForAddr(x)
+	const doubleCheck = false // slow but helpful; enable to test modifications to this code
 
 	// dataSize is always size rounded up to the next malloc size class,
 	// except in the case of allocating a defer block, in which case
@@ -593,6 +587,7 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		// (non-pointers are aggregated into tinySize allocations),
 		// initSpan sets the pointer bits for us. Nothing to do here.
 		if doubleCheck {
+			h := heapBitsForAddr(x)
 			if !h.isPointer() {
 				throw("heapBitsSetType: pointer bit missing")
 			}
@@ -600,33 +595,8 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		return
 	}
 
-	ptrmask := (*uint8)(unsafe.Pointer(typ.gc[0])) // pointer to unrolled mask
-	if typ.kind&kindGCProg != 0 {
-		nptr := typ.ptrdata / ptrSize
-		masksize := (nptr + 7) / 8
-		masksize++ // unroll flag in the beginning
-		if masksize > maxGCMask && typ.gc[1] != 0 {
-			// write barriers have not been updated to deal with this case yet.
-			throw("maxGCMask too small for now")
-			// If the mask is too large, unroll the program directly
-			// into the GC bitmap. It's 7 times slower than copying
-			// from the pre-unrolled mask, but saves 1/16 of type size
-			// memory for the mask.
-			systemstack(func() {
-				unrollgcproginplace_m(unsafe.Pointer(x), typ, size, dataSize)
-			})
-			return
-		}
-		// Check whether the program is already unrolled
-		// by checking if the unroll flag byte is set
-		maskword := uintptr(atomicloadp(unsafe.Pointer(ptrmask)))
-		if *(*uint8)(unsafe.Pointer(&maskword)) == 0 {
-			systemstack(func() {
-				unrollgcprog_m(typ)
-			})
-		}
-		ptrmask = add1(ptrmask) // skip the unroll flag byte
-	}
+	h := heapBitsForAddr(x)
+	ptrmask := typ.gcdata // start of 1-bit pointer mask (or GC program, handled below)
 
 	// Heap bitmap bits for 2-word object are only 4 bits,
 	// so also shared with objects next to it; use atomic updates.
@@ -661,6 +631,12 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 			return
 		}
 		// Otherwise typ.size must be 2*ptrSize, and typ.kind&kindGCProg == 0.
+		if doubleCheck {
+			if typ.size != 2*ptrSize || typ.kind&kindGCProg != 0 {
+				print("runtime: heapBitsSetType size=", size, " but typ.size=", typ.size, " gcprog=", typ.kind&kindGCProg != 0, "\n")
+				throw("heapBitsSetType")
+			}
+		}
 		b := uint32(*ptrmask)
 		hb := b & 3
 		if gcphase == _GCoff {
@@ -678,16 +654,49 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 	// This is a lot of lines of code, but it compiles into relatively few
 	// machine instructions.
 
-	// Ptrmask buffer.
 	var (
+		// Ptrmask input.
 		p     *byte   // last ptrmask byte read
 		b     uintptr // ptrmask bits already loaded
 		nb    uintptr // number of bits in b at next read
 		endp  *byte   // final ptrmask byte to read (then repeat)
 		endnb uintptr // number of valid bits in *endp
 		pbits uintptr // alternate source of bits
+
+		// Heap bitmap output.
+		w     uintptr // words processed
+		nw    uintptr // number of words to process
+		hbitp *byte   // next heap bitmap byte to write
+		hb    uintptr // bits being prepared for *hbitp
 	)
 
+	hbitp = h.bitp
+
+	// Handle GC program. Delayed until this part of the code
+	// so that we can use the same double-checking mechanism
+	// as the 1-bit case. Nothing above could have encountered
+	// GC programs: the cases were all too small.
+	if typ.kind&kindGCProg != 0 {
+		heapBitsSetTypeGCProg(h, typ.ptrdata, typ.size, dataSize, size, addb(typ.gcdata, 4))
+		if doubleCheck {
+			// Double-check the heap bits written by GC program
+			// by running the GC program to create a 1-bit pointer mask
+			// and then jumping to the double-check code below.
+			// This doesn't catch bugs shared between the 1-bit and 4-bit
+			// GC program execution, but it does catch mistakes specific
+			// to just one of those and bugs in heapBitsSetTypeGCProg's
+			// implementation of arrays.
+			lock(&debugPtrmask.lock)
+			if debugPtrmask.data == nil {
+				debugPtrmask.data = (*byte)(persistentalloc(1<<20, 1, &memstats.other_sys))
+			}
+			ptrmask = debugPtrmask.data
+			runGCProg(addb(typ.gcdata, 4), nil, ptrmask, 1)
+			goto Phase4
+		}
+		return
+	}
+
 	// Note about sizes:
 	//
 	// typ.size is the number of words in the object,
@@ -780,8 +789,6 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		nb = 8
 	}
 
-	var w uintptr  // words processed
-	var nw uintptr // number of words to process
 	if typ.size == dataSize {
 		// Single entry: can stop once we reach the non-pointer data.
 		nw = typ.ptrdata / ptrSize
@@ -803,9 +810,6 @@ func heapBitsSetType(x, size, dataSize uintptr, typ *_type) {
 		nw = 2
 	}
 
-	hbitp := h.bitp // next heap bitmap byte to write
-	var hb uintptr  // bits being preapred for *h.bitp
-
 	// Phase 1: Special case for leading byte (shift==0) or half-byte (shift==4).
 	// The leading byte is special because it contains the bits for words 0 and 1,
 	// which do not have the marked bits set.
@@ -967,10 +971,11 @@ Phase3:
 		}
 	}
 
+Phase4:
 	// Phase 4: all done, but perhaps double check.
 	if doubleCheck {
 		end := heapBitsForAddr(x + size)
-		if hbitp != end.bitp || (w == nw+2) != (end.shift == 2) {
+		if typ.kind&kindGCProg == 0 && (hbitp != end.bitp || (w == nw+2) != (end.shift == 2)) {
 			println("ended at wrong bitmap byte for", *typ._string, "x", dataSize/typ.size)
 			print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n")
 			print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n")
@@ -986,15 +991,16 @@ Phase3:
 		nptr := typ.ptrdata / ptrSize
 		ndata := typ.size / ptrSize
 		count := dataSize / typ.size
-		for i := uintptr(0); i <= dataSize/ptrSize; i++ {
+		totalptr := ((count-1)*typ.size + typ.ptrdata) / ptrSize
+		for i := uintptr(0); i < size/ptrSize; i++ {
 			j := i % ndata
 			var have, want uint8
-			if i == dataSize/ptrSize && dataSize >= size {
-				break
-			}
 			have = (*h.bitp >> h.shift) & (bitPointer | bitMarked)
-			if i == dataSize/ptrSize || i/ndata == count-1 && j >= nptr {
-				want = 0 // dead marker
+			if i >= totalptr {
+				want = 0 // deadmarker
+				if typ.kind&kindGCProg != 0 && i < (totalptr+3)/4*4 {
+					want = bitMarked
+				}
 			} else {
 				if j < nptr && (*addb(ptrmask, j/8)>>(j%8))&1 != 0 {
 					want |= bitPointer
@@ -1008,177 +1014,483 @@ Phase3:
 			if have != want {
 				println("mismatch writing bits for", *typ._string, "x", dataSize/typ.size)
 				print("typ.size=", typ.size, " typ.ptrdata=", typ.ptrdata, " dataSize=", dataSize, " size=", size, "\n")
+				print("kindGCProg=", typ.kind&kindGCProg != 0, "\n")
 				print("w=", w, " nw=", nw, " b=", hex(b), " nb=", nb, " hb=", hex(hb), "\n")
 				h0 := heapBitsForAddr(x)
 				print("initial bits h0.bitp=", h0.bitp, " h0.shift=", h0.shift, "\n")
 				print("current bits h.bitp=", h.bitp, " h.shift=", h.shift, " *h.bitp=", hex(*h.bitp), "\n")
 				print("ptrmask=", ptrmask, " p=", p, " endp=", endp, " endnb=", endnb, " pbits=", hex(pbits), " b=", hex(b), " nb=", nb, "\n")
 				println("at word", i, "offset", i*ptrSize, "have", have, "want", want)
+				if typ.kind&kindGCProg != 0 {
+					println("GC program:")
+					dumpGCProg(addb(typ.gcdata, 4))
+				}
 				throw("bad heapBitsSetType")
 			}
 			h = h.next()
 		}
+		if ptrmask == debugPtrmask.data {
+			unlock(&debugPtrmask.lock)
+		}
 	}
 }
 
-// GC type info programs
+var debugPtrmask struct {
+	lock mutex
+	data *byte
+}
+
+// heapBitsSetTypeGCProg implements heapBitsSetType using a GC program.
+// progSize is the size of the memory described by the program.
+// elemSize is the size of the element that the GC program describes (a prefix of).
+// dataSize is the total size of the intended data, a multiple of elemSize.
+// allocSize is the total size of the allocated memory.
 //
-// TODO(rsc): Clean up and enable.
+// GC programs are only used for large allocations.
+// heapBitsSetType requires that allocSize is a multiple of 4 words,
+// so that the relevant bitmap bytes are not shared with surrounding
+// objects and need not be accessed with atomic instructions.
+func heapBitsSetTypeGCProg(h heapBits, progSize, elemSize, dataSize, allocSize uintptr, prog *byte) {
+	if ptrSize == 8 && allocSize%(4*ptrSize) != 0 {
+		// Alignment will be wrong.
+		throw("heapBitsSetTypeGCProg: small allocation")
+	}
+	var totalBits uintptr
+	if elemSize == dataSize {
+		totalBits = runGCProg(prog, nil, h.bitp, 2)
+		if totalBits*ptrSize != progSize {
+			println("runtime: heapBitsSetTypeGCProg: total bits", totalBits, "but progSize", progSize)
+			throw("heapBitsSetTypeGCProg: unexpected bit count")
+		}
+	} else {
+		count := dataSize / elemSize
 
-const (
-	// GC type info programs.
-	// The programs allow to store type info required for GC in a compact form.
-	// Most importantly arrays take O(1) space instead of O(n).
-	// The program grammar is:
-	//
-	// Program = {Block} "insEnd"
-	// Block = Data | Array
-	// Data = "insData" DataSize DataBlock
-	// DataSize = int // size of the DataBlock in bit pairs, 1 byte
-	// DataBlock = binary // dense GC mask (2 bits per word) of size ]DataSize/4[ bytes
-	// Array = "insArray" ArrayLen Block "insArrayEnd"
-	// ArrayLen = int // length of the array, 8 bytes (4 bytes for 32-bit arch)
-	//
-	// Each instruction (insData, insArray, etc) is 1 byte.
-	// For example, for type struct { x []byte; y [20]struct{ z int; w *byte }; }
-	// the program looks as:
-	//
-	// insData 3 (typePointer typeScalar typeScalar)
-	//	insArray 20 insData 2 (typeScalar typePointer) insArrayEnd insEnd
-	//
-	// Total size of the program is 17 bytes (13 bytes on 32-bits).
-	// The corresponding GC mask would take 43 bytes (it would be repeated
-	// because the type has odd number of words).
-	insData = 1 + iota
-	insArray
-	insArrayEnd
-	insEnd
+		// Piece together program trailer to run after prog that does:
+		//	literal(0)
+		//	repeat(1, elemSize-progSize-1) // zeros to fill element size
+		//	repeat(elemSize, count-1) // repeat that element for count
+		// This zero-pads the data remaining in the first element and then
+		// repeats that first element to fill the array.
+		var trailer [40]byte // 3 varints (max 10 each) + some bytes
+		i := 0
+		if n := elemSize/ptrSize - progSize/ptrSize; n > 0 {
+			// literal(0)
+			trailer[i] = 0x01
+			i++
+			trailer[i] = 0
+			i++
+			if n > 1 {
+				// repeat(1, n-1)
+				trailer[i] = 0x81
+				i++
+				n--
+				for ; n >= 0x80; n >>= 7 {
+					trailer[i] = byte(n | 0x80)
+					i++
+				}
+				trailer[i] = byte(n)
+				i++
+			}
+		}
+		// repeat(elemSize/ptrSize, count-1)
+		trailer[i] = 0x80
+		i++
+		n := elemSize / ptrSize
+		for ; n >= 0x80; n >>= 7 {
+			trailer[i] = byte(n | 0x80)
+			i++
+		}
+		trailer[i] = byte(n)
+		i++
+		n = count
+		for ; n >= 0x80; n >>= 7 {
+			trailer[i] = byte(n | 0x80)
+			i++
+		}
+		trailer[i] = byte(n)
+		i++
+		trailer[i] = 0
+		i++
 
-	// 64 bytes cover objects of size 1024/512 on 64/32 bits, respectively.
-	maxGCMask = 65536 // TODO(rsc): change back to 64
-)
+		runGCProg(prog, &trailer[0], h.bitp, 2)
 
-// Recursively unrolls GC program in prog.
-// mask is where to store the result.
-// If inplace is true, store the result not in mask but in the heap bitmap for mask.
-// ppos is a pointer to position in mask, in bits.
-// sparse says to generate 4-bits per word mask for heap (1-bit for data/bss otherwise).
-//go:nowritebarrier
-func unrollgcprog1(maskp *byte, prog *byte, ppos *uintptr, inplace bool) *byte {
-	pos := *ppos
-	mask := (*[1 << 30]byte)(unsafe.Pointer(maskp))
+		// Even though we filled in the full array just now,
+		// record that we only filled in up to the ptrdata of the
+		// last element. This will cause the code below to
+		// memclr the dead section of the final array element,
+		// so that scanobject can stop early in the final element.
+		totalBits = (elemSize*(count-1) + progSize) / ptrSize
+	}
+	endProg := unsafe.Pointer(subtractb(h.bitp, (totalBits+3)/4))
+	endAlloc := unsafe.Pointer(subtractb(h.bitp, allocSize/heapBitmapScale))
+	memclr(add(endAlloc, 1), uintptr(endProg)-uintptr(endAlloc))
+}
+
+// progToPointerMask returns the 1-bit pointer mask output by the GC program prog.
+// size the size of the region described by prog, in bytes.
+// The resulting bitvector will have no more than size/ptrSize bits.
+func progToPointerMask(prog *byte, size uintptr) bitvector {
+	n := (size/ptrSize + 7) / 8
+	x := (*[1 << 30]byte)(persistentalloc(n+1, 1, &memstats.buckhash_sys))[:n+1]
+	x[len(x)-1] = 0xa1 // overflow check sentinel
+	n = runGCProg(prog, nil, &x[0], 1)
+	if x[len(x)-1] != 0xa1 {
+		throw("progToPointerMask: overflow")
+	}
+	return bitvector{int32(n), &x[0]}
+}
+
+// Packed GC pointer bitmaps, aka GC programs.
+//
+// For large types containing arrays, the type information has a
+// natural repetition that can be encoded to save space in the
+// binary and in the memory representation of the type information.
+//
+// The encoding is a simple Lempel-Ziv style bytecode machine
+// with the following instructions:
+//
+//	00000000: stop
+//	0nnnnnnn: emit n bits copied from the next (n+7)/8 bytes
+//	10000000 n c: repeat the previous n bits c times; n, c are varints
+//	1nnnnnnn c: repeat the previous n bits c times; c is a varint
+
+// runGCProg executes the GC program prog, and then trailer if non-nil,
+// writing to dst with entries of the given size.
+// If size == 1, dst is a 1-bit pointer mask laid out moving forward from dst.
+// If size == 2, dst is the 2-bit heap bitmap, and writes move backward
+// starting at dst (because the heap bitmap does). In this case, the caller guarantees
+// that only whole bytes in dst need to be written.
+//
+// runGCProg returns the number of 1- or 2-bit entries written to memory.
+func runGCProg(prog, trailer, dst *byte, size int) uintptr {
+	dstStart := dst
+
+	// Bits waiting to be written to memory.
+	var bits uintptr
+	var nbits uintptr
+
+	p := prog
+Run:
 	for {
-		switch *prog {
-		default:
-			throw("unrollgcprog: unknown instruction")
+		// Flush accumulated full bytes.
+		// The rest of the loop assumes that nbits <= 7.
+		for ; nbits >= 8; nbits -= 8 {
+			if size == 1 {
+				*dst = uint8(bits)
+				dst = add1(dst)
+				bits >>= 8
+			} else {
+				v := bits&bitPointerAll | bitMarkedAll
+				*dst = uint8(v)
+				dst = subtract1(dst)
+				bits >>= 4
+				v = bits&bitPointerAll | bitMarkedAll
+				*dst = uint8(v)
+				dst = subtract1(dst)
+				bits >>= 4
+			}
+		}
 
-		case insData:
-			prog = add1(prog)
-			siz := int(*prog)
-			prog = add1(prog)
-			p := (*[1 << 30]byte)(unsafe.Pointer(prog))
-			for i := 0; i < siz; i++ {
-				v := p[i/8] >> (uint(i) % 8) & 1
-				if inplace {
-					throw("gc inplace")
-					const typeShift = 2
-					// Store directly into GC bitmap.
-					h := heapBitsForAddr(uintptr(unsafe.Pointer(&mask[pos])))
-					if h.shift == 0 {
-						*h.bitp = v << typeShift
-					} else {
-						*h.bitp |= v << (4 + typeShift)
-					}
-					pos += ptrSize
+		// Process one instruction.
+		inst := uintptr(*p)
+		p = add1(p)
+		n := inst & 0x7F
+		if inst&0x80 == 0 {
+			// Literal bits; n == 0 means end of program.
+			if n == 0 {
+				// Program is over; continue in trailer if present.
+				if trailer != nil {
+					//println("trailer")
+					p = trailer
+					trailer = nil
+					continue
+				}
+				//println("done")
+				break Run
+			}
+			//println("lit", n, dst)
+			nbyte := n / 8
+			for i := uintptr(0); i < nbyte; i++ {
+				bits |= uintptr(*p) << nbits
+				p = add1(p)
+				if size == 1 {
+					*dst = uint8(bits)
+					dst = add1(dst)
+					bits >>= 8
 				} else {
-					// 1 bit per word, for data/bss bitmap
-					mask[pos/8] |= v << (pos % 8)
-					pos++
+					v := bits&0xf | bitMarkedAll
+					*dst = uint8(v)
+					dst = subtract1(dst)
+					bits >>= 4
+					v = bits&0xf | bitMarkedAll
+					*dst = uint8(v)
+					dst = subtract1(dst)
+					bits >>= 4
 				}
 			}
-			prog = addb(prog, (uintptr(siz)+7)/8)
+			if n %= 8; n > 0 {
+				bits |= uintptr(*p) << nbits
+				p = add1(p)
+				nbits += n
+			}
+			continue Run
+		}
 
-		case insArray:
-			prog = (*byte)(add(unsafe.Pointer(prog), 1))
-			siz := uintptr(0)
-			for i := uintptr(0); i < ptrSize; i++ {
-				siz = (siz << 8) + uintptr(*(*byte)(add(unsafe.Pointer(prog), ptrSize-i-1)))
+		// Repeat. If n == 0, it is encoded in a varint in the next bytes.
+		if n == 0 {
+			for off := uint(0); ; off += 7 {
+				x := uintptr(*p)
+				p = add1(p)
+				n |= (x & 0x7F) << off
+				if x&0x80 == 0 {
+					break
+				}
 			}
-			prog = (*byte)(add(unsafe.Pointer(prog), ptrSize))
-			var prog1 *byte
-			for i := uintptr(0); i < siz; i++ {
-				prog1 = unrollgcprog1(&mask[0], prog, &pos, inplace)
-			}
-			if *prog1 != insArrayEnd {
-				throw("unrollgcprog: array does not end with insArrayEnd")
-			}
-			prog = (*byte)(add(unsafe.Pointer(prog1), 1))
+		}
 
-		case insArrayEnd, insEnd:
-			*ppos = pos
-			return prog
+		// Count is encoded in a varint in the next bytes.
+		c := uintptr(0)
+		for off := uint(0); ; off += 7 {
+			x := uintptr(*p)
+			p = add1(p)
+			c |= (x & 0x7F) << off
+			if x&0x80 == 0 {
+				break
+			}
+		}
+		c *= n // now total number of bits to copy
+
+		// If the number of bits being repeated is small, load them
+		// into a register and use that register for the entire loop
+		// instead of repeatedly reading from memory.
+		// Handling fewer than 8 bits here makes the general loop simpler.
+		// The cutoff is ptrSize*8 - 7 to guarantee that when we add
+		// the pattern to a bit buffer holding at most 7 bits (a partial byte)
+		// it will not overflow.
+		src := dst
+		const maxBits = ptrSize*8 - 7
+		if n <= maxBits {
+			// Start with bits in output buffer.
+			pattern := bits
+			npattern := nbits
+
+			// If we need more bits, fetch them from memory.
+			if size == 1 {
+				src = subtract1(src)
+				for npattern < n {
+					pattern <<= 8
+					pattern |= uintptr(*src)
+					src = subtract1(src)
+					npattern += 8
+				}
+			} else {
+				src = add1(src)
+				for npattern < n {
+					pattern <<= 4
+					pattern |= uintptr(*src) & 0xf
+					src = add1(src)
+					npattern += 4
+				}
+			}
+
+			// We started with the whole bit output buffer,
+			// and then we loaded bits from whole bytes.
+			// Either way, we might now have too many instead of too few.
+			// Discard the extra.
+			if npattern > n {
+				pattern >>= npattern - n
+				npattern = n
+			}
+
+			// Replicate pattern to at most maxBits.
+			if npattern == 1 {
+				// One bit being repeated.
+				// If the bit is 1, make the pattern all 1s.
+				// If the bit is 0, the pattern is already all 0s,
+				// but we can claim that the number of bits
+				// in the word is equal to the number we need (c),
+				// because right shift of bits will zero fill.
+				if pattern == 1 {
+					pattern = 1<8 bits, there will be full bytes to flush
+			// on each iteration.
+			for ; c >= npattern; c -= npattern {
+				bits |= pattern << nbits
+				nbits += npattern
+				if size == 1 {
+					for nbits >= 8 {
+						*dst = uint8(bits)
+						dst = add1(dst)
+						bits >>= 8
+						nbits -= 8
+					}
+				} else {
+					for nbits >= 4 {
+						*dst = uint8(bits&0xf | bitMarkedAll)
+						dst = subtract1(dst)
+						bits >>= 4
+						nbits -= 4
+					}
+				}
+			}
+
+			// Add final fragment to bit buffer.
+			if c > 0 {
+				pattern &= 1< nbits because n > maxBits and nbits <= 7
+		if size == 1 {
+			// Leading src fragment.
+			src = subtractb(src, (off+7)/8)
+			if frag := off & 7; frag != 0 {
+				bits |= uintptr(*src) >> (8 - frag) << nbits
+				src = add1(src)
+				nbits += frag
+				c -= frag
+			}
+			// Main loop: load one byte, write another.
+			// The bits are rotating through the bit buffer.
+			for i := c / 8; i > 0; i-- {
+				bits |= uintptr(*src) << nbits
+				src = add1(src)
+				*dst = uint8(bits)
+				dst = add1(dst)
+				bits >>= 8
+			}
+			// Final src fragment.
+			if c %= 8; c > 0 {
+				bits |= (uintptr(*src) & (1<> (4 - frag) << nbits
+				src = subtract1(src)
+				nbits += frag
+				c -= frag
+			}
+			// Main loop: load one byte, write another.
+			// The bits are rotating through the bit buffer.
+			for i := c / 4; i > 0; i-- {
+				bits |= (uintptr(*src) & 0xf) << nbits
+				src = subtract1(src)
+				*dst = uint8(bits&0xf | bitMarkedAll)
+				dst = subtract1(dst)
+				bits >>= 4
+			}
+			// Final src fragment.
+			if c %= 4; c > 0 {
+				bits |= (uintptr(*src) & (1< 0; nbits -= 8 {
+			*dst = uint8(bits)
+			dst = add1(dst)
+			bits >>= 8
+		}
+	} else {
+		totalBits = (uintptr(unsafe.Pointer(dstStart))-uintptr(unsafe.Pointer(dst)))*4 + nbits
+		nbits += -nbits & 3
+		for ; nbits > 0; nbits -= 4 {
+			v := bits&0xf | bitMarkedAll
+			*dst = uint8(v)
+			dst = subtract1(dst)
+			bits >>= 4
+		}
+		// Clear the mark bits in the first two entries.
+		// They are the actual mark and checkmark bits,
+		// not non-dead markers. It simplified the code
+		// above to set the marker in every bit written and
+		// then clear these two as a special case at the end.
+		*dstStart &^= bitMarked | bitMarked< nptr && ret[len(ret)-1] == 0 {
+		ret = ret[:len(ret)-1]
+	}
+	return ret
+}
+
 // Returns GC type info for object p for testing.
 func getgcmask(ep interface{}) (mask []byte) {
 	e := *(*eface)(unsafe.Pointer(&ep))
@@ -1243,36 +1568,43 @@ func getgcmask(ep interface{}) (mask []byte) {
 	}
 
 	// stack
-	var frame stkframe
-	frame.sp = uintptr(p)
-	_g_ := getg()
-	gentraceback(_g_.m.curg.sched.pc, _g_.m.curg.sched.sp, 0, _g_.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0)
-	if frame.fn != nil {
-		f := frame.fn
-		targetpc := frame.continpc
-		if targetpc == 0 {
-			return
-		}
-		if targetpc != f.entry {
-			targetpc--
-		}
-		pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, targetpc)
-		if pcdata == -1 {
-			return
-		}
-		stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
-		if stkmap == nil || stkmap.n <= 0 {
-			return
-		}
-		bv := stackmapdata(stkmap, pcdata)
-		size := uintptr(bv.n) * ptrSize
-		n := (*ptrtype)(unsafe.Pointer(t)).elem.size
-		mask = make([]byte, n/ptrSize)
-		for i := uintptr(0); i < n; i += ptrSize {
-			bitmap := bv.bytedata
-			off := (uintptr(p) + i - frame.varp + size) / ptrSize
-			mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1
+	if _g_ := getg(); _g_.m.curg.stack.lo <= uintptr(p) && uintptr(p) < _g_.m.curg.stack.hi {
+		var frame stkframe
+		frame.sp = uintptr(p)
+		_g_ := getg()
+		gentraceback(_g_.m.curg.sched.pc, _g_.m.curg.sched.sp, 0, _g_.m.curg, 0, nil, 1000, getgcmaskcb, noescape(unsafe.Pointer(&frame)), 0)
+		if frame.fn != nil {
+			f := frame.fn
+			targetpc := frame.continpc
+			if targetpc == 0 {
+				return
+			}
+			if targetpc != f.entry {
+				targetpc--
+			}
+			pcdata := pcdatavalue(f, _PCDATA_StackMapIndex, targetpc)
+			if pcdata == -1 {
+				return
+			}
+			stkmap := (*stackmap)(funcdata(f, _FUNCDATA_LocalsPointerMaps))
+			if stkmap == nil || stkmap.n <= 0 {
+				return
+			}
+			bv := stackmapdata(stkmap, pcdata)
+			size := uintptr(bv.n) * ptrSize
+			n := (*ptrtype)(unsafe.Pointer(t)).elem.size
+			mask = make([]byte, n/ptrSize)
+			for i := uintptr(0); i < n; i += ptrSize {
+				bitmap := bv.bytedata
+				off := (uintptr(p) + i - frame.varp + size) / ptrSize
+				mask[i/ptrSize] = (*addb(bitmap, off/8) >> (off % 8)) & 1
+			}
 		}
+		return
 	}
+
+	// otherwise, not something the GC knows about.
+	// possibly read-only data, like malloc(0).
+	// must not have pointers
 	return
 }
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 11e885f928..d33fbf11de 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -146,8 +146,8 @@ func gcinit() {
 	work.markfor = parforalloc(_MaxGcproc)
 	_ = setGCPercent(readgogc())
 	for datap := &firstmoduledata; datap != nil; datap = datap.next {
-		datap.gcdatamask = unrollglobgcprog((*byte)(unsafe.Pointer(datap.gcdata)), datap.edata-datap.data)
-		datap.gcbssmask = unrollglobgcprog((*byte)(unsafe.Pointer(datap.gcbss)), datap.ebss-datap.bss)
+		datap.gcdatamask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcdata)), datap.edata-datap.data)
+		datap.gcbssmask = progToPointerMask((*byte)(unsafe.Pointer(datap.gcbss)), datap.ebss-datap.bss)
 	}
 	memstats.next_gc = heapminimum
 }
diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go
index 9afa954259..687f067cb9 100644
--- a/src/runtime/symtab.go
+++ b/src/runtime/symtab.go
@@ -32,6 +32,8 @@ const (
 // moduledata records information about the layout of the executable
 // image. It is written by the linker. Any changes here must be
 // matched changes to the code in cmd/internal/ld/symtab.go:symtab.
+// moduledata is stored in read-only memory; none of the pointers here
+// are visible to the garbage collector.
 type moduledata struct {
 	pclntable    []byte
 	ftab         []functab
diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 0f29608aae..12b2a53603 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -469,7 +469,7 @@ func setArgInfo(frame *stkframe, f *_func, needArgMap bool) {
 				throw("reflect mismatch")
 			}
 			bv := (*bitvector)(unsafe.Pointer(fn[1]))
-			frame.arglen = uintptr(bv.n / 2 * ptrSize)
+			frame.arglen = uintptr(bv.n * ptrSize)
 			frame.argmap = bv
 		}
 	}
diff --git a/src/runtime/type.go b/src/runtime/type.go
index 48df2a4382..45bdac8b91 100644
--- a/src/runtime/type.go
+++ b/src/runtime/type.go
@@ -20,17 +20,10 @@ type _type struct {
 	fieldalign uint8
 	kind       uint8
 	alg        *typeAlg
-	// gc stores type info required for garbage collector.
-	// If (kind&KindGCProg)==0, then gc[0] points at sparse GC bitmap
-	// (no indirection), 4 bits per word.
-	// If (kind&KindGCProg)!=0, then gc[1] points to a compiler-generated
-	// read-only GC program; and gc[0] points to BSS space for sparse GC bitmap.
-	// For huge types (>maxGCMask), runtime unrolls the program directly into
-	// GC bitmap and gc[0] is not used. For moderately-sized types, runtime
-	// unrolls the program into gc[0] space on first use. The first byte of gc[0]
-	// (gc[0][0]) contains 'unroll' flag saying whether the program is already
-	// unrolled into gc[0] or not.
-	gc      [2]uintptr
+	// gcdata stores the GC type data for the garbage collector.
+	// If the KindGCProg bit is set in kind, gcdata is a GC program.
+	// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
+	gcdata  *byte
 	_string *string
 	x       *uncommontype
 	ptrto   *_type

From d36cc027959170c9927a52f139482d2369f173af Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Fri, 15 May 2015 14:46:20 -0400
Subject: [PATCH 124/232] reflect: make PtrTo(FuncOf(...)) not crash

Change-Id: Ie67e295bf327126dfdc75b73979fe33fbcb79ad9
Reviewed-on: https://go-review.googlesource.com/10150
Reviewed-by: Austin Clements 
---
 src/reflect/all_test.go | 23 ++++++++++++++++++++++-
 src/reflect/type.go     |  2 +-
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/src/reflect/all_test.go b/src/reflect/all_test.go
index 9214577c2e..9a99f742d6 100644
--- a/src/reflect/all_test.go
+++ b/src/reflect/all_test.go
@@ -4618,7 +4618,7 @@ func TestGCBits(t *testing.T) {
 	verifyGCBits(t, ChanOf(BothDir, ArrayOf(100, Tscalar)), lit(1))
 
 	verifyGCBits(t, TypeOf((func([100]Xscalarptr))(nil)), lit(1))
-	//verifyGCBits(t, FuncOf([]Type{ArrayOf(100, Tscalarptr)}, nil, false), lit(1))
+	verifyGCBits(t, FuncOf([]Type{ArrayOf(100, Tscalarptr)}, nil, false), lit(1))
 
 	verifyGCBits(t, TypeOf((map[[100]Xscalarptr]Xscalar)(nil)), lit(1))
 	verifyGCBits(t, MapOf(ArrayOf(100, Tscalarptr), Tscalar), lit(1))
@@ -4643,3 +4643,24 @@ func TestGCBits(t *testing.T) {
 func rep(n int, b []byte) []byte { return bytes.Repeat(b, n) }
 func join(b ...[]byte) []byte    { return bytes.Join(b, nil) }
 func lit(x ...byte) []byte       { return x }
+
+func TestTypeOfTypeOf(t *testing.T) {
+	// Check that all the type constructors return concrete *rtype implementations.
+	// It's difficult to test directly because the reflect package is only at arm's length.
+	// The easiest thing to do is just call a function that crashes if it doesn't get an *rtype.
+	check := func(name string, typ Type) {
+		if underlying := TypeOf(typ).String(); underlying != "*reflect.rtype" {
+			t.Errorf("%v returned %v, not *reflect.rtype", name, underlying)
+		}
+	}
+
+	type T struct{ int }
+	check("TypeOf", TypeOf(T{}))
+
+	check("ArrayOf", ArrayOf(10, TypeOf(T{})))
+	check("ChanOf", ChanOf(BothDir, TypeOf(T{})))
+	check("FuncOf", FuncOf([]Type{TypeOf(T{})}, nil, false))
+	check("MapOf", MapOf(TypeOf(T{}), TypeOf(T{})))
+	check("PtrTo", PtrTo(TypeOf(T{})))
+	check("SliceOf", SliceOf(TypeOf(T{})))
+}
diff --git a/src/reflect/type.go b/src/reflect/type.go
index f39ba52a42..e55a0d146c 100644
--- a/src/reflect/type.go
+++ b/src/reflect/type.go
@@ -1609,7 +1609,7 @@ func FuncOf(in, out []Type, variadic bool) Type {
 	ft.ptrToThis = nil
 	funcLookupCache.m[hash] = append(funcLookupCache.m[hash], &ft.rtype)
 
-	return ft
+	return &ft.rtype
 }
 
 // funcStr builds a string representation of a funcType.

From 6e8bcbbe8987d9b06ef2c349913cde11e6dd8339 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Fri, 15 May 2015 16:11:25 -0400
Subject: [PATCH 125/232] cmd/internal/gc: refine ginscmp comment

Change-Id: I2ebb36c6c5de9d34e52ed523e9c888452591924a
Reviewed-on: https://go-review.googlesource.com/10152
Reviewed-by: Minux Ma 
---
 src/cmd/internal/gc/go.go | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/cmd/internal/gc/go.go b/src/cmd/internal/gc/go.go
index 31692bdf00..5fa85e25a7 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/internal/gc/go.go
@@ -786,8 +786,9 @@ type Arch struct {
 	//
 	// Ginscmp must be able to handle all kinds of arguments for n1 and n2,
 	// not just simple registers, although it can assume that there are no
-	// function calls needed during the evaluation, so no in-memory temporaries
-	// are necessary.
+	// function calls needed during the evaluation, and on 32-bit systems
+	// the values are guaranteed not to be 64-bit values, so no in-memory
+	// temporaries are necessary.
 	Ginscmp func(op int, t *Type, n1, n2 *Node, likely int) *obj.Prog
 
 	// Ginsboolval inserts instructions to convert the result

From ab4e7988bb30eaf43ef9c5b4dcedc30502a0b0e6 Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Fri, 1 May 2015 22:23:04 -0400
Subject: [PATCH 126/232] cmd/dist: add -k to "dist test" to keep going after
 error

Fixes #10336.

Change-Id: Idc3f60851aea590575dc293165d4d6f85ae001bc
Reviewed-on: https://go-review.googlesource.com/9645
Reviewed-by: Brad Fitzpatrick 
---
 src/cmd/dist/test.go | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index addf61dad9..1f26eef5ee 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -24,6 +24,7 @@ func cmdtest() {
 	var t tester
 	flag.BoolVar(&t.listMode, "list", false, "list available tests")
 	flag.BoolVar(&t.noRebuild, "no-rebuild", false, "don't rebuild std and cmd packages")
+	flag.BoolVar(&t.keepGoing, "k", false, "keep going even when error occurred")
 	flag.StringVar(&t.banner, "banner", "##### ", "banner prefix; blank means no section banners")
 	flag.StringVar(&t.runRxStr, "run", os.Getenv("GOTESTONLY"),
 		"run only those tests matching the regular expression; empty means to run all. "+
@@ -36,6 +37,7 @@ func cmdtest() {
 type tester struct {
 	listMode  bool
 	noRebuild bool
+	keepGoing bool
 	runRxStr  string
 	runRx     *regexp.Regexp
 	runRxWant bool
@@ -163,6 +165,7 @@ func (t *tester) run() {
 	os.Unsetenv("GOROOT_FINAL")
 
 	var lastHeading string
+	ok := true
 	for _, dt := range t.tests {
 		if t.runRx != nil && (t.runRx.MatchString(dt.name) != t.runRxWant) {
 			t.partial = true
@@ -176,10 +179,18 @@ func (t *tester) run() {
 			fmt.Printf("# go tool dist test -run=^%s$\n", dt.name)
 		}
 		if err := dt.fn(); err != nil {
-			log.Fatalf("Failed: %v", err)
+			ok = false
+			if t.keepGoing {
+				log.Printf("Failed: %v", err)
+			} else {
+				log.Fatalf("Failed: %v", err)
+			}
 		}
 	}
-	if t.partial {
+	if !ok {
+		fmt.Println("\nFAILED")
+		os.Exit(1)
+	} else if t.partial {
 		fmt.Println("\nALL TESTS PASSED (some were excluded)")
 	} else {
 		fmt.Println("\nALL TESTS PASSED")

From a0fc306023d77e5605203c14ca92f368bdbce3ae Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Wed, 13 May 2015 17:08:16 -0400
Subject: [PATCH 127/232] runtime: eliminate runqvictims and a copy from
 runqsteal

Currently, runqsteal steals Gs from another P into an intermediate
buffer and then copies those Gs into the current P's run queue. This
intermediate buffer itself was moved from the stack to the P in commit
c4fe503 to eliminate the cost of zeroing it on every steal.

This commit follows up c4fe503 by stealing directly into the current
P's run queue, which eliminates the copy and the need for the
intermediate buffer. The update to the tail pointer is only committed
once the entire steal operation has succeeded, so the semantics of
stealing do not change.

Change-Id: Icdd7a0eb82668980bf42c0154b51eef6419fdd51
Reviewed-on: https://go-review.googlesource.com/9998
Reviewed-by: Russ Cox 
Run-TryBot: Austin Clements 
---
 src/runtime/proc1.go    | 21 ++++++++++-----------
 src/runtime/runtime2.go |  7 +++----
 2 files changed, 13 insertions(+), 15 deletions(-)

diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 8aeacee747..4ce756b692 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -3460,10 +3460,11 @@ func runqget(_p_ *p) (gp *g, inheritTime bool) {
 	}
 }
 
-// Grabs a batch of goroutines from local runnable queue.
-// batch array must be of size len(p->runq)/2. Returns number of grabbed goroutines.
+// Grabs a batch of goroutines from _p_'s runnable queue into batch.
+// Batch is a ring buffer starting at batchHead.
+// Returns number of grabbed goroutines.
 // Can be executed by any P.
-func runqgrab(_p_ *p, batch []*g, stealRunNextG bool) uint32 {
+func runqgrab(_p_ *p, batch *[256]*g, batchHead uint32, stealRunNextG bool) uint32 {
 	for {
 		h := atomicload(&_p_.runqhead) // load-acquire, synchronize with other consumers
 		t := atomicload(&_p_.runqtail) // load-acquire, synchronize with the producer
@@ -3484,7 +3485,7 @@ func runqgrab(_p_ *p, batch []*g, stealRunNextG bool) uint32 {
 					if !_p_.runnext.cas(next, 0) {
 						continue
 					}
-					batch[0] = next.ptr()
+					batch[batchHead%uint32(len(batch))] = next.ptr()
 					return 1
 				}
 			}
@@ -3494,7 +3495,8 @@ func runqgrab(_p_ *p, batch []*g, stealRunNextG bool) uint32 {
 			continue
 		}
 		for i := uint32(0); i < n; i++ {
-			batch[i] = _p_.runq[(h+i)%uint32(len(_p_.runq))]
+			g := _p_.runq[(h+i)%uint32(len(_p_.runq))]
+			batch[(batchHead+i)%uint32(len(batch))] = g
 		}
 		if cas(&_p_.runqhead, h, h+n) { // cas-release, commits consume
 			return n
@@ -3506,23 +3508,20 @@ func runqgrab(_p_ *p, batch []*g, stealRunNextG bool) uint32 {
 // and put onto local runnable queue of p.
 // Returns one of the stolen elements (or nil if failed).
 func runqsteal(_p_, p2 *p, stealRunNextG bool) *g {
-	n := runqgrab(p2, _p_.runqvictims[:], stealRunNextG)
+	t := _p_.runqtail
+	n := runqgrab(p2, &_p_.runq, t, stealRunNextG)
 	if n == 0 {
 		return nil
 	}
 	n--
-	gp := _p_.runqvictims[n]
+	gp := _p_.runq[(t+n)%uint32(len(_p_.runq))]
 	if n == 0 {
 		return gp
 	}
 	h := atomicload(&_p_.runqhead) // load-acquire, synchronize with consumers
-	t := _p_.runqtail
 	if t-h+n >= uint32(len(_p_.runq)) {
 		throw("runqsteal: runq overflow")
 	}
-	for i := uint32(0); i < n; i++ {
-		_p_.runq[(t+i)%uint32(len(_p_.runq))] = _p_.runqvictims[i]
-	}
 	atomicstore(&_p_.runqtail, t+n) // store-release, makes the item available for consumption
 	return gp
 }
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index ae93bb8dcb..8dfece5845 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -353,10 +353,9 @@ type p struct {
 	goidcacheend uint64
 
 	// Queue of runnable goroutines. Accessed without lock.
-	runqhead    uint32
-	runqtail    uint32
-	runq        [256]*g
-	runqvictims [128]*g // Used to stage victims from another p's runq
+	runqhead uint32
+	runqtail uint32
+	runq     [256]*g
 	// runnext, if non-nil, is a runnable G that was ready'd by
 	// the current G and should be run next instead of what's in
 	// runq if there's time remaining in the running G's time

From 0b9866fd561d5ecebee14d73c0a4938dec5abe7d Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Sat, 16 May 2015 20:05:58 -0400
Subject: [PATCH 128/232] buildall.bash: exit 1 when make.bash fails

If make.bash fails, there is no point continuing any further.

Fixes #10880.

Change-Id: I350cc16999372422ad3d2e0327d52d467886a5b1
Reviewed-on: https://go-review.googlesource.com/10180
Reviewed-by: Brad Fitzpatrick 
---
 src/buildall.bash | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/buildall.bash b/src/buildall.bash
index a07529e733..ba23d31a50 100755
--- a/src/buildall.bash
+++ b/src/buildall.bash
@@ -36,7 +36,7 @@ fi
 targets="$((ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p'; echo linux-386-387 linux-arm-arm5) | sort | egrep -v android-arm | egrep "$pattern" | egrep 'linux|nacl')
 $(ls runtime | sed -n 's/^rt0_\(.*\)_\(.*\)\.s/\1-\2/p' | egrep -v 'android-arm|darwin-arm' | egrep "$pattern" | egrep -v 'linux|nacl')"
 
-./make.bash
+./make.bash || exit 1
 GOROOT="$(cd .. && pwd)"
 
 failed=false

From e544bee1ddf4a2869221b68ef8cec6c97b6d827b Mon Sep 17 00:00:00 2001
From: Alex Brainman 
Date: Tue, 21 Apr 2015 10:56:45 +1000
Subject: [PATCH 129/232] runtime: correct exception stack trace output

It is misleading when stack trace say:

signal arrived during cgo execution

but we are not in cgo call.

Change-Id: I627e2f2bdc7755074677f77f21befc070a101914
Reviewed-on: https://go-review.googlesource.com/9190
Reviewed-by: Russ Cox 
---
 src/runtime/signal_windows.go | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/runtime/signal_windows.go b/src/runtime/signal_windows.go
index da8a1c5801..b2fce53534 100644
--- a/src/runtime/signal_windows.go
+++ b/src/runtime/signal_windows.go
@@ -131,7 +131,9 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
 
 	print("PC=", hex(r.ip()), "\n")
 	if _g_.m.lockedg != nil && _g_.m.ncgo > 0 && gp == _g_.m.g0 {
-		print("signal arrived during cgo execution\n")
+		if iscgo {
+			print("signal arrived during external code execution\n")
+		}
 		gp = _g_.m.lockedg
 	}
 	print("\n")

From 5f7060afd2dea927ac64804c7e4639f6635c3bb7 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Fri, 15 May 2015 16:03:27 -0400
Subject: [PATCH 130/232] runtime: don't start GC if preemptoff is set

In order to avoid deadlocks, startGC avoids kicking off GC if locks
are held by the calling M. However, it currently fails to check
preemptoff, which is the other way to disable preemption.

Fix this by adding a check for preemptoff.

Change-Id: Ie1083166e5ba4af5c9d6c5a42efdfaaef41ca997
Reviewed-on: https://go-review.googlesource.com/10153
Reviewed-by: Russ Cox 
---
 src/runtime/mgc.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index d33fbf11de..848b46804c 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -703,7 +703,7 @@ func startGC(mode int) {
 	// trying to run gc while holding a lock. The next mallocgc without a lock
 	// will do the gc instead.
 	mp := acquirem()
-	if gp := getg(); gp == mp.g0 || mp.locks > 1 || !memstats.enablegc || panicking != 0 || gcpercent < 0 {
+	if gp := getg(); gp == mp.g0 || mp.locks > 1 || mp.preemptoff != "" || !memstats.enablegc || panicking != 0 || gcpercent < 0 {
 		releasem(mp)
 		return
 	}

From a1da255aa0b962231d80e594abe300200e6b4c73 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Fri, 15 May 2015 16:00:50 -0400
Subject: [PATCH 131/232] runtime: factor stoptheworld/starttheworld pattern

There are several steps to stopping and starting the world and
currently they're open-coded in several places. The garbage collector
is the only thing that needs to stop and start the world in a
non-trivial pattern. Replace all other uses with calls to higher-level
functions that implement the entire pattern necessary to stop and
start the world.

This is a pure refectoring and should not change any code semantics.
In the following commits, we'll make changes that are easier to do
with this abstraction in place.

This commit renames the old starttheworld to startTheWorldWithSema.
This is a slight misnomer right now because the callers release
worldsema just before calling this. However, a later commit will swap
these and I don't want to think of another name in the mean time.

Change-Id: I5dc97f87b44fb98963c49c777d7053653974c911
Reviewed-on: https://go-review.googlesource.com/10154
Reviewed-by: Russ Cox 
---
 src/runtime/debug.go    | 11 ++----
 src/runtime/heapdump.go | 13 +++----
 src/runtime/mgc.go      | 10 +++---
 src/runtime/mprof.go    | 18 +++-------
 src/runtime/mstats.go   | 17 +++------
 src/runtime/proc.go     |  2 +-
 src/runtime/proc1.go    | 80 +++++++++++++++++++++++++++++------------
 src/runtime/trace.go    | 34 ++++++------------
 8 files changed, 88 insertions(+), 97 deletions(-)

diff --git a/src/runtime/debug.go b/src/runtime/debug.go
index 3ecaac10bc..9aec3b03e0 100644
--- a/src/runtime/debug.go
+++ b/src/runtime/debug.go
@@ -22,17 +22,12 @@ func GOMAXPROCS(n int) int {
 		return ret
 	}
 
-	semacquire(&worldsema, false)
-	gp := getg()
-	gp.m.preemptoff = "GOMAXPROCS"
-	systemstack(stoptheworld)
+	stopTheWorld("GOMAXPROCS")
 
-	// newprocs will be processed by starttheworld
+	// newprocs will be processed by startTheWorld
 	newprocs = int32(n)
 
-	gp.m.preemptoff = ""
-	semrelease(&worldsema)
-	systemstack(starttheworld)
+	startTheWorld()
 	return ret
 }
 
diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index 0add63acb4..196cb3fcb5 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -15,20 +15,15 @@ import "unsafe"
 
 //go:linkname runtime_debug_WriteHeapDump runtime/debug.WriteHeapDump
 func runtime_debug_WriteHeapDump(fd uintptr) {
-	semacquire(&worldsema, false)
-	gp := getg()
-	gp.m.preemptoff = "write heap dump"
-	systemstack(stoptheworld)
+	stopTheWorld("write heap dump")
 
 	systemstack(func() {
 		writeheapdump_m(fd)
 	})
 
-	gp.m.preemptoff = ""
-	gp.m.locks++
-	semrelease(&worldsema)
-	systemstack(starttheworld)
-	gp.m.locks--
+	getg().m.locks++ // TODO: Is this necessary?
+	startTheWorld()
+	getg().m.locks--
 }
 
 const (
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 848b46804c..68636740a6 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -699,7 +699,7 @@ const (
 func startGC(mode int) {
 	// The gc is turned off (via enablegc) until the bootstrap has completed.
 	// Also, malloc gets called in the guts of a number of libraries that might be
-	// holding locks. To avoid deadlocks during stoptheworld, don't bother
+	// holding locks. To avoid deadlocks during stop-the-world, don't bother
 	// trying to run gc while holding a lock. The next mallocgc without a lock
 	// will do the gc instead.
 	mp := acquirem()
@@ -797,7 +797,7 @@ func gc(mode int) {
 		traceGCStart()
 	}
 
-	systemstack(stoptheworld)
+	systemstack(stopTheWorldWithSema)
 	systemstack(finishsweep_m) // finish sweep before we start concurrent scan.
 	// clearpools before we start the GC. If we wait they memory will not be
 	// reclaimed until the next GC cycle.
@@ -814,7 +814,7 @@ func gc(mode int) {
 			setGCPhase(_GCscan)
 
 			// Concurrent scan.
-			starttheworld()
+			startTheWorldWithSema()
 			if debug.gctrace > 0 {
 				tScan = nanotime()
 			}
@@ -858,7 +858,7 @@ func gc(mode int) {
 		if debug.gctrace > 0 {
 			tMarkTerm = nanotime()
 		}
-		systemstack(stoptheworld)
+		systemstack(stopTheWorldWithSema)
 		// The gcphase is _GCmark, it will transition to _GCmarktermination
 		// below. The important thing is that the wb remains active until
 		// all marking is complete. This includes writes made by the GC.
@@ -958,7 +958,7 @@ func gc(mode int) {
 		throw("gc done but gcphase != _GCoff")
 	}
 
-	systemstack(starttheworld)
+	systemstack(startTheWorldWithSema)
 
 	releasem(mp)
 	mp = nil
diff --git a/src/runtime/mprof.go b/src/runtime/mprof.go
index 4544344780..a618bd5e81 100644
--- a/src/runtime/mprof.go
+++ b/src/runtime/mprof.go
@@ -521,9 +521,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
 	n = NumGoroutine()
 	if n <= len(p) {
 		gp := getg()
-		semacquire(&worldsema, false)
-		gp.m.preemptoff = "profile"
-		systemstack(stoptheworld)
+		stopTheWorld("profile")
 
 		n = NumGoroutine()
 		if n <= len(p) {
@@ -544,9 +542,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
 			}
 		}
 
-		gp.m.preemptoff = ""
-		semrelease(&worldsema)
-		systemstack(starttheworld)
+		startTheWorld()
 	}
 
 	return n, ok
@@ -565,10 +561,7 @@ func saveg(pc, sp uintptr, gp *g, r *StackRecord) {
 // into buf after the trace for the current goroutine.
 func Stack(buf []byte, all bool) int {
 	if all {
-		semacquire(&worldsema, false)
-		gp := getg()
-		gp.m.preemptoff = "stack trace"
-		systemstack(stoptheworld)
+		stopTheWorld("stack trace")
 	}
 
 	n := 0
@@ -590,10 +583,7 @@ func Stack(buf []byte, all bool) int {
 	}
 
 	if all {
-		gp := getg()
-		gp.m.preemptoff = ""
-		semrelease(&worldsema)
-		systemstack(starttheworld)
+		startTheWorld()
 	}
 	return n
 }
diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go
index c8e5249156..bd6ac1a4d5 100644
--- a/src/runtime/mstats.go
+++ b/src/runtime/mstats.go
@@ -153,24 +153,15 @@ func init() {
 
 // ReadMemStats populates m with memory allocator statistics.
 func ReadMemStats(m *MemStats) {
-	// Have to acquire worldsema to stop the world,
-	// because stoptheworld can only be used by
-	// one goroutine at a time, and there might be
-	// a pending garbage collection already calling it.
-	semacquire(&worldsema, false)
-	gp := getg()
-	gp.m.preemptoff = "read mem stats"
-	systemstack(stoptheworld)
+	stopTheWorld("read mem stats")
 
 	systemstack(func() {
 		readmemstats_m(m)
 	})
 
-	gp.m.preemptoff = ""
-	gp.m.locks++
-	semrelease(&worldsema)
-	systemstack(starttheworld)
-	gp.m.locks--
+	getg().m.locks++ // TODO: Is this necessary?
+	startTheWorld()
+	getg().m.locks--
 }
 
 func readmemstats_m(stats *MemStats) {
diff --git a/src/runtime/proc.go b/src/runtime/proc.go
index f725fc890b..805b96e627 100644
--- a/src/runtime/proc.go
+++ b/src/runtime/proc.go
@@ -203,7 +203,7 @@ func acquireSudog() *sudog {
 	// acquireSudog, acquireSudog calls new(sudog),
 	// new calls malloc, malloc can call the garbage collector,
 	// and the garbage collector calls the semaphore implementation
-	// in stoptheworld.
+	// in stopTheWorld.
 	// Break the cycle by doing acquirem/releasem around new(sudog).
 	// The acquirem/releasem increments m.locks during new(sudog),
 	// which keeps the garbage collector from being invoked.
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 4ce756b692..3d86d40654 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -211,7 +211,7 @@ func helpgc(nproc int32) {
 // sched.stopwait to in order to request that all Gs permanently stop.
 const freezeStopWait = 0x7fffffff
 
-// Similar to stoptheworld but best-effort and can be called several times.
+// Similar to stopTheWorld but best-effort and can be called several times.
 // There is no reverse operation, used during crashing.
 // This function must not lock any mutexes.
 func freezetheworld() {
@@ -528,31 +528,65 @@ func quiesce(mastergp *g) {
 	mcall(mquiesce)
 }
 
+// stopTheWorld stops all P's from executing goroutines, interrupting
+// all goroutines at GC safe points and records reason as the reason
+// for the stop. On return, only the current goroutine's P is running.
+// stopTheWorld must not be called from a system stack and the caller
+// must not hold worldsema. The caller must call startTheWorld when
+// other P's should resume execution.
+//
+// stopTheWorld is safe for multiple goroutines to call at the
+// same time. Each will execute its own stop, and the stops will
+// be serialized.
+//
+// This is also used by routines that do stack dumps. If the system is
+// in panic or being exited, this may not reliably stop all
+// goroutines.
+func stopTheWorld(reason string) {
+	semacquire(&worldsema, false)
+	getg().m.preemptoff = reason
+	systemstack(stopTheWorldWithSema)
+}
+
+// startTheWorld undoes the effects of stopTheWorld.
+func startTheWorld() {
+	getg().m.preemptoff = ""
+	semrelease(&worldsema)
+	systemstack(startTheWorldWithSema)
+}
+
 // Holding worldsema grants an M the right to try to stop the world.
-// The procedure is:
-//
-//	semacquire(&worldsema);
-//	m.preemptoff = "reason";
-//	stoptheworld();
-//
-//	... do stuff ...
-//
-//	m.preemptoff = "";
-//	semrelease(&worldsema);
-//	starttheworld();
-//
 var worldsema uint32 = 1
 
-// This is used by the GC as well as the routines that do stack dumps. In the case
-// of GC all the routines can be reliably stopped. This is not always the case
-// when the system is in panic or being exited.
-func stoptheworld() {
+// stopTheWorldWithSema is the core implementation of stopTheWorld.
+// The caller is responsible for acquiring worldsema and disabling
+// preemption first and then should stopTheWorldWithSema on the system
+// stack:
+//
+//	semacquire(&worldsema, false)
+//	m.preemptoff = "reason"
+//	systemstack(stopTheWorldWithSema)
+//
+// When finished, the caller must either call startTheWorld or undo
+// these three operations separately:
+//
+//	m.preemptoff = ""
+//	semrelease(&worldsema)
+//	systemstack(startTheWorldWithSema)
+//
+// It is allowed to acquire worldsema once and then execute multiple
+// startTheWorldWithSema/stopTheWorldWithSema pairs.
+// Other P's are able to execute between successive calls to
+// startTheWorldWithSema and stopTheWorldWithSema.
+// Holding worldsema causes any other goroutines invoking
+// stopTheWorld to block.
+func stopTheWorldWithSema() {
 	_g_ := getg()
 
 	// If we hold a lock, then we won't be able to stop another M
 	// that is blocked trying to acquire the lock.
 	if _g_.m.locks > 0 {
-		throw("stoptheworld: holding locks")
+		throw("stopTheWorld: holding locks")
 	}
 
 	lock(&sched.lock)
@@ -599,12 +633,12 @@ func stoptheworld() {
 		}
 	}
 	if sched.stopwait != 0 {
-		throw("stoptheworld: not stopped")
+		throw("stopTheWorld: not stopped")
 	}
 	for i := 0; i < int(gomaxprocs); i++ {
 		p := allp[i]
 		if p.status != _Pgcstop {
-			throw("stoptheworld: not stopped")
+			throw("stopTheWorld: not stopped")
 		}
 	}
 }
@@ -614,7 +648,7 @@ func mhelpgc() {
 	_g_.m.helpgc = -1
 }
 
-func starttheworld() {
+func startTheWorldWithSema() {
 	_g_ := getg()
 
 	_g_.m.locks++        // disable preemption because it can be holding p in a local var
@@ -643,7 +677,7 @@ func starttheworld() {
 			mp := p.m.ptr()
 			p.m = 0
 			if mp.nextp != 0 {
-				throw("starttheworld: inconsistent mp->nextp")
+				throw("startTheWorld: inconsistent mp->nextp")
 			}
 			mp.nextp.set(p)
 			notewakeup(&mp.park)
@@ -1304,7 +1338,7 @@ func startlockedm(gp *g) {
 	stopm()
 }
 
-// Stops the current m for stoptheworld.
+// Stops the current m for stopTheWorld.
 // Returns when the world is restarted.
 func gcstopm() {
 	_g_ := getg()
diff --git a/src/runtime/trace.go b/src/runtime/trace.go
index 3b7501b9b4..6da7baddc5 100644
--- a/src/runtime/trace.go
+++ b/src/runtime/trace.go
@@ -132,10 +132,7 @@ type traceBuf struct {
 func StartTrace() error {
 	// Stop the world, so that we can take a consistent snapshot
 	// of all goroutines at the beginning of the trace.
-	semacquire(&worldsema, false)
-	_g_ := getg()
-	_g_.m.preemptoff = "start tracing"
-	systemstack(stoptheworld)
+	stopTheWorld("start tracing")
 
 	// We are in stop-the-world, but syscalls can finish and write to trace concurrently.
 	// Exitsyscall could check trace.enabled long before and then suddenly wake up
@@ -146,9 +143,7 @@ func StartTrace() error {
 
 	if trace.enabled || trace.shutdown {
 		unlock(&trace.bufLock)
-		_g_.m.preemptoff = ""
-		semrelease(&worldsema)
-		systemstack(starttheworld)
+		startTheWorld()
 		return errorString("tracing is already enabled")
 	}
 
@@ -175,9 +170,7 @@ func StartTrace() error {
 
 	unlock(&trace.bufLock)
 
-	_g_.m.preemptoff = ""
-	semrelease(&worldsema)
-	systemstack(starttheworld)
+	startTheWorld()
 	return nil
 }
 
@@ -186,19 +179,14 @@ func StartTrace() error {
 func StopTrace() {
 	// Stop the world so that we can collect the trace buffers from all p's below,
 	// and also to avoid races with traceEvent.
-	semacquire(&worldsema, false)
-	_g_ := getg()
-	_g_.m.preemptoff = "stop tracing"
-	systemstack(stoptheworld)
+	stopTheWorld("stop tracing")
 
 	// See the comment in StartTrace.
 	lock(&trace.bufLock)
 
 	if !trace.enabled {
 		unlock(&trace.bufLock)
-		_g_.m.preemptoff = ""
-		semrelease(&worldsema)
-		systemstack(starttheworld)
+		startTheWorld()
 		return
 	}
 
@@ -236,9 +224,7 @@ func StopTrace() {
 
 	unlock(&trace.bufLock)
 
-	_g_.m.preemptoff = ""
-	semrelease(&worldsema)
-	systemstack(starttheworld)
+	startTheWorld()
 
 	// The world is started but we've set trace.shutdown, so new tracing can't start.
 	// Wait for the trace reader to flush pending buffers and stop.
@@ -428,9 +414,9 @@ func traceEvent(ev byte, skip int, args ...uint64) {
 
 	// The caller checked that trace.enabled == true, but trace.enabled might have been
 	// turned off between the check and now. Check again. traceLockBuffer did mp.locks++,
-	// StopTrace does stoptheworld, and stoptheworld waits for mp.locks to go back to zero,
+	// StopTrace does stopTheWorld, and stopTheWorld waits for mp.locks to go back to zero,
 	// so if we see trace.enabled == true now, we know it's true for the rest of the function.
-	// Exitsyscall can run even during stoptheworld. The race with StartTrace/StopTrace
+	// Exitsyscall can run even during stopTheWorld. The race with StartTrace/StopTrace
 	// during tracing in exitsyscall is resolved by locking trace.bufLock in traceLockBuffer.
 	if !trace.enabled {
 		traceReleaseBuffer(pid)
@@ -733,7 +719,7 @@ func traceProcStart() {
 }
 
 func traceProcStop(pp *p) {
-	// Sysmon and stoptheworld can stop Ps blocked in syscalls,
+	// Sysmon and stopTheWorld can stop Ps blocked in syscalls,
 	// to handle this we temporary employ the P.
 	mp := acquirem()
 	oldp := mp.p
@@ -807,7 +793,7 @@ func traceGoSysExit(ts int64) {
 }
 
 func traceGoSysBlock(pp *p) {
-	// Sysmon and stoptheworld can declare syscalls running on remote Ps as blocked,
+	// Sysmon and stopTheWorld can declare syscalls running on remote Ps as blocked,
 	// to handle this we temporary employ the P.
 	mp := acquirem()
 	oldp := mp.p

From 9c44a41dd56660b7685da61bf1efb00cc7c1e198 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Fri, 15 May 2015 16:10:00 -0400
Subject: [PATCH 132/232] runtime: disallow preemption during startTheWorld

Currently, startTheWorld clears preemptoff for the current M before
starting the world. A few callers increment m.locks around
startTheWorld, presumably to prevent preemption any time during
starting the world. This is almost certainly pointless (none of the
other callers do this), but there's no harm in making startTheWorld
keep preemption disabled until it's all done, which definitely lets us
drop these m.locks manipulations.

Change-Id: I8a93658abd0c72276c9bafa3d2c7848a65b4691a
Reviewed-on: https://go-review.googlesource.com/10155
Reviewed-by: Russ Cox 
---
 src/runtime/heapdump.go | 2 --
 src/runtime/mstats.go   | 2 --
 src/runtime/proc1.go    | 2 +-
 3 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/runtime/heapdump.go b/src/runtime/heapdump.go
index 196cb3fcb5..c0fff3f1ce 100644
--- a/src/runtime/heapdump.go
+++ b/src/runtime/heapdump.go
@@ -21,9 +21,7 @@ func runtime_debug_WriteHeapDump(fd uintptr) {
 		writeheapdump_m(fd)
 	})
 
-	getg().m.locks++ // TODO: Is this necessary?
 	startTheWorld()
-	getg().m.locks--
 }
 
 const (
diff --git a/src/runtime/mstats.go b/src/runtime/mstats.go
index bd6ac1a4d5..3eff7f6b3e 100644
--- a/src/runtime/mstats.go
+++ b/src/runtime/mstats.go
@@ -159,9 +159,7 @@ func ReadMemStats(m *MemStats) {
 		readmemstats_m(m)
 	})
 
-	getg().m.locks++ // TODO: Is this necessary?
 	startTheWorld()
-	getg().m.locks--
 }
 
 func readmemstats_m(stats *MemStats) {
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 3d86d40654..ab0566b470 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -550,9 +550,9 @@ func stopTheWorld(reason string) {
 
 // startTheWorld undoes the effects of stopTheWorld.
 func startTheWorld() {
-	getg().m.preemptoff = ""
 	semrelease(&worldsema)
 	systemstack(startTheWorldWithSema)
+	getg().m.preemptoff = ""
 }
 
 // Holding worldsema grants an M the right to try to stop the world.

From 277acca286e47fd704aae10d030c74927ba2a8d2 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Fri, 15 May 2015 16:13:14 -0400
Subject: [PATCH 133/232] runtime: hold worldsema while starting the world

Currently, startTheWorld releases worldsema before starting the
world. Since startTheWorld can change gomaxprocs after allowing Ps to
run, this means that gomaxprocs can change while another P holds
worldsema.

Unfortunately, the garbage collector and forEachP assume that holding
worldsema protects against changes in gomaxprocs (which it *almost*
does). In particular, this is causing somewhat frequent "P did not run
fn" crashes in forEachP in the runtime tests because gomaxprocs is
changing between the several loops that forEachP does over all the Ps.

Fix this by only releasing worldsema after the world is started.

This relates to issue #10618. forEachP still fails under stress
testing, but much less frequently.

Change-Id: I085d627b70cca9ebe9af28fe73b9872f1bb224ff
Reviewed-on: https://go-review.googlesource.com/10156
Reviewed-by: Russ Cox 
---
 src/runtime/mgc.go   | 3 +--
 src/runtime/proc1.go | 9 ++++++---
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index 68636740a6..a16d7603a6 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -952,13 +952,12 @@ func gc(mode int) {
 	// all done
 	mp.preemptoff = ""
 
-	semrelease(&worldsema)
-
 	if gcphase != _GCoff {
 		throw("gc done but gcphase != _GCoff")
 	}
 
 	systemstack(startTheWorldWithSema)
+	semrelease(&worldsema)
 
 	releasem(mp)
 	mp = nil
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index ab0566b470..31247db02a 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -550,12 +550,15 @@ func stopTheWorld(reason string) {
 
 // startTheWorld undoes the effects of stopTheWorld.
 func startTheWorld() {
-	semrelease(&worldsema)
 	systemstack(startTheWorldWithSema)
+	// worldsema must be held over startTheWorldWithSema to ensure
+	// gomaxprocs cannot change while worldsema is held.
+	semrelease(&worldsema)
 	getg().m.preemptoff = ""
 }
 
-// Holding worldsema grants an M the right to try to stop the world.
+// Holding worldsema grants an M the right to try to stop the world
+// and prevents gomaxprocs from changing concurrently.
 var worldsema uint32 = 1
 
 // stopTheWorldWithSema is the core implementation of stopTheWorld.
@@ -571,8 +574,8 @@ var worldsema uint32 = 1
 // these three operations separately:
 //
 //	m.preemptoff = ""
-//	semrelease(&worldsema)
 //	systemstack(startTheWorldWithSema)
+//	semrelease(&worldsema)
 //
 // It is allowed to acquire worldsema once and then execute multiple
 // startTheWorldWithSema/stopTheWorldWithSema pairs.

From f0dd002895b48595f6c14f2bf606775289f59d5f Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Fri, 15 May 2015 16:31:17 -0400
Subject: [PATCH 134/232] runtime: use separate count and note for forEachP

Currently, forEachP reuses the stopwait and stopnote fields from
stopTheWorld to track how many Ps have not responded to the safe-point
request and to sleep until all Ps have responded.

It was assumed this was safe because both stopTheWorld and forEachP
must occur under the worlsema and hence stopwait and stopnote cannot
be used for both purposes simultaneously and callers could always
determine the appropriate use based on sched.gcwaiting (which is only
set by stopTheWorld). However, this is not the case, since it's
possible for there to be a window between when an M observes that
gcwaiting is set and when it checks stopwait during which stopwait
could have changed meanings. When this happens, the M decrements
stopwait and may wakeup stopnote, but does not otherwise participate
in the forEachP protocol. As a result, stopwait is decremented too
many times, so it may reach zero before all Ps have run the safe-point
function, causing forEachP to wake up early. It will then either
observe that some P has not run the safe-point function and panic with
"P did not run fn", or the remaining P (or Ps) will run the safe-point
function before it wakes up and it will observe that stopwait is
negative and panic with "not stopped".

Fix this problem by giving forEachP its own safePointWait and
safePointNote fields.

One known sequence of events that can cause this race is as
follows. It involves three actors:

G1 is running on M1 on P1. P1 has an empty run queue.

G2/M2 is in a blocked syscall and has lost its P. (The details of this
don't matter, it just needs to be in a position where it needs to grab
an idle P.)

GC just started on G3/M3/P3. (These aren't very involved, they just
have to be separate from the other G's, M's, and P's.)

1. GC calls stopTheWorld(), which sets sched.gcwaiting to 1.

Now G1/M1 begins to enter a syscall:

2. G1/M1 invokes reentersyscall, which sets the P1's status to
   _Psyscall.

3. G1/M1's reentersyscall observes gcwaiting != 0 and calls
   entersyscall_gcwait.

4. G1/M1's entersyscall_gcwait blocks acquiring sched.lock.

Back on GC:

5. stopTheWorld cas's P1's status to _Pgcstop, does other stuff, and
   returns.

6. GC does stuff and then calls startTheWorld().

7. startTheWorld() calls procresize(), which sets P1's status to
   _Pidle and puts P1 on the idle list.

Now G2/M2 returns from its syscall and takes over P1:

8. G2/M2 returns from its blocked syscall and gets P1 from the idle
   list.

9. G2/M2 acquires P1, which sets P1's status to _Prunning.

10. G2/M2 starts a new syscall and invokes reentersyscall, which sets
    P1's status to _Psyscall.

Back on G1/M1:

11. G1/M1 finally acquires sched.lock in entersyscall_gcwait.

At this point, G1/M1 still thinks it's running on P1. P1's status is
_Psyscall, which is consistent with what G1/M1 is doing, but it's
_Psyscall because *G2/M2* put it in to _Psyscall, not G1/M1. This is
basically an ABA race on P1's status.

Because forEachP currently shares stopwait with stopTheWorld. G1/M1's
entersyscall_gcwait observes the non-zero stopwait set by forEachP,
but mistakes it for a stopTheWorld. It cas's P1's status from
_Psyscall (set by G2/M2) to _Pgcstop and proceeds to decrement
stopwait one more time than forEachP was expecting.

Fixes #10618. (See the issue for details on why the above race is safe
when forEachP is not involved.)

Prior to this commit, the command
  stress ./runtime.test -test.run TestFutexsleep\|TestGoroutineProfile
would reliably fail after a few hundred runs. With this commit, it
ran for over 2 million runs and never crashed.

Change-Id: I9a91ea20035b34b6e5f07ef135b144115f281f30
Reviewed-on: https://go-review.googlesource.com/10157
Reviewed-by: Russ Cox 
---
 src/runtime/proc1.go    | 30 +++++++++++++++---------------
 src/runtime/runtime2.go |  4 +++-
 2 files changed, 18 insertions(+), 16 deletions(-)

diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 31247db02a..b0b3bf7711 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -790,10 +790,10 @@ func forEachP(fn func(*p)) {
 	_p_ := getg().m.p.ptr()
 
 	lock(&sched.lock)
-	if sched.stopwait != 0 {
-		throw("forEachP: sched.stopwait != 0")
+	if sched.safePointWait != 0 {
+		throw("forEachP: sched.safePointWait != 0")
 	}
-	sched.stopwait = gomaxprocs - 1
+	sched.safePointWait = gomaxprocs - 1
 	sched.safePointFn = fn
 
 	// Ask all Ps to run the safe point function.
@@ -813,11 +813,11 @@ func forEachP(fn func(*p)) {
 	for p := sched.pidle.ptr(); p != nil; p = p.link.ptr() {
 		if cas(&p.runSafePointFn, 1, 0) {
 			fn(p)
-			sched.stopwait--
+			sched.safePointWait--
 		}
 	}
 
-	wait := sched.stopwait > 0
+	wait := sched.safePointWait > 0
 	unlock(&sched.lock)
 
 	// Run fn for the current P.
@@ -843,15 +843,15 @@ func forEachP(fn func(*p)) {
 		for {
 			// Wait for 100us, then try to re-preempt in
 			// case of any races.
-			if notetsleep(&sched.stopnote, 100*1000) {
-				noteclear(&sched.stopnote)
+			if notetsleep(&sched.safePointNote, 100*1000) {
+				noteclear(&sched.safePointNote)
 				break
 			}
 			preemptall()
 		}
 	}
-	if sched.stopwait != 0 {
-		throw("forEachP: not stopped")
+	if sched.safePointWait != 0 {
+		throw("forEachP: not done")
 	}
 	for i := 0; i < int(gomaxprocs); i++ {
 		p := allp[i]
@@ -887,9 +887,9 @@ func runSafePointFn() {
 	}
 	sched.safePointFn(p)
 	lock(&sched.lock)
-	sched.stopwait--
-	if sched.stopwait == 0 {
-		notewakeup(&sched.stopnote)
+	sched.safePointWait--
+	if sched.safePointWait == 0 {
+		notewakeup(&sched.safePointNote)
 	}
 	unlock(&sched.lock)
 }
@@ -1262,9 +1262,9 @@ func handoffp(_p_ *p) {
 	}
 	if _p_.runSafePointFn != 0 && cas(&_p_.runSafePointFn, 1, 0) {
 		sched.safePointFn(_p_)
-		sched.stopwait--
-		if sched.stopwait == 0 {
-			notewakeup(&sched.stopnote)
+		sched.safePointWait--
+		if sched.safePointWait == 0 {
+			notewakeup(&sched.safePointNote)
 		}
 	}
 	if sched.runqsize != 0 {
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 8dfece5845..83d8062baf 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -441,7 +441,9 @@ type schedt struct {
 
 	// safepointFn should be called on each P at the next GC
 	// safepoint if p.runSafePointFn is set.
-	safePointFn func(*p)
+	safePointFn   func(*p)
+	safePointWait int32
+	safePointNote note
 
 	profilehz int32 // cpu profiling rate
 

From a21cf5b6a281df2c3506105cecfbeeda70afca1c Mon Sep 17 00:00:00 2001
From: David Chase 
Date: Fri, 15 May 2015 12:19:07 -0400
Subject: [PATCH 135/232] cmd/internal/gc: extend escape analysis to pointers
 in slices

Modified esc.go to allow slice literals (before append)
to be non-escaping.  Modified tests to account for changes
in escape behavior and to also test the two cases that
were previously not tested.

Also minor cleanups to debug-printing within esc.go

Allocation stats for running compiler
( cd src/html/template;
  for i in {1..5} ; do
     go tool 6g -memprofile=testzz.${i}.prof  -memprofilerate=1 *.go ;
     go tool pprof -alloc_objects -text  testzz.${i}.prof ;
     done ; )
before about 86k allocations
after  about 83k allocations

Fixes #8972

Change-Id: Ib61dd70dc74adb40d6f6fdda6eaa4bf7d83481de
Reviewed-on: https://go-review.googlesource.com/10118
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/esc.go | 100 +++++++++++++++++--------------------
 test/escape2.go            |  14 +++---
 test/escape2n.go           |  14 +++---
 test/escape_slice.go       |  81 ++++++++++++++++++++++++++++--
 4 files changed, 138 insertions(+), 71 deletions(-)

diff --git a/src/cmd/internal/gc/esc.go b/src/cmd/internal/gc/esc.go
index 5fb2095bda..a5b6a9b2b1 100644
--- a/src/cmd/internal/gc/esc.go
+++ b/src/cmd/internal/gc/esc.go
@@ -387,6 +387,19 @@ type EscState struct {
 	recursive bool      // recursive function or group of mutually recursive functions.
 }
 
+// funcSym returns n.Nname.Sym if no nils are encountered along the way.
+func funcSym(n *Node) *Sym {
+	if n == nil || n.Nname == nil {
+		return nil
+	}
+	return n.Nname.Sym
+}
+
+// curfnSym returns n.Curfn.Nname.Sym if no nils are encountered along the way.
+func curfnSym(n *Node) *Sym {
+	return funcSym(n.Curfn)
+}
+
 func escAnalyze(all *NodeList, recursive bool) {
 	var es EscState
 	e := &es
@@ -428,13 +441,7 @@ func escAnalyze(all *NodeList, recursive bool) {
 	if Debug['m'] != 0 {
 		for l := e.noesc; l != nil; l = l.Next {
 			if l.N.Esc == EscNone {
-				var tmp *Sym
-				if l.N.Curfn != nil && l.N.Curfn.Nname != nil {
-					tmp = l.N.Curfn.Nname.Sym
-				} else {
-					tmp = nil
-				}
-				Warnl(int(l.N.Lineno), "%v %v does not escape", tmp, Nconv(l.N, obj.FmtShort))
+				Warnl(int(l.N.Lineno), "%v %v does not escape", curfnSym(l.N), Nconv(l.N, obj.FmtShort))
 			}
 		}
 	}
@@ -593,13 +600,7 @@ func esc(e *EscState, n *Node, up *Node) {
 	}
 
 	if Debug['m'] > 1 {
-		var tmp *Sym
-		if Curfn != nil && Curfn.Nname != nil {
-			tmp = Curfn.Nname.Sym
-		} else {
-			tmp = nil
-		}
-		fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, tmp, n)
+		fmt.Printf("%v:[%d] %v esc: %v\n", Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn), n)
 	}
 
 	switch n.Op {
@@ -629,8 +630,12 @@ func esc(e *EscState, n *Node, up *Node) {
 
 		// Everything but fixed array is a dereference.
 	case ORANGE:
-		if Isfixedarray(n.Type) && n.List != nil && n.List.Next != nil {
-			escassign(e, n.List.Next.N, n.Right)
+		if n.List != nil && n.List.Next != nil {
+			if Isfixedarray(n.Type) {
+				escassign(e, n.List.Next.N, n.Right)
+			} else {
+				escassign(e, n.List.Next.N, addDereference(n.Right))
+			}
 		}
 
 	case OSWITCH:
@@ -670,13 +675,7 @@ func esc(e *EscState, n *Node, up *Node) {
 			// b escapes as well. If we ignore such OSLICEARR, we will conclude
 			// that b does not escape when b contents do.
 			if Debug['m'] != 0 {
-				var tmp *Sym
-				if n.Curfn != nil && n.Curfn.Nname != nil {
-					tmp = n.Curfn.Nname.Sym
-				} else {
-					tmp = nil
-				}
-				Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", tmp, Nconv(n.Left, obj.FmtShort))
+				Warnl(int(n.Lineno), "%v ignoring self-assignment to %v", curfnSym(n), Nconv(n.Left, obj.FmtShort))
 			}
 
 			break
@@ -763,7 +762,15 @@ func esc(e *EscState, n *Node, up *Node) {
 			for ll := n.List.Next; ll != nil; ll = ll.Next {
 				escassign(e, &e.theSink, ll.N) // lose track of assign to dereference
 			}
+		} else {
+			// append(slice1, slice2...) -- slice2 itself does not escape, but contents do.
+			slice2 := n.List.Next.N
+			escassign(e, &e.theSink, addDereference(slice2)) // lose track of assign of dereference
+			if Debug['m'] > 2 {
+				Warnl(int(n.Lineno), "%v special treatment of append(slice1, slice2...) %v", curfnSym(n), Nconv(n, obj.FmtShort))
+			}
 		}
+		escassign(e, &e.theSink, addDereference(n.List.N)) // The original elements are now leaked, too
 
 	case OCONV, OCONVNOP:
 		escassign(e, n, n.Left)
@@ -776,19 +783,15 @@ func esc(e *EscState, n *Node, up *Node) {
 
 	case OARRAYLIT:
 		if Isslice(n.Type) {
-			n.Esc = EscNone // until proven otherwise
+			// Slice itself is not leaked until proven otherwise
+			n.Esc = EscNone
 			e.noesc = list(e.noesc, n)
 			n.Escloopdepth = e.loopdepth
+		}
 
-			// Values make it to memory, lose track.
-			for ll := n.List; ll != nil; ll = ll.Next {
-				escassign(e, &e.theSink, ll.N.Right)
-			}
-		} else {
-			// Link values to array.
-			for ll := n.List; ll != nil; ll = ll.Next {
-				escassign(e, n, ll.N.Right)
-			}
+		// Link values to array/slice
+		for ll := n.List; ll != nil; ll = ll.Next {
+			escassign(e, n, ll.N.Right)
 		}
 
 		// Link values to struct.
@@ -909,14 +912,8 @@ func escassign(e *EscState, dst *Node, src *Node) {
 	}
 
 	if Debug['m'] > 1 {
-		var tmp *Sym
-		if Curfn != nil && Curfn.Nname != nil {
-			tmp = Curfn.Nname.Sym
-		} else {
-			tmp = nil
-		}
 		fmt.Printf("%v:[%d] %v escassign: %v(%v)[%v] = %v(%v)[%v]\n",
-			Ctxt.Line(int(lineno)), e.loopdepth, tmp,
+			Ctxt.Line(int(lineno)), e.loopdepth, funcSym(Curfn),
 			Nconv(dst, obj.FmtShort), Jconv(dst, obj.FmtShort), Oconv(int(dst.Op), 0),
 			Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), Oconv(int(src.Op), 0))
 	}
@@ -1038,12 +1035,15 @@ func escassign(e *EscState, dst *Node, src *Node) {
 
 	case OAPPEND:
 		// Append returns first argument.
+		// Subsequent arguments are already leaked because they are operands to append.
 		escassign(e, dst, src.List.N)
 
 	case OINDEX:
 		// Index of array preserves input value.
 		if Isfixedarray(src.Left.Type) {
 			escassign(e, dst, src.Left)
+		} else {
+			escflows(e, dst, src)
 		}
 
 		// Might be pointer arithmetic, in which case
@@ -1510,13 +1510,7 @@ func escflood(e *EscState, dst *Node) {
 	}
 
 	if Debug['m'] > 1 {
-		var tmp *Sym
-		if dst.Curfn != nil && dst.Curfn.Nname != nil {
-			tmp = dst.Curfn.Nname.Sym
-		} else {
-			tmp = nil
-		}
-		fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), tmp, dst.Escloopdepth)
+		fmt.Printf("\nescflood:%d: dst %v scope:%v[%d]\n", walkgen, Nconv(dst, obj.FmtShort), curfnSym(dst), dst.Escloopdepth)
 	}
 
 	for l := dst.Escflowsrc; l != nil; l = l.Next {
@@ -1548,14 +1542,8 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
 	src.Esclevel = level
 
 	if Debug['m'] > 1 {
-		var tmp *Sym
-		if src.Curfn != nil && src.Curfn.Nname != nil {
-			tmp = src.Curfn.Nname.Sym
-		} else {
-			tmp = nil
-		}
 		fmt.Printf("escwalk: level:%d depth:%d %.*s op=%v %v(%v) scope:%v[%d]\n",
-			level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), tmp, src.Escloopdepth)
+			level, e.pdepth, e.pdepth, "\t\t\t\t\t\t\t\t\t\t", Oconv(int(src.Op), 0), Nconv(src, obj.FmtShort), Jconv(src, obj.FmtShort), curfnSym(src), src.Escloopdepth)
 	}
 
 	e.pdepth++
@@ -1657,6 +1645,10 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) {
 		if Isfixedarray(src.Type) {
 			break
 		}
+		for ll := src.List; ll != nil; ll = ll.Next {
+			escwalk(e, level.dec(), dst, ll.N.Right)
+		}
+
 		fallthrough
 
 	case ODDDARG,
diff --git a/test/escape2.go b/test/escape2.go
index cc714711cf..dfc37ed45f 100644
--- a/test/escape2.go
+++ b/test/escape2.go
@@ -787,7 +787,7 @@ func foo93(c chan *int) *int { // ERROR "foo93 c does not escape$"
 }
 
 // does not leak m
-func foo94(m map[*int]*int, b bool) *int { // ERROR "foo94 m does not escape$"
+func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r2 level=1"
 	for k, v := range m {
 		if b {
 			return k
@@ -802,8 +802,8 @@ func foo95(m map[*int]*int, x *int) { // ERROR "foo95 m does not escape$" "leaki
 	m[x] = x
 }
 
-// does not leak m
-func foo96(m []*int) *int { // ERROR "foo96 m does not escape$"
+// does not leak m but does leak content
+func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
 	return m[0]
 }
 
@@ -823,7 +823,7 @@ func foo99(m *[1]*int) []*int { // ERROR "leaking param: m to result ~r1 level=0
 }
 
 // does not leak m
-func foo100(m []*int) *int { // ERROR "foo100 m does not escape$"
+func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
 	for _, v := range m {
 		return v
 	}
@@ -863,8 +863,8 @@ func foo104(x []*int) { // ERROR "foo104 x does not escape$"
 	copy(y, x)
 }
 
-// does not leak x
-func foo105(x []*int) { // ERROR "foo105 x does not escape$"
+// does not leak x but does leak content
+func foo105(x []*int) { // ERROR "leaking param content: x"
 	_ = append(y, x...)
 }
 
@@ -894,7 +894,7 @@ func foo110(x *int) *int { // ERROR "leaking param: x$"
 	return m[nil]
 }
 
-func foo111(x *int) *int { // ERROR "leaking param: x$"
+func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
 	m := []*int{x} // ERROR "foo111 \[\]\*int literal does not escape$"
 	return m[0]
 }
diff --git a/test/escape2n.go b/test/escape2n.go
index bf8c534a91..56f05eba30 100644
--- a/test/escape2n.go
+++ b/test/escape2n.go
@@ -787,7 +787,7 @@ func foo93(c chan *int) *int { // ERROR "foo93 c does not escape$"
 }
 
 // does not leak m
-func foo94(m map[*int]*int, b bool) *int { // ERROR "foo94 m does not escape$"
+func foo94(m map[*int]*int, b bool) *int { // ERROR "leaking param: m to result ~r2 level=1"
 	for k, v := range m {
 		if b {
 			return k
@@ -802,8 +802,8 @@ func foo95(m map[*int]*int, x *int) { // ERROR "foo95 m does not escape$" "leaki
 	m[x] = x
 }
 
-// does not leak m
-func foo96(m []*int) *int { // ERROR "foo96 m does not escape$"
+// does not leak m but does leak content
+func foo96(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
 	return m[0]
 }
 
@@ -823,7 +823,7 @@ func foo99(m *[1]*int) []*int { // ERROR "leaking param: m to result ~r1 level=0
 }
 
 // does not leak m
-func foo100(m []*int) *int { // ERROR "foo100 m does not escape$"
+func foo100(m []*int) *int { // ERROR "leaking param: m to result ~r1 level=1"
 	for _, v := range m {
 		return v
 	}
@@ -863,8 +863,8 @@ func foo104(x []*int) { // ERROR "foo104 x does not escape$"
 	copy(y, x)
 }
 
-// does not leak x
-func foo105(x []*int) { // ERROR "foo105 x does not escape$"
+// does not leak x but does leak content
+func foo105(x []*int) { // ERROR "leaking param content: x"
 	_ = append(y, x...)
 }
 
@@ -894,7 +894,7 @@ func foo110(x *int) *int { // ERROR "leaking param: x$"
 	return m[nil]
 }
 
-func foo111(x *int) *int { // ERROR "leaking param: x$"
+func foo111(x *int) *int { // ERROR "leaking param: x to result ~r1 level=0"
 	m := []*int{x} // ERROR "foo111 \[\]\*int literal does not escape$"
 	return m[0]
 }
diff --git a/test/escape_slice.go b/test/escape_slice.go
index 9315e27682..0b6599719d 100644
--- a/test/escape_slice.go
+++ b/test/escape_slice.go
@@ -8,6 +8,11 @@
 
 package escape
 
+import (
+	"os"
+	"strings"
+)
+
 var sink interface{}
 
 func slice0() {
@@ -71,9 +76,8 @@ func slice7() *int {
 }
 
 func slice8() {
-	// BAD: i should not escape here
-	i := 0          // ERROR "moved to heap: i"
-	s := []*int{&i} // ERROR "&i escapes to heap" "literal does not escape"
+	i := 0
+	s := []*int{&i} // ERROR "&i does not escape" "literal does not escape"
 	_ = s
 }
 
@@ -88,3 +92,74 @@ func slice10() []*int {
 	s := []*int{&i} // ERROR "&i escapes to heap" "literal escapes to heap"
 	return s
 }
+
+func envForDir(dir string) []string { // ERROR "dir does not escape"
+	env := os.Environ()
+	return mergeEnvLists([]string{"PWD=" + dir}, env) // ERROR ".PWD=. \+ dir escapes to heap" "\[\]string literal does not escape"
+}
+
+func mergeEnvLists(in, out []string) []string { // ERROR "leaking param content: in" "leaking param content: out" "leaking param: out to result ~r2 level=0"
+NextVar:
+	for _, inkv := range in {
+		k := strings.SplitAfterN(inkv, "=", 2)[0]
+		for i, outkv := range out {
+			if strings.HasPrefix(outkv, k) {
+				out[i] = inkv
+				continue NextVar
+			}
+		}
+		out = append(out, inkv)
+	}
+	return out
+}
+
+const (
+	IPv4len = 4
+	IPv6len = 16
+)
+
+var v4InV6Prefix = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff}
+
+func IPv4(a, b, c, d byte) IP {
+	p := make(IP, IPv6len) // ERROR "make\(IP, IPv6len\) escapes to heap"
+	copy(p, v4InV6Prefix)
+	p[12] = a
+	p[13] = b
+	p[14] = c
+	p[15] = d
+	return p
+}
+
+type IP []byte
+
+type IPAddr struct {
+	IP   IP
+	Zone string // IPv6 scoped addressing zone
+}
+
+type resolveIPAddrTest struct {
+	network       string
+	litAddrOrName string
+	addr          *IPAddr
+	err           error
+}
+
+var resolveIPAddrTests = []resolveIPAddrTest{
+	{"ip", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+	{"ip4", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+	{"ip4:icmp", "127.0.0.1", &IPAddr{IP: IPv4(127, 0, 0, 1)}, nil},
+}
+
+func setupTestData() {
+	resolveIPAddrTests = append(resolveIPAddrTests,
+		[]resolveIPAddrTest{ // ERROR "\[\]resolveIPAddrTest literal does not escape"
+			{"ip",
+				"localhost",
+				&IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+				nil},
+			{"ip4",
+				"localhost",
+				&IPAddr{IP: IPv4(127, 0, 0, 1)}, // ERROR "&IPAddr literal escapes to heap"
+				nil},
+		}...)
+}

From f9ec929aafeec42eea7234c1fa6c6c817c7a6548 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Mon, 18 May 2015 11:18:58 -0700
Subject: [PATCH 136/232] spec: fix typo

Fixes #10893.

Change-Id: I8afeb55acda1e1c8e181379dbaf443716d63ded1
Reviewed-on: https://go-review.googlesource.com/10201
Reviewed-by: Rob Pike 
---
 doc/go_spec.html | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/doc/go_spec.html b/doc/go_spec.html
index 4e2f911388..cdcca6be57 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,6 +1,6 @@
 
 
@@ -2605,7 +2605,7 @@ one may write:
 
 t.z          // t.z
 t.y          // t.T1.y
-t.x          // (*t.TO).x
+t.x          // (*t.T0).x
 
 p.z          // (*p).z
 p.y          // (*p).T1.y

From 362a40e37da849696e1af6874d0d5f1275a1a386 Mon Sep 17 00:00:00 2001
From: Michael Hudson-Doyle 
Date: Thu, 7 May 2015 21:29:47 +1200
Subject: [PATCH 137/232] misc/cgo/testshared: rewrite in Go

And fix to work on filesystems with only 1s resolution.

Fixes #10724

Change-Id: Ia07463f090b4290fc27f5953fa94186463d7afc7
Reviewed-on: https://go-review.googlesource.com/9768
Reviewed-by: Brad Fitzpatrick 
---
 misc/cgo/testshared/shared_test.go | 364 +++++++++++++++++++++++++++++
 misc/cgo/testshared/test.bash      | 137 -----------
 src/cmd/dist/test.go               |   2 +-
 3 files changed, 365 insertions(+), 138 deletions(-)
 create mode 100644 misc/cgo/testshared/shared_test.go
 delete mode 100755 misc/cgo/testshared/test.bash

diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
new file mode 100644
index 0000000000..81b9dffb07
--- /dev/null
+++ b/misc/cgo/testshared/shared_test.go
@@ -0,0 +1,364 @@
+// Copyright 2015 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package shared_test
+
+import (
+	"bufio"
+	"bytes"
+	"debug/elf"
+	"errors"
+	"flag"
+	"fmt"
+	"go/build"
+	"io/ioutil"
+	"log"
+	"math/rand"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"strings"
+	"testing"
+	"time"
+)
+
+var gopathInstallDir, gorootInstallDir, suffix string
+
+// This is the smallest set of packages we can link into a shared
+// library (runtime/cgo is built implicitly).
+var minpkgs = []string{"runtime", "sync/atomic"}
+var soname = "libruntime,sync-atomic.so"
+
+// run runs a command and calls t.Errorf if it fails.
+func run(t *testing.T, msg string, args ...string) {
+	c := exec.Command(args[0], args[1:]...)
+	if output, err := c.CombinedOutput(); err != nil {
+		t.Errorf("executing %s (%s) failed %s:\n%s", strings.Join(args, " "), msg, err, output)
+	}
+}
+
+// goCmd invokes the go tool with the installsuffix set up by TestMain. It calls
+// t.Errorf if the command fails.
+func goCmd(t *testing.T, args ...string) {
+	newargs := []string{args[0], "-installsuffix=" + suffix}
+	if testing.Verbose() {
+		newargs = append(newargs, "-v")
+	}
+	newargs = append(newargs, args[1:]...)
+	c := exec.Command("go", newargs...)
+	if testing.Verbose() {
+		fmt.Printf("+ go %s\n", strings.Join(newargs, " "))
+	}
+	if output, err := c.CombinedOutput(); err != nil {
+		if t != nil {
+			t.Errorf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output)
+		} else {
+			log.Fatalf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output)
+		}
+	}
+}
+
+// TestMain calls testMain so that the latter can use defer (TestMain exits with os.Exit).
+func testMain(m *testing.M) (int, error) {
+	// Because go install -buildmode=shared $standard_library_package always
+	// installs into $GOROOT, here are some gymnastics to come up with a unique
+	// installsuffix to use in this test that we can clean up afterwards.
+	myContext := build.Default
+	runtimeP, err := myContext.Import("runtime", ".", build.ImportComment)
+	if err != nil {
+		return 0, fmt.Errorf("import failed: %v", err)
+	}
+	for i := 0; i < 10000; i++ {
+		try := fmt.Sprintf("%s_%d_dynlink", runtimeP.PkgTargetRoot, rand.Int63())
+		err = os.Mkdir(try, 0700)
+		if os.IsExist(err) {
+			continue
+		}
+		if err == nil {
+			gorootInstallDir = try
+		}
+		break
+	}
+	if err != nil {
+		return 0, fmt.Errorf("can't create temporary directory: %v", err)
+	}
+	if gorootInstallDir == "" {
+		return 0, errors.New("could not create temporary directory after 10000 tries")
+	}
+	defer os.RemoveAll(gorootInstallDir)
+
+	// Some tests need to edit the source in GOPATH, so copy this directory to a
+	// temporary directory and chdir to that.
+	scratchDir, err := ioutil.TempDir("", "testshared")
+	if err != nil {
+		return 0, fmt.Errorf("TempDir failed: %v", err)
+	}
+	defer os.RemoveAll(scratchDir)
+	err = filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
+		scratchPath := filepath.Join(scratchDir, path)
+		if info.IsDir() {
+			if path == "." {
+				return nil
+			}
+			return os.Mkdir(scratchPath, info.Mode())
+		} else {
+			fromBytes, err := ioutil.ReadFile(path)
+			if err != nil {
+				return err
+			}
+			return ioutil.WriteFile(scratchPath, fromBytes, info.Mode())
+		}
+	})
+	if err != nil {
+		return 0, fmt.Errorf("walk failed: %v", err)
+	}
+	os.Setenv("GOPATH", scratchDir)
+	myContext.GOPATH = scratchDir
+	os.Chdir(scratchDir)
+
+	// All tests depend on runtime being built into a shared library. Because
+	// that takes a few seconds, do it here and have all tests use the version
+	// built here.
+	suffix = strings.Split(filepath.Base(gorootInstallDir), "_")[2]
+	goCmd(nil, append([]string{"install", "-buildmode=shared"}, minpkgs...)...)
+
+	myContext.InstallSuffix = suffix + "_dynlink"
+	depP, err := myContext.Import("dep", ".", build.ImportComment)
+	if err != nil {
+		return 0, fmt.Errorf("import failed: %v", err)
+	}
+	gopathInstallDir = depP.PkgTargetRoot
+	return m.Run(), nil
+}
+
+func TestMain(m *testing.M) {
+	flag.Parse()
+	exitCode, err := testMain(m)
+	if err != nil {
+		log.Fatal(err)
+	}
+	os.Exit(exitCode)
+}
+
+// The shared library was built at the expected location.
+func TestSOBuilt(t *testing.T) {
+	_, err := os.Stat(filepath.Join(gorootInstallDir, soname))
+	if err != nil {
+		t.Error(err)
+	}
+}
+
+// The install command should have created a "shlibname" file for the
+// listed packages (and runtime/cgo) indicating the name of the shared
+// library containing it.
+func TestShlibnameFiles(t *testing.T) {
+	pkgs := append([]string{}, minpkgs...)
+	pkgs = append(pkgs, "runtime/cgo")
+	for _, pkg := range pkgs {
+		shlibnamefile := filepath.Join(gorootInstallDir, pkg+".shlibname")
+		contentsb, err := ioutil.ReadFile(shlibnamefile)
+		if err != nil {
+			t.Errorf("error reading shlibnamefile for %s: %v", pkg, err)
+			continue
+		}
+		contents := strings.TrimSpace(string(contentsb))
+		if contents != soname {
+			t.Errorf("shlibnamefile for %s has wrong contents: %q", pkg, contents)
+		}
+	}
+}
+
+func dynStrings(path string, flag elf.DynTag) []string {
+	f, err := elf.Open(path)
+	defer f.Close()
+	if err != nil {
+		log.Fatal("elf.Open failed: ", err)
+	}
+	dynstrings, err := f.DynString(flag)
+	if err != nil {
+		log.Fatal("dynstring failed: ", err)
+	}
+	return dynstrings
+}
+
+func AssertIsLinkedTo(t *testing.T, path, lib string) {
+	for _, dynstring := range dynStrings(path, elf.DT_NEEDED) {
+		if dynstring == lib {
+			return
+		}
+	}
+	t.Errorf("%s is not linked to %s", path, lib)
+}
+
+func AssertHasRPath(t *testing.T, path, dir string) {
+	for _, dynstring := range dynStrings(path, elf.DT_RPATH) {
+		for _, rpath := range strings.Split(dynstring, ":") {
+			if filepath.Clean(rpath) == filepath.Clean(dir) {
+				return
+			}
+		}
+	}
+	t.Errorf("%s does not have rpath %s", path, dir)
+}
+
+// Build a trivial program that links against the shared runtime and check it runs.
+func TestTrivialExecutable(t *testing.T) {
+	goCmd(t, "install", "-linkshared", "trivial")
+	run(t, "trivial executable", "./bin/trivial")
+	AssertIsLinkedTo(t, "./bin/trivial", soname)
+	AssertHasRPath(t, "./bin/trivial", gorootInstallDir)
+}
+
+// Build a GOPATH package into a shared library that links against the goroot runtime
+// and an executable that links against both.
+func TestGOPathShlib(t *testing.T) {
+	goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+	AssertIsLinkedTo(t, filepath.Join(gopathInstallDir, "libdep.so"), soname)
+	goCmd(t, "install", "-linkshared", "exe")
+	AssertIsLinkedTo(t, "./bin/exe", soname)
+	AssertIsLinkedTo(t, "./bin/exe", "libdep.so")
+	AssertHasRPath(t, "./bin/exe", gorootInstallDir)
+	AssertHasRPath(t, "./bin/exe", gopathInstallDir)
+	// And check it runs.
+	run(t, "executable linked to GOPATH library", "./bin/exe")
+}
+
+// Testing rebuilding of shared libraries when they are stale is a bit more
+// complicated that it seems like it should be. First, we make everything "old": but
+// only a few seconds old, or it might be older than 6g (or the runtime source) and
+// everything will get rebuilt. Then define a timestamp slightly newer than this
+// time, which is what we set the mtime to of a file to cause it to be seen as new,
+// and finally another slightly even newer one that we can compare files against to
+// see if they have been rebuilt.
+var oldTime = time.Now().Add(-9 * time.Second)
+var nearlyNew = time.Now().Add(-6 * time.Second)
+var stampTime = time.Now().Add(-3 * time.Second)
+
+// resetFileStamps makes "everything" (bin, src, pkg from GOPATH and the
+// test-specific parts of GOROOT) appear old.
+func resetFileStamps() {
+	chtime := func(path string, info os.FileInfo, err error) error {
+		return os.Chtimes(path, oldTime, oldTime)
+	}
+	reset := func(path string) {
+		if err := filepath.Walk(path, chtime); err != nil {
+			log.Fatalf("resetFileStamps failed: %v", err)
+		}
+
+	}
+	reset("bin")
+	reset("pkg")
+	reset("src")
+	reset(gorootInstallDir)
+}
+
+// touch makes path newer than the "old" time stamp used by resetFileStamps.
+func touch(path string) {
+	if err := os.Chtimes(path, nearlyNew, nearlyNew); err != nil {
+		log.Fatalf("os.Chtimes failed: %v", err)
+	}
+}
+
+// isNew returns if the path is newer than the time stamp used by touch.
+func isNew(path string) bool {
+	fi, err := os.Stat(path)
+	if err != nil {
+		log.Fatalf("os.Stat failed: %v", err)
+	}
+	return fi.ModTime().After(stampTime)
+}
+
+// Fail unless path has been rebuilt (i.e. is newer than the time stamp used by
+// isNew)
+func AssertRebuilt(t *testing.T, msg, path string) {
+	if !isNew(path) {
+		t.Errorf("%s was not rebuilt (%s)", msg, path)
+	}
+}
+
+// Fail if path has been rebuilt (i.e. is newer than the time stamp used by isNew)
+func AssertNotRebuilt(t *testing.T, msg, path string) {
+	if isNew(path) {
+		t.Errorf("%s was rebuilt (%s)", msg, path)
+	}
+}
+
+func TestRebuilding(t *testing.T) {
+	goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+	goCmd(t, "install", "-linkshared", "exe")
+
+	// If the source is newer than both the .a file and the .so, both are rebuilt.
+	resetFileStamps()
+	touch("src/dep/dep.go")
+	goCmd(t, "install", "-linkshared", "exe")
+	AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "dep.a"))
+	AssertRebuilt(t, "new source", filepath.Join(gopathInstallDir, "libdep.so"))
+
+	// If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
+	resetFileStamps()
+	touch(filepath.Join(gopathInstallDir, "dep.a"))
+	goCmd(t, "install", "-linkshared", "exe")
+	AssertNotRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "dep.a"))
+	AssertRebuilt(t, "new .a file", filepath.Join(gopathInstallDir, "libdep.so"))
+}
+
+func appendFile(path, content string) {
+	f, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND, 0660)
+	if err != nil {
+		log.Fatalf("os.OpenFile failed: %v", err)
+	}
+	defer func() {
+		err := f.Close()
+		if err != nil {
+			log.Fatalf("f.Close failed: %v", err)
+		}
+	}()
+	_, err = f.WriteString(content)
+	if err != nil {
+		log.Fatalf("f.WriteString failed: %v", err)
+	}
+}
+
+func TestABIChecking(t *testing.T) {
+	goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+	goCmd(t, "install", "-linkshared", "exe")
+
+	// If we make an ABI-breaking change to dep and rebuild libp.so but not exe,
+	// exe will abort with a complaint on startup.
+	// This assumes adding an exported function breaks ABI, which is not true in
+	// some senses but suffices for the narrow definition of ABI compatiblity the
+	// toolchain uses today.
+	appendFile("src/dep/dep.go", "func ABIBreak() {}\n")
+	goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+	c := exec.Command("./bin/exe")
+	output, err := c.CombinedOutput()
+	if err == nil {
+		t.Fatal("executing exe did not fail after ABI break")
+	}
+	scanner := bufio.NewScanner(bytes.NewReader(output))
+	foundMsg := false
+	const wantLine = "abi mismatch detected between the executable and libdep.so"
+	for scanner.Scan() {
+		if scanner.Text() == wantLine {
+			foundMsg = true
+			break
+		}
+	}
+	if err = scanner.Err(); err != nil {
+		t.Errorf("scanner encountered error: %v", err)
+	}
+	if !foundMsg {
+		t.Fatalf("exe failed, but without line %q; got output:\n%s", wantLine, output)
+	}
+
+	// Rebuilding exe makes it work again.
+	goCmd(t, "install", "-linkshared", "exe")
+	run(t, "rebuilt exe", "./bin/exe")
+
+	// If we make a change which does not break ABI (such as adding an unexported
+	// function) and rebuild libdep.so, exe still works.
+	appendFile("src/dep/dep.go", "func noABIBreak() {}\n")
+	goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep")
+	run(t, "after non-ABI breaking change", "./bin/exe")
+}
diff --git a/misc/cgo/testshared/test.bash b/misc/cgo/testshared/test.bash
deleted file mode 100755
index 0d67ff1719..0000000000
--- a/misc/cgo/testshared/test.bash
+++ /dev/null
@@ -1,137 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2015 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Test that -buildmode=shared can produce a shared library and that
-# -linkshared can link against it to produce a working executable.
-
-set -eu
-
-
-die () {
-    echo $@
-    exit 1
-}
-
-# Because go install -buildmode=shared $standard_library_package always
-# installs into $GOROOT, here are some gymnastics to come up with a
-# unique installsuffix to use in this test that we can clean up
-# afterwards.
-rootdir="$(dirname $(go list -f '{{.Target}}' runtime))"
-template="${rootdir}_XXXXXXXX_dynlink"
-std_install_dir=$(mktemp -d "$template")
-
-scratch_dir=$(mktemp -d)
-cp -a . $scratch_dir
-opwd="$(pwd)"
-cd $scratch_dir
-export GOPATH="$(pwd)"
-
-cleanup () {
-    rm -rf $std_install_dir $scratch_dir
-}
-trap cleanup EXIT
-
-mysuffix=$(echo $std_install_dir | sed -e 's/.*_\([^_]*\)_dynlink/\1/')
-
-# This is the smallest set of packages we can link into a shared
-# library (runtime/cgo is built implicitly). Check they are built into
-# a library with the expected name.
-minpkgs="runtime sync/atomic"
-soname=libruntime,sync-atomic.so
-
-go install -installsuffix="$mysuffix" -buildmode=shared $minpkgs || die "install -buildmode=shared failed"
-
-if [ ! -f "$std_install_dir/$soname" ]; then
-    echo "$std_install_dir/$soname not found!"
-    exit 1
-fi
-
-# The install command should have created a "shlibname" file for the
-# listed packages (and runtime/cgo) indicating the name of the shared
-# library containing it.
-for pkg in $minpkgs runtime/cgo; do
-    if [ ! -f "$std_install_dir/$pkg.shlibname" ]; then
-        die "no shlibname file for $pkg"
-    fi
-    if [ "$(cat "$std_install_dir/$pkg.shlibname")" != "$soname" ]; then
-        die "shlibname file for $pkg has wrong contents"
-    fi
-done
-
-# Build a trivial program that links against the shared library we
-# just made and check it runs.
-go install -installsuffix="$mysuffix" -linkshared trivial || die "build -linkshared failed"
-./bin/trivial || die "./bin/trivial failed"
-
-# And check that it is actually dynamically linked against the library
-# we hope it is linked against.
-
-ensure_ldd () {
-    a="$(ldd $1)" || die "ldd $1 failed: $a"
-    { echo "$a" | grep -q "$2"; } || die "$1 does not appear to be linked against $2"
-}
-
-ensure_ldd ./bin/trivial $std_install_dir/$soname
-
-# Build a GOPATH package into a shared library that links against the above one.
-rootdir="$(dirname $(go list -installsuffix="$mysuffix" -linkshared -f '{{.Target}}' dep))"
-go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
-ensure_ldd $rootdir/libdep.so $std_install_dir/$soname
-
-
-# And exe that links against both
-go install -installsuffix="$mysuffix" -linkshared exe
-./bin/exe || die "./bin/exe failed with code $?"
-ensure_ldd ./bin/exe $rootdir/libdep.so
-ensure_ldd ./bin/exe $std_install_dir/$soname
-
-# Now, test rebuilding of shared libraries when they are stale.
-
-will_check_rebuilt () {
-    for f in $@; do cp $f $f.bak; done
-}
-
-assert_rebuilt () {
-    find $1 -newer $1.bak | grep -q . || die "$1 was not rebuilt"
-}
-
-assert_not_rebuilt () {
-    find $1 -newer $1.bak | grep  . && die "$1 was rebuilt" || true
-}
-
-# If the source is newer than both the .a file and the .so, both are rebuilt.
-touch src/dep/dep.go
-will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
-go install -installsuffix="$mysuffix" -linkshared exe
-assert_rebuilt $rootdir/dep.a
-assert_rebuilt $rootdir/libdep.so
-
-# If the .a file is newer than the .so, the .so is rebuilt (but not the .a)
-touch $rootdir/dep.a
-will_check_rebuilt $rootdir/libdep.so $rootdir/dep.a
-go install  -installsuffix="$mysuffix" -linkshared exe
-assert_not_rebuilt $rootdir/dep.a
-assert_rebuilt $rootdir/libdep.so
-
-# If we make an ABI-breaking change to dep and rebuild libp.so but not exe, exe will
-# abort with a complaint on startup.
-# This assumes adding an exported function breaks ABI, which is not true in some
-# senses but suffices for the narrow definition of ABI compatiblity the toolchain
-# uses today.
-echo "func ABIBreak() {}" >> src/dep/dep.go
-go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
-output="$(./bin/exe 2>&1)" && die "exe succeeded after ABI break" || true
-msg="abi mismatch detected between the executable and libdep.so"
-{ echo "$output" | grep -q "$msg"; } || die "exe did not fail with expected message"
-
-# Rebuilding exe makes it work again.
-go install -installsuffix="$mysuffix" -linkshared exe
-./bin/exe || die "exe failed after rebuild"
-
-# If we make a change which does not break ABI (such as adding an
-# unexported function) and rebuild libdep.so, exe still works.
-echo "func noABIBreak() {}" >> src/dep/dep.go
-go install -installsuffix="$mysuffix" -buildmode=shared -linkshared dep
-./bin/exe || die "exe failed after non-ABI breaking change"
diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go
index 1f26eef5ee..0b6b592eef 100644
--- a/src/cmd/dist/test.go
+++ b/src/cmd/dist/test.go
@@ -324,7 +324,7 @@ func (t *tester) registerTests() {
 			t.registerTest("testcshared", "../misc/cgo/testcshared", "./test.bash")
 		}
 		if t.supportedBuildmode("shared") {
-			t.registerTest("testshared", "../misc/cgo/testshared", "./test.bash")
+			t.registerTest("testshared", "../misc/cgo/testshared", "go", "test")
 		}
 		if t.gohostos == "linux" && t.goarch == "amd64" {
 			t.registerTest("testasan", "../misc/cgo/testasan", "go", "run", "main.go")

From 6f7b4e893871775b74e57d5d048ff0565d32ef79 Mon Sep 17 00:00:00 2001
From: Rob Pike 
Date: Fri, 15 May 2015 13:30:42 -0700
Subject: [PATCH 138/232] cmd/doc: put blank lines around comment for types,
 etc.

Better layout.

Fixes #10859.

The issue suggests rearranging so the comment comes out
after the methods. I tried this and it looks good but it is less
useful, since the stuff you're probably looking for - the methods
- are scrolled away by the comment. The most important
information should be last because that leaves it on your
screen after the print if the output is long.

Change-Id: I560f992601ccbe2293c347fa1b1018a3f5346c82
Reviewed-on: https://go-review.googlesource.com/10160
Reviewed-by: Russ Cox 
---
 src/cmd/doc/pkg.go | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/cmd/doc/pkg.go b/src/cmd/doc/pkg.go
index ed4b0b82db..5c8976b663 100644
--- a/src/cmd/doc/pkg.go
+++ b/src/cmd/doc/pkg.go
@@ -115,7 +115,7 @@ func (pkg *Package) emit(comment string, node ast.Node) {
 			log.Fatal(err)
 		}
 		if comment != "" {
-			pkg.newlines(1)
+			pkg.newlines(2) // Guarantee blank line before comment.
 			doc.ToText(&pkg.buf, comment, "    ", "\t", 80)
 		}
 		pkg.newlines(1)
@@ -352,6 +352,9 @@ func (pkg *Package) symbolDoc(symbol string) {
 		}
 		pkg.emit(typ.Doc, decl)
 		// Show associated methods, constants, etc.
+		if len(typ.Consts) > 0 || len(typ.Vars) > 0 || len(typ.Funcs) > 0 || len(typ.Methods) > 0 {
+			pkg.Printf("\n")
+		}
 		pkg.valueSummary(typ.Consts)
 		pkg.valueSummary(typ.Vars)
 		pkg.funcSummary(typ.Funcs)

From 19354b9dc87d62dbb5280354be7fd90bdf196a24 Mon Sep 17 00:00:00 2001
From: Daniel Morsing 
Date: Sat, 2 May 2015 13:08:12 +0100
Subject: [PATCH 139/232] cmd/pprof/internal/profile: ignore comments when
 parsing heap profiles

Fixes #10659.

Change-Id: I22dc306ce6f398dd40010ac430928a718d67d466
Reviewed-on: https://go-review.googlesource.com/9623
Reviewed-by: Russ Cox 
---
 src/cmd/pprof/internal/profile/legacy_profile.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/cmd/pprof/internal/profile/legacy_profile.go b/src/cmd/pprof/internal/profile/legacy_profile.go
index bfc8110e45..e4c92cdd19 100644
--- a/src/cmd/pprof/internal/profile/legacy_profile.go
+++ b/src/cmd/pprof/internal/profile/legacy_profile.go
@@ -554,9 +554,10 @@ func parseHeap(b []byte) (p *Profile, err error) {
 			}
 		}
 
-		if l = strings.TrimSpace(l); l == "" {
+		if isSpaceOrComment(l) {
 			continue
 		}
+		l = strings.TrimSpace(l)
 
 		if sectionTrigger(l) != unrecognizedSection {
 			break

From 79986e24e0152ba448fd41d65eeb24ebdb6c7ec7 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Mon, 18 May 2015 19:38:56 +0000
Subject: [PATCH 140/232] runtime/pprof: write heap statistics to heap profile
 always

This is a duplicate of CL 9491.
That CL broke the build due to pprof shortcomings
and was reverted in CL 9565.

CL 9623 fixed pprof, so this can go in again.

Fixes #10659.

Change-Id: If470fc90b3db2ade1d161b4417abd2f5c6c330b8
Reviewed-on: https://go-review.googlesource.com/10212
Reviewed-by: Matthew Dempsky 
---
 doc/go1.5.txt              |  1 +
 src/runtime/pprof/pprof.go | 50 ++++++++++++++++++--------------------
 2 files changed, 25 insertions(+), 26 deletions(-)

diff --git a/doc/go1.5.txt b/doc/go1.5.txt
index 10095d0c18..54a4c6e349 100644
--- a/doc/go1.5.txt
+++ b/doc/go1.5.txt
@@ -66,6 +66,7 @@ reflect: add ArrayOf (https://golang.org/cl/4111)
 reflect: add FuncOf (https://golang.org/cl/1996)
 runtime, syscall: use SYSCALL instruction on FreeBSD (Go 1.5 now requires FreeBSD 8-STABLE+) (https://golang.org/cl/3020)
 runtime, syscall: use get_random_bytes syscall for NaCl (Go 1.5 now requires NaCl SDK pepper-39 or above) (https://golang.org/cl/1755)
+runtime/pprof: memory profiles include overall memory statistics by default (https://golang.org/cl/9491)
 strings: add Compare(x, y string) int, for symmetry with bytes.Compare (https://golang.org/cl/2828)
 syscall: Add Foreground and Pgid to SysProcAttr (https://golang.org/cl/5130)
 syscall: add missing Syscall9 for darwin/amd64 (https://golang.org/cl/6555)
diff --git a/src/runtime/pprof/pprof.go b/src/runtime/pprof/pprof.go
index b3d0ae9b64..4290edb7be 100644
--- a/src/runtime/pprof/pprof.go
+++ b/src/runtime/pprof/pprof.go
@@ -442,35 +442,33 @@ func writeHeap(w io.Writer, debug int) error {
 
 	// Print memstats information too.
 	// Pprof will ignore, but useful for people
-	if debug > 0 {
-		s := new(runtime.MemStats)
-		runtime.ReadMemStats(s)
-		fmt.Fprintf(w, "\n# runtime.MemStats\n")
-		fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
-		fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
-		fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
-		fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
-		fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
-		fmt.Fprintf(w, "# Frees = %d\n", s.Frees)
+	s := new(runtime.MemStats)
+	runtime.ReadMemStats(s)
+	fmt.Fprintf(w, "\n# runtime.MemStats\n")
+	fmt.Fprintf(w, "# Alloc = %d\n", s.Alloc)
+	fmt.Fprintf(w, "# TotalAlloc = %d\n", s.TotalAlloc)
+	fmt.Fprintf(w, "# Sys = %d\n", s.Sys)
+	fmt.Fprintf(w, "# Lookups = %d\n", s.Lookups)
+	fmt.Fprintf(w, "# Mallocs = %d\n", s.Mallocs)
+	fmt.Fprintf(w, "# Frees = %d\n", s.Frees)
 
-		fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
-		fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
-		fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
-		fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
-		fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
-		fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)
+	fmt.Fprintf(w, "# HeapAlloc = %d\n", s.HeapAlloc)
+	fmt.Fprintf(w, "# HeapSys = %d\n", s.HeapSys)
+	fmt.Fprintf(w, "# HeapIdle = %d\n", s.HeapIdle)
+	fmt.Fprintf(w, "# HeapInuse = %d\n", s.HeapInuse)
+	fmt.Fprintf(w, "# HeapReleased = %d\n", s.HeapReleased)
+	fmt.Fprintf(w, "# HeapObjects = %d\n", s.HeapObjects)
 
-		fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
-		fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
-		fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
-		fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
+	fmt.Fprintf(w, "# Stack = %d / %d\n", s.StackInuse, s.StackSys)
+	fmt.Fprintf(w, "# MSpan = %d / %d\n", s.MSpanInuse, s.MSpanSys)
+	fmt.Fprintf(w, "# MCache = %d / %d\n", s.MCacheInuse, s.MCacheSys)
+	fmt.Fprintf(w, "# BuckHashSys = %d\n", s.BuckHashSys)
 
-		fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
-		fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
-		fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
-		fmt.Fprintf(w, "# EnableGC = %v\n", s.EnableGC)
-		fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
-	}
+	fmt.Fprintf(w, "# NextGC = %d\n", s.NextGC)
+	fmt.Fprintf(w, "# PauseNs = %d\n", s.PauseNs)
+	fmt.Fprintf(w, "# NumGC = %d\n", s.NumGC)
+	fmt.Fprintf(w, "# EnableGC = %v\n", s.EnableGC)
+	fmt.Fprintf(w, "# DebugGC = %v\n", s.DebugGC)
 
 	if tw != nil {
 		tw.Flush()

From 2b063bdff1a61961936db7ef1e963aecf1ae3db7 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Thu, 14 May 2015 19:33:31 -0700
Subject: [PATCH 141/232] cmd/internal/gc: make all Node depths int32

Funcdepth was already int32. Make Escloopdepth
and Decldepth also int32 instead of int.

No functional changes for non-absurd code. Passes toolstash -cmp.

Change-Id: I47e145dd732b6a73cfcc6d45956df0dbccdcd999
Reviewed-on: https://go-review.googlesource.com/10129
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/esc.go    | 2 +-
 src/cmd/internal/gc/go.go     | 2 +-
 src/cmd/internal/gc/syntax.go | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/cmd/internal/gc/esc.go b/src/cmd/internal/gc/esc.go
index a5b6a9b2b1..a9a1748b9a 100644
--- a/src/cmd/internal/gc/esc.go
+++ b/src/cmd/internal/gc/esc.go
@@ -379,7 +379,7 @@ type EscState struct {
 	theSink Node
 
 	dsts      *NodeList // all dst nodes
-	loopdepth int       // for detecting nested loop scopes
+	loopdepth int32     // for detecting nested loop scopes
 	pdepth    int       // for debug printing in recursions.
 	dstcount  int       // diagnostic
 	edgecount int       // diagnostic
diff --git a/src/cmd/internal/gc/go.go b/src/cmd/internal/gc/go.go
index 5fa85e25a7..6a3379b896 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/internal/gc/go.go
@@ -448,7 +448,7 @@ var nsavederrors int
 
 var nsyntaxerrors int
 
-var decldepth int
+var decldepth int32
 
 var safemode int
 
diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go
index 818d546970..50de7f74de 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/internal/gc/syntax.go
@@ -90,7 +90,7 @@ type Node struct {
 	// Escape analysis.
 	Escflowsrc   *NodeList // flow(this, src)
 	Escretval    *NodeList // on OCALLxxx, list of dummy return values
-	Escloopdepth int       // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
+	Escloopdepth int32     // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
 
 	Sym      *Sym  // various
 	Vargen   int32 // unique name for OTYPE/ONAME within a function.  Function outputs are numbered starting at one.
@@ -108,7 +108,7 @@ type Node struct {
 type Name struct {
 	Heapaddr  *Node // temp holding heap address of param
 	Inlvar    *Node // ONAME substitute while inlining
-	Decldepth int   // declaration loop depth, increased for every loop or label
+	Decldepth int32 // declaration loop depth, increased for every loop or label
 	Method    bool  // OCALLMETH name
 	Readonly  bool
 	Captured  bool // is the variable captured by a closure

From ddc93398b955a4d71683c8019d87d2ff9c739070 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Thu, 14 May 2015 19:50:41 -0700
Subject: [PATCH 142/232] cmd/6g, cmd/internal/gc: use Etype instead of Ostk

Change-Id: Ifda5d84b28717986c93b63767298180a6d6236c0
Reviewed-on: https://go-review.googlesource.com/10140
Reviewed-by: Russ Cox 
---
 src/cmd/6g/ggen.go            | 6 +++---
 src/cmd/internal/gc/syntax.go | 3 +--
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/cmd/6g/ggen.go b/src/cmd/6g/ggen.go
index 7282ac53e0..e0e1b8a4df 100644
--- a/src/cmd/6g/ggen.go
+++ b/src/cmd/6g/ggen.go
@@ -306,7 +306,7 @@ func dodiv(op int, nl *gc.Node, nr *gc.Node, res *gc.Node) {
  * known to be dead.
  */
 func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) {
-	r := int(reg[dr])
+	r := reg[dr]
 
 	// save current ax and dx if they are live
 	// and not the destination
@@ -318,7 +318,7 @@ func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) {
 		x.Type = gc.Types[gc.TINT64]
 		gmove(x, oldx)
 		x.Type = t
-		oldx.Ostk = int32(r) // squirrel away old r value
+		oldx.Etype = r // squirrel away old r value
 		reg[dr] = 1
 	}
 }
@@ -326,7 +326,7 @@ func savex(dr int, x *gc.Node, oldx *gc.Node, res *gc.Node, t *gc.Type) {
 func restx(x *gc.Node, oldx *gc.Node) {
 	if oldx.Op != 0 {
 		x.Type = gc.Types[gc.TINT64]
-		reg[x.Reg] = uint8(oldx.Ostk)
+		reg[x.Reg] = oldx.Etype
 		gmove(oldx, x)
 		gc.Regfree(oldx)
 	}
diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go
index 50de7f74de..d4ede60c90 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/internal/gc/syntax.go
@@ -27,7 +27,7 @@ type Node struct {
 	Nointerface bool
 	Ullman      uint8 // sethi/ullman number
 	Addable     bool  // addressable
-	Etype       uint8 // op for OASOP, etype for OTYPE, exclam for export
+	Etype       uint8 // op for OASOP, etype for OTYPE, exclam for export, 6g saved reg
 	Bounded     bool  // bounds check unnecessary
 	Class       uint8 // PPARAM, PAUTO, PEXTERN, etc
 	Embedded    uint8 // ODCLFIELD embedded type
@@ -97,7 +97,6 @@ type Node struct {
 	Lineno   int32
 	Xoffset  int64
 	Stkdelta int64 // offset added by stack frame compaction phase.
-	Ostk     int32 // 6g only
 	Iota     int32
 	Walkgen  uint32
 	Esclevel Level

From f4ab8203bab58b3c4ae53a99535719a747d05332 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Mon, 18 May 2015 10:27:59 -0700
Subject: [PATCH 143/232] cmd/internal/gc: separate Node param fields

Param will be converted from an anonymous to a
named field in a subsequent, automated CL.

Reduces Node size from 368 to 328.
Reduces inuse_space on the rotate tests by about 3%.

No functional changes. Passes toolstash -cmp.

Updates #9933.

Change-Id: I5867b00328abf17ee24aea6ca58876bae9d8bfed
Reviewed-on: https://go-review.googlesource.com/10210
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/export.go    |  2 +-
 src/cmd/internal/gc/subr.go      |  4 ++++
 src/cmd/internal/gc/syntax.go    | 26 +++++++++++++++-----------
 src/cmd/internal/gc/typecheck.go |  8 ++++----
 4 files changed, 24 insertions(+), 16 deletions(-)

diff --git a/src/cmd/internal/gc/export.go b/src/cmd/internal/gc/export.go
index 1efc8150c5..614de4e2ce 100644
--- a/src/cmd/internal/gc/export.go
+++ b/src/cmd/internal/gc/export.go
@@ -64,7 +64,7 @@ func autoexport(n *Node, ctxt uint8) {
 	if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN {
 		return
 	}
-	if n.Ntype != nil && n.Ntype.Op == OTFUNC && n.Ntype.Left != nil { // method
+	if n.Param != nil && n.Ntype != nil && n.Ntype.Op == OTFUNC && n.Ntype.Left != nil { // method
 		return
 	}
 
diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/internal/gc/subr.go
index 33741c3baf..b10a6b3d3d 100644
--- a/src/cmd/internal/gc/subr.go
+++ b/src/cmd/internal/gc/subr.go
@@ -371,8 +371,12 @@ func Nod(op int, nleft *Node, nright *Node) *Node {
 	switch op {
 	case OCLOSURE, ODCLFUNC:
 		n.Func = new(Func)
+		n.Param = new(Param)
 	case ONAME:
 		n.Name = new(Name)
+		n.Param = new(Param)
+	case ODCLFIELD:
+		n.Param = new(Param)
 	}
 	return n
 }
diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go
index d4ede60c90..d52a3d4fe7 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/internal/gc/syntax.go
@@ -65,21 +65,12 @@ type Node struct {
 
 	// ONAME
 	Name     *Name
-	Ntype    *Node
 	Defn     *Node // ONAME: initializing assignment; OLABEL: labeled statement
 	Pack     *Node // real package for import . names
 	Curfn    *Node // function for local variables
 	Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn
-
-	// ONAME func param with PHEAP
-	Outerexpr  *Node // expression copied into closure for variable
-	Stackparam *Node // OPARAM node referring to stack copy of param
-	Alloc      *Node // allocation call
-
-	// ONAME closure param with PPARAMREF
-	Outer   *Node // outer PPARAMREF in nested closure
-	Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF
-	Top     int   // top context (Ecall, Eproc, etc)
+	Alloc    *Node // allocation call
+	*Param
 
 	// OPACK
 	Pkg *Pkg
@@ -115,6 +106,19 @@ type Name struct {
 	Needzero  bool // if it contains pointers, needs to be zeroed on function entry
 }
 
+type Param struct {
+	Ntype *Node
+
+	// ONAME func param with PHEAP
+	Outerexpr  *Node // expression copied into closure for variable
+	Stackparam *Node // OPARAM node referring to stack copy of param
+
+	// ONAME closure param with PPARAMREF
+	Outer   *Node // outer PPARAMREF in nested closure
+	Closure *Node // ONAME/PHEAP <-> ONAME/PPARAMREF
+	Top     int   // top context (Ecall, Eproc, etc)
+}
+
 // Func holds Node fields used only with function-like nodes.
 type Func struct {
 	Shortname *Node
diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go
index 06f8b34305..8af9f084e2 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/internal/gc/typecheck.go
@@ -813,7 +813,7 @@ OpSwitch:
 		var l *Node
 		for l = n.Left; l != r; l = l.Left {
 			l.Addrtaken = true
-			if l.Closure != nil {
+			if l.Param != nil && l.Closure != nil {
 				l.Closure.Addrtaken = true
 			}
 		}
@@ -822,7 +822,7 @@ OpSwitch:
 			Fatal("found non-orig name node %v", l)
 		}
 		l.Addrtaken = true
-		if l.Closure != nil {
+		if l.Param != nil && l.Closure != nil {
 			l.Closure.Addrtaken = true
 		}
 		defaultlit(&n.Left, nil)
@@ -3273,13 +3273,13 @@ func checkassign(stmt *Node, n *Node) {
 		var l *Node
 		for l = n; l != r; l = l.Left {
 			l.Assigned = true
-			if l.Closure != nil {
+			if l.Param != nil && l.Closure != nil {
 				l.Closure.Assigned = true
 			}
 		}
 
 		l.Assigned = true
-		if l.Closure != nil {
+		if l.Param != nil && l.Closure != nil {
 			l.Closure.Assigned = true
 		}
 	}

From 82833b313e5e23f67d5ed1141d9a2464bf78f277 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Mon, 18 May 2015 15:49:02 -0700
Subject: [PATCH 144/232] cmd/internal/gc: rearrange Node fields

Rearrange Node fields to enable better struct packing.
This reduces readability in favor of shrinking
the size of Nodes.

This reduces the size of Node from 328 to 312.
This reduces the memory usage to compile the
rotate tests by about 4.4%.

No functional changes. Passes toolstash -cmp.

Updates #9933.

Change-Id: I2764c5847fb1635ddc898e2ee385d007d67f03c5
Reviewed-on: https://go-review.googlesource.com/10141
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/syntax.go | 108 ++++++++++++++++++----------------
 1 file changed, 58 insertions(+), 50 deletions(-)

diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/internal/gc/syntax.go
index d52a3d4fe7..69348d1c2f 100644
--- a/src/cmd/internal/gc/syntax.go
+++ b/src/cmd/internal/gc/syntax.go
@@ -23,6 +23,60 @@ type Node struct {
 	List  *NodeList
 	Rlist *NodeList
 
+	// most nodes
+	Type  *Type
+	Orig  *Node // original form, for printing, and tracking copies of ONAMEs
+	Nname *Node
+
+	// func
+	Func *Func
+
+	// ONAME
+	Name     *Name
+	Defn     *Node // ONAME: initializing assignment; OLABEL: labeled statement
+	Pack     *Node // real package for import . names
+	Curfn    *Node // function for local variables
+	Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn
+	Alloc    *Node // allocation call
+	*Param
+
+	// OPACK
+	Pkg *Pkg
+
+	// OARRAYLIT, OMAPLIT, OSTRUCTLIT.
+	Initplan *InitPlan
+
+	// Escape analysis.
+	Escflowsrc *NodeList // flow(this, src)
+	Escretval  *NodeList // on OCALLxxx, list of dummy return values
+
+	Sym *Sym // various
+
+	Opt interface{} // for optimization passes
+
+	// OLITERAL
+	Val Val
+
+	Xoffset  int64
+	Stkdelta int64 // offset added by stack frame compaction phase.
+
+	// Escape analysis.
+	Escloopdepth int32 // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
+
+	Vargen  int32 // unique name for OTYPE/ONAME within a function.  Function outputs are numbered starting at one.
+	Lineno  int32
+	Iota    int32
+	Walkgen uint32
+
+	Funcdepth int32
+
+	// OREGISTER, OINDREG
+	Reg int16
+
+	// most nodes - smaller fields
+	Esclevel Level
+	Esc      uint16 // EscXXX
+
 	Op          uint8
 	Nointerface bool
 	Ullman      uint8 // sethi/ullman number
@@ -42,56 +96,10 @@ type Node struct {
 	Used        bool
 	Isddd       bool // is the argument variadic
 	Implicit    bool
-	Addrtaken   bool   // address taken, even if not moved to heap
-	Assigned    bool   // is the variable ever assigned to
-	Likely      int8   // likeliness of if statement
-	Hasbreak    bool   // has break statement
-	Esc         uint16 // EscXXX
-	Funcdepth   int32
-
-	// most nodes
-	Type  *Type
-	Orig  *Node // original form, for printing, and tracking copies of ONAMEs
-	Nname *Node
-
-	// func
-	Func *Func
-
-	// OLITERAL
-	Val Val
-
-	// OREGISTER, OINDREG
-	Reg int16
-
-	// ONAME
-	Name     *Name
-	Defn     *Node // ONAME: initializing assignment; OLABEL: labeled statement
-	Pack     *Node // real package for import . names
-	Curfn    *Node // function for local variables
-	Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn
-	Alloc    *Node // allocation call
-	*Param
-
-	// OPACK
-	Pkg *Pkg
-
-	// OARRAYLIT, OMAPLIT, OSTRUCTLIT.
-	Initplan *InitPlan
-
-	// Escape analysis.
-	Escflowsrc   *NodeList // flow(this, src)
-	Escretval    *NodeList // on OCALLxxx, list of dummy return values
-	Escloopdepth int32     // -1: global, 0: return variables, 1:function top level, increased inside function for every loop or label to mark scopes
-
-	Sym      *Sym  // various
-	Vargen   int32 // unique name for OTYPE/ONAME within a function.  Function outputs are numbered starting at one.
-	Lineno   int32
-	Xoffset  int64
-	Stkdelta int64 // offset added by stack frame compaction phase.
-	Iota     int32
-	Walkgen  uint32
-	Esclevel Level
-	Opt      interface{} // for optimization passes
+	Addrtaken   bool // address taken, even if not moved to heap
+	Assigned    bool // is the variable ever assigned to
+	Likely      int8 // likeliness of if statement
+	Hasbreak    bool // has break statement
 }
 
 // Name holds Node fields used only by ONAME nodes.

From b21ff39679486c03648b1abda7ce206fcf09bc36 Mon Sep 17 00:00:00 2001
From: Aaron Jacobs 
Date: Tue, 19 May 2015 10:47:24 +1000
Subject: [PATCH 145/232] flag: Fix up a package comment a bit.

I think "the flag" was a typo, and the word "after" was repetitive.

Change-Id: I81c034ca11a3a778ff1eb4b3af5b96bc525ab985
Reviewed-on: https://go-review.googlesource.com/10195
Reviewed-by: Rob Pike 
Reviewed-by: Andrew Gerrand 
---
 src/flag/flag.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/flag/flag.go b/src/flag/flag.go
index 4e4279069f..060660248e 100644
--- a/src/flag/flag.go
+++ b/src/flag/flag.go
@@ -31,7 +31,7 @@
 		fmt.Println("ip has value ", *ip)
 		fmt.Println("flagvar has value ", flagvar)
 
-	After parsing, the arguments after the flag are available as the
+	After parsing, the arguments following the flags are available as the
 	slice flag.Args() or individually as flag.Arg(i).
 	The arguments are indexed from 0 through flag.NArg()-1.
 

From f3fc8b024530c6b67367667455748d9b1f19eafe Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Tue, 19 May 2015 00:25:47 -0400
Subject: [PATCH 146/232] time: document that not all Unix time can be
 represented

Fixes #10906.

Change-Id: I7ae25a500df493c1e78183d69d89b3e2a64a0d1a
Reviewed-on: https://go-review.googlesource.com/10223
Reviewed-by: Andrew Gerrand 
---
 src/time/time.go | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/time/time.go b/src/time/time.go
index 0300e846a4..fbf3f8d3c8 100644
--- a/src/time/time.go
+++ b/src/time/time.go
@@ -966,6 +966,8 @@ func (t *Time) UnmarshalText(data []byte) (err error) {
 // Unix returns the local Time corresponding to the given Unix time,
 // sec seconds and nsec nanoseconds since January 1, 1970 UTC.
 // It is valid to pass nsec outside the range [0, 999999999].
+// Not all sec values have a corresponding time value. Notable such
+// values are -1<<63 and 1<<63-1.
 func Unix(sec int64, nsec int64) Time {
 	if nsec < 0 || nsec >= 1e9 {
 		n := nsec / 1e9

From 366ba526e88f5b523298d3ad5014e04a495add82 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Mon, 18 May 2015 16:54:59 -0400
Subject: [PATCH 147/232] cmd/internal/gc: add missing write barrier in
 append(x, BigStructWithPointers)

Fixes #10897.

Change-Id: I5c2d1f9d26333e2b2a0613ebf496daa465e07c24
Reviewed-on: https://go-review.googlesource.com/10221
Reviewed-by: Austin Clements 
---
 src/cmd/internal/gc/cgen.go | 31 ++++++++++++++++++++++---------
 test/writebarrier.go        | 16 ++++++++++++++++
 2 files changed, 38 insertions(+), 9 deletions(-)

diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go
index bb022b8351..002439ce36 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/internal/gc/cgen.go
@@ -2156,14 +2156,27 @@ func bins(typ *Type, res *Node, a, likely int, to *obj.Prog) {
 	}
 }
 
-/*
- * n is on stack, either local variable
- * or return value from function call.
- * return n's offset from SP.
- */
+// stkof returns n's offset from SP if n is on the stack
+// (either a local variable or the return value from a function call
+// or the arguments to a function call).
+// If n is not on the stack, stkof returns -1000.
+// If n is on the stack but in an unknown location
+// (due to array index arithmetic), stkof returns +1000.
+//
+// NOTE(rsc): It is possible that the ODOT and OINDEX cases
+// are not relevant here, since it shouldn't be possible for them
+// to be involved in an overlapping copy. Only function results
+// from one call and the arguments to the next can overlap in
+// any non-trivial way. If they can be dropped, then this function
+// becomes much simpler and also more trustworthy.
+// The fact that it works at all today is probably due to the fact
+// that ODOT and OINDEX are irrelevant.
 func stkof(n *Node) int64 {
 	switch n.Op {
 	case OINDREG:
+		if n.Reg != int16(Thearch.REGSP) {
+			return -1000 // not on stack
+		}
 		return n.Xoffset
 
 	case ODOT:
@@ -2172,7 +2185,7 @@ func stkof(n *Node) int64 {
 			break
 		}
 		off := stkof(n.Left)
-		if off == -1000 || off == 1000 {
+		if off == -1000 || off == +1000 {
 			return off
 		}
 		return off + n.Xoffset
@@ -2183,13 +2196,13 @@ func stkof(n *Node) int64 {
 			break
 		}
 		off := stkof(n.Left)
-		if off == -1000 || off == 1000 {
+		if off == -1000 || off == +1000 {
 			return off
 		}
 		if Isconst(n.Right, CTINT) {
 			return off + t.Type.Width*Mpgetfix(n.Right.Val.U.(*Mpint))
 		}
-		return 1000
+		return +1000 // on stack but not sure exactly where
 
 	case OCALLMETH, OCALLINTER, OCALLFUNC:
 		t := n.Left.Type
@@ -2210,7 +2223,7 @@ func stkof(n *Node) int64 {
 
 	// botch - probably failing to recognize address
 	// arithmetic on the above. eg INDEX and DOT
-	return -1000
+	return -1000 // not on stack
 }
 
 /*
diff --git a/test/writebarrier.go b/test/writebarrier.go
index b24af9a14d..9b741a60df 100644
--- a/test/writebarrier.go
+++ b/test/writebarrier.go
@@ -128,3 +128,19 @@ func f13(x []int, y *[]int) {
 func f14(y *[]int) {
 	*y = append(*y, 1) // ERROR "write barrier"
 }
+
+type T1 struct {
+	X *int
+}
+
+func f15(x []T1, y T1) []T1 {
+	return append(x, y) // ERROR "write barrier"
+}
+
+type T8 struct {
+	X [8]*int
+}
+
+func f16(x []T8, y T8) []T8 {
+	return append(x, y) // ERROR "write barrier"
+}

From 8903b3db0e889f08587a09566927d6252c9f9ebc Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Mon, 18 May 2015 11:40:29 -0400
Subject: [PATCH 148/232] runtime: add fast check for self-loop pointer in
 scanobject
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Addresses a problem reported on the mailing list.

This will come up mainly in programs custom allocators that batch allocations,
but it still helps in our programs, which mainly do not have such allocations.

name                   old mean              new mean              delta
BinaryTree17            5.95s × (0.97,1.03)   5.93s × (0.97,1.04)    ~    (p=0.613)
Fannkuch11              4.46s × (0.98,1.04)   4.33s × (0.99,1.01)  -2.93% (p=0.000)
FmtFprintfEmpty        86.6ns × (0.98,1.03)  86.8ns × (0.98,1.02)    ~    (p=0.523)
FmtFprintfString        290ns × (0.98,1.05)   287ns × (0.98,1.03)    ~    (p=0.061)
FmtFprintfInt           271ns × (0.98,1.04)   286ns × (0.99,1.01)  +5.54% (p=0.000)
FmtFprintfIntInt        495ns × (0.98,1.04)   489ns × (0.99,1.01)  -1.24% (p=0.015)
FmtFprintfPrefixedInt   391ns × (0.99,1.02)   407ns × (0.99,1.01)  +4.00% (p=0.000)
FmtFprintfFloat         578ns × (0.99,1.01)   559ns × (0.99,1.01)  -3.35% (p=0.000)
FmtManyArgs            1.96µs × (0.98,1.05)  1.94µs × (0.99,1.01)  -1.33% (p=0.030)
GobDecode              15.9ms × (0.97,1.05)  15.7ms × (0.99,1.01)  -1.35% (p=0.044)
GobEncode              11.4ms × (0.97,1.05)  11.3ms × (0.98,1.03)    ~    (p=0.141)
Gzip                    658ms × (0.98,1.05)   648ms × (0.99,1.01)  -1.59% (p=0.009)
Gunzip                  144ms × (0.99,1.03)   144ms × (0.99,1.01)    ~    (p=0.867)
HTTPClientServer       92.1µs × (0.97,1.05)  90.3µs × (0.99,1.01)  -1.89% (p=0.005)
JSONEncode             31.0ms × (0.96,1.07)  30.2ms × (0.98,1.03)  -2.66% (p=0.001)
JSONDecode              110ms × (0.97,1.04)   107ms × (0.99,1.01)  -2.59% (p=0.000)
Mandelbrot200          6.15ms × (0.98,1.04)  6.07ms × (0.99,1.02)  -1.32% (p=0.045)
GoParse                6.79ms × (0.97,1.04)  6.74ms × (0.97,1.04)    ~    (p=0.242)
RegexpMatchEasy0_32     158ns × (0.98,1.05)   155ns × (0.99,1.01)  -1.64% (p=0.010)
RegexpMatchEasy0_1K     548ns × (0.97,1.04)   540ns × (0.99,1.01)  -1.34% (p=0.042)
RegexpMatchEasy1_32     133ns × (0.97,1.04)   132ns × (0.97,1.05)    ~    (p=0.466)
RegexpMatchEasy1_1K     899ns × (0.96,1.05)   878ns × (0.99,1.01)  -2.32% (p=0.002)
RegexpMatchMedium_32    250ns × (0.96,1.03)   243ns × (0.99,1.01)  -2.90% (p=0.000)
RegexpMatchMedium_1K   73.4µs × (0.98,1.04)  73.0µs × (0.98,1.04)    ~    (p=0.411)
RegexpMatchHard_32     3.87µs × (0.97,1.07)  3.84µs × (0.98,1.04)    ~    (p=0.273)
RegexpMatchHard_1K      120µs × (0.97,1.08)   117µs × (0.99,1.01)  -2.06% (p=0.010)
Revcomp                 940ms × (0.96,1.07)   924ms × (0.97,1.07)    ~    (p=0.071)
Template                128ms × (0.96,1.05)   128ms × (0.99,1.01)    ~    (p=0.502)
TimeParse               632ns × (0.96,1.07)   616ns × (0.99,1.01)  -2.58% (p=0.001)
TimeFormat              671ns × (0.97,1.06)   657ns × (0.99,1.02)  -2.10% (p=0.002)

In contrast to the one in test/bench/go1 (above), the binarytree program on the
shootout site uses more goroutines, batches allocations, and sets GOMAXPROCS
to runtime.NumCPU()*2.

Using that version, before vs after:

name          old mean             new mean             delta
BinaryTree20  18.6s × (0.96,1.05)  11.3s × (0.98,1.02)  -39.46% (p=0.000)

And Go 1.4 vs after:

name          old mean             new mean             delta
BinaryTree20  13.0s × (0.97,1.02)  11.3s × (0.98,1.02)  -13.21% (p=0.000)

There is still a scheduling problem - the raw run times are hiding the fact that
this chews up 2x the CPU - but we'll take care of that separately.

Change-Id: I3f5da879b24ae73a0d06745381ffb88c3744948b
Reviewed-on: https://go-review.googlesource.com/10220
Reviewed-by: Austin Clements 
---
 src/runtime/mgcmark.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 460997880b..0c4e6eba51 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -611,8 +611,8 @@ func scanobject(b uintptr, gcw *gcWork) {
 		obj := *(*uintptr)(unsafe.Pointer(b + i))
 
 		// At this point we have extracted the next potential pointer.
-		// Check if it points into heap.
-		if obj != 0 && arena_start <= obj && obj < arena_used {
+		// Check if it points into heap and not back at the current object.
+		if obj != 0 && arena_start <= obj && obj < arena_used && obj-b >= n {
 			// Mark the object.
 			if obj, hbits, span := heapBitsForObject(obj); obj != 0 {
 				greyobject(obj, b, i, hbits, span, gcw)

From f4d51eb2f567d12c8b908f9fe3f9650e8a175eb7 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Sat, 16 May 2015 21:14:37 -0400
Subject: [PATCH 149/232] runtime: minor clean up to heapminimum

Currently setGCPercent sets heapminimum to heapminimum*GOGC/100. The
real intent is to set heapminimum to a scaled multiple of a fixed
default heap minimum, not to scale heapminimum based on its current
value. This turns out to be okay because setGCPercent is only called
once and heapminimum is initially set to this default heap minimum.
However, the code as written is confusing, especially since
setGCPercent is otherwise written so it could be called again to
change GOGC. Fix this by introducing a defaultHeapMinimum constant and
using this instead of the current value of heapminimum to compute the
scaled heap minimum.

As part of this, this commit improves the documentation on
heapminimum.

Change-Id: I4eb82c73dc2eb44a6e5a17c780a747a2e73d7493
Reviewed-on: https://go-review.googlesource.com/10181
Reviewed-by: Russ Cox 
---
 src/runtime/mgc.go | 25 +++++++++++++++++--------
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index a16d7603a6..fb2b210020 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -127,13 +127,22 @@ const (
 	_RootCount       = 5
 )
 
-// heapminimum is the minimum number of bytes in the heap.
-// This cleans up the corner case of where we have a very small live set but a lot
-// of allocations and collecting every GOGC * live set is expensive.
-// heapminimum is adjust by multiplying it by GOGC/100. In
-// the special case of GOGC==0 this will set heapminimum to 0 resulting
-// collecting at every allocation even when the heap size is small.
-var heapminimum = uint64(4 << 20)
+// heapminimum is the minimum heap size at which to trigger GC.
+// For small heaps, this overrides the usual GOGC*live set rule.
+//
+// When there is a very small live set but a lot of allocation, simply
+// collecting when the heap reaches GOGC*live results in many GC
+// cycles and high total per-GC overhead. This minimum amortizes this
+// per-GC overhead while keeping the heap reasonably small.
+//
+// During initialization this is set to 4MB*GOGC/100. In the case of
+// GOGC==0, this will set heapminimum to 0, resulting in constant
+// collection even when the heap size is small, which is useful for
+// debugging.
+var heapminimum uint64 = defaultHeapMinimum
+
+// defaultHeapMinimum is the value of heapminimum for GOGC==100.
+const defaultHeapMinimum = 4 << 20
 
 // Initialized from $GOGC.  GOGC=off means no GC.
 var gcpercent int32
@@ -180,7 +189,7 @@ func setGCPercent(in int32) (out int32) {
 		in = -1
 	}
 	gcpercent = in
-	heapminimum = heapminimum * uint64(gcpercent) / 100
+	heapminimum = defaultHeapMinimum * uint64(gcpercent) / 100
 	unlock(&mheap_.lock)
 	return out
 }

From 913db7685ec80bc9c56f357b09ced127fbf09a1e Mon Sep 17 00:00:00 2001
From: Rick Hudson 
Date: Mon, 18 May 2015 16:02:37 -0400
Subject: [PATCH 150/232] runtime: run background mark helpers only if work is
 available

Prior to this CL whenever the GC marking was enabled and
a P was looking for work we supplied a G to help
the GC do its marking tasks. Once this G finished all
the marking available it would release the P to find another
available G. In the case where there was no work the P would drop
into findrunnable which would execute the mark helper G which would
immediately return and the P would drop into findrunnable again repeating
the process. Since the P was always given a G to run it never blocks.
This CL first checks if the GC mark helper G has available work and if
not the P immediately falls through to its blocking logic.

Fixes #10901

Change-Id: I94ac9646866ba64b7892af358888bc9950de23b5
Reviewed-on: https://go-review.googlesource.com/10189
Reviewed-by: Austin Clements 
---
 src/runtime/mgc.go     | 12 ++++++++++++
 src/runtime/mgcwork.go |  7 +++++++
 src/runtime/proc1.go   |  2 +-
 3 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index fb2b210020..ebecc4ffa8 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -1168,6 +1168,18 @@ func gcBgMarkDone() {
 	}
 }
 
+// gcMarkWorkAvailable determines if mark work is readily available.
+// It is used by the scheduler to decide if this p run a mark work.
+func gcMarkWorkAvailable(p *p) bool {
+	if !p.gcw.empty() {
+		return true
+	}
+	if atomicload64(&work.full) != 0 || atomicload64(&work.partial) != 0 {
+		return true // global work available
+	}
+	return false
+}
+
 // gcFlushGCWork disposes the gcWork caches of all Ps. The world must
 // be stopped.
 //go:nowritebarrier
diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go
index 9c32ae8880..930c644c0a 100644
--- a/src/runtime/mgcwork.go
+++ b/src/runtime/mgcwork.go
@@ -182,6 +182,13 @@ func (w *gcWork) balance() {
 	}
 }
 
+// empty returns true if w has no mark work available.
+//go:nowritebarrier
+func (w *gcWork) empty() bool {
+	wbuf := w.wbuf
+	return wbuf == 0 || wbuf.ptr().nobj == 0
+}
+
 // Internally, the GC work pool is kept in arrays in work buffers.
 // The gcWork interface caches a work buffer until full (or empty) to
 // avoid contending on the global work buffer lists.
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index b0b3bf7711..54d6698b3f 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -1479,7 +1479,7 @@ stop:
 	// We have nothing to do. If we're in the GC mark phase and can
 	// safely scan and blacken objects, run idle-time marking
 	// rather than give up the P.
-	if _p_ := _g_.m.p.ptr(); gcBlackenEnabled != 0 && _p_.gcBgMarkWorker != nil {
+	if _p_ := _g_.m.p.ptr(); gcBlackenEnabled != 0 && _p_.gcBgMarkWorker != nil && gcMarkWorkAvailable(_p_) {
 		_p_.gcMarkWorkerMode = gcMarkWorkerIdleMode
 		gp := _p_.gcBgMarkWorker
 		casgstatus(gp, _Gwaiting, _Grunnable)

From c735064cdeb6bf4ec84a0a4b2b48a5cafc4b83dd Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Tue, 19 May 2015 14:00:27 -0400
Subject: [PATCH 151/232] cmd/internal/gc: type of str[i] is byte, not uint8

Fixes #8745.

Change-Id: Id0641e3c0f259812b41ed871e83c68740feb2b19
Reviewed-on: https://go-review.googlesource.com/10261
Reviewed-by: Austin Clements 
---
 src/cmd/internal/gc/typecheck.go |  2 +-
 test/fixedbugs/issue8745.go      | 13 +++++++++++++
 2 files changed, 14 insertions(+), 1 deletion(-)
 create mode 100644 test/fixedbugs/issue8745.go

diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/internal/gc/typecheck.go
index 8af9f084e2..0395ec5f5b 100644
--- a/src/cmd/internal/gc/typecheck.go
+++ b/src/cmd/internal/gc/typecheck.go
@@ -1027,7 +1027,7 @@ OpSwitch:
 		case TSTRING, TARRAY:
 			indexlit(&n.Right)
 			if t.Etype == TSTRING {
-				n.Type = Types[TUINT8]
+				n.Type = bytetype
 			} else {
 				n.Type = t.Type
 			}
diff --git a/test/fixedbugs/issue8745.go b/test/fixedbugs/issue8745.go
new file mode 100644
index 0000000000..f3a70aff71
--- /dev/null
+++ b/test/fixedbugs/issue8745.go
@@ -0,0 +1,13 @@
+// errorcheck
+
+// Copyright 2015 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Check that the error says s[2] is a byte, not a uint8.
+
+package p
+
+func f(s string) {
+	var _ float64 = s[2] // ERROR "cannot use.*type byte.*as type float64"
+}

From 9c9e36b34040d33b9f9a0b6fd918ef470338aec4 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Tue, 19 May 2015 15:15:52 -0400
Subject: [PATCH 152/232] cmd/internal/gc: sync nowritebarrier checks and write
 barrier insertion

Change-Id: I348223d0336e28d95b8e68d7653aa547acc7c9c3
Reviewed-on: https://go-review.googlesource.com/10262
Reviewed-by: Austin Clements 
---
 src/cmd/internal/gc/cgen.go | 6 ++++++
 src/cmd/internal/gc/walk.go | 3 +++
 2 files changed, 9 insertions(+)

diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go
index 002439ce36..e003ea9f4f 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/internal/gc/cgen.go
@@ -785,6 +785,9 @@ abop: // asymmetric binary
 var sys_wbptr *Node
 
 func cgen_wbptr(n, res *Node) {
+	if Curfn != nil && Curfn.Func.Nowritebarrier {
+		Yyerror("write barrier prohibited")
+	}
 	if Debug_wb > 0 {
 		Warn("write barrier")
 	}
@@ -828,6 +831,9 @@ func cgen_wbptr(n, res *Node) {
 }
 
 func cgen_wbfat(n, res *Node) {
+	if Curfn != nil && Curfn.Func.Nowritebarrier {
+		Yyerror("write barrier prohibited")
+	}
 	if Debug_wb > 0 {
 		Warn("write barrier")
 	}
diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go
index a7f5256b19..36e4d66b33 100644
--- a/src/cmd/internal/gc/walk.go
+++ b/src/cmd/internal/gc/walk.go
@@ -2217,6 +2217,9 @@ func applywritebarrier(n *Node, init **NodeList) *Node {
 			return n
 		}
 		// Use slow path always for race detector.
+		if Curfn != nil && Curfn.Func.Nowritebarrier {
+			Yyerror("write barrier prohibited")
+		}
 		if Debug_wb > 0 {
 			Warnl(int(n.Lineno), "write barrier")
 		}

From 8b83306cf20abed54d7cb23a3f3091b7e6202056 Mon Sep 17 00:00:00 2001
From: Ryan Brown 
Date: Wed, 8 Apr 2015 12:55:34 -0700
Subject: [PATCH 153/232] cmd/internal/ld: output dwarf in external link mode
 on darwin

Fixes #8973

Change-Id: Idd53fc6d9e6971ae31ed72a3df3cfdce0bfbc1fd
Reviewed-on: https://go-review.googlesource.com/8661
Reviewed-by: Russ Cox 
Run-TryBot: Russ Cox 
---
 src/cmd/5l/asm.go                          |  14 +-
 src/cmd/6l/asm.go                          |   2 +-
 src/cmd/7l/asm.go                          |  14 +-
 src/cmd/8l/asm.go                          |   2 +-
 src/cmd/internal/ld/dwarf.go               | 155 +++++++--
 src/cmd/internal/ld/lib.go                 |  21 +-
 src/cmd/internal/ld/macho.go               |   8 +-
 src/cmd/internal/ld/macho_combine_dwarf.go | 369 +++++++++++++++++++++
 8 files changed, 527 insertions(+), 58 deletions(-)
 create mode 100644 src/cmd/internal/ld/macho_combine_dwarf.go

diff --git a/src/cmd/5l/asm.go b/src/cmd/5l/asm.go
index 1b69671b9f..70d6790fc1 100644
--- a/src/cmd/5l/asm.go
+++ b/src/cmd/5l/asm.go
@@ -533,14 +533,12 @@ func asmb() {
 			fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
 		}
 
-		if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
-			dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
-			ld.Cseek(int64(dwarfoff))
+		dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+		ld.Cseek(int64(dwarfoff))
 
-			ld.Segdwarf.Fileoff = uint64(ld.Cpos())
-			ld.Dwarfemitdebugsections()
-			ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
-		}
+		ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+		ld.Dwarfemitdebugsections()
+		ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
 
 		machlink = uint32(ld.Domacholink())
 	}
@@ -567,7 +565,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
diff --git a/src/cmd/6l/asm.go b/src/cmd/6l/asm.go
index 5520a5acf1..02b4c7cdd2 100644
--- a/src/cmd/6l/asm.go
+++ b/src/cmd/6l/asm.go
@@ -710,7 +710,7 @@ func asmb() {
 			symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = int64(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 
 		case obj.Hlinux,
 			obj.Hfreebsd,
diff --git a/src/cmd/7l/asm.go b/src/cmd/7l/asm.go
index 3dfb8c666d..064ff56283 100644
--- a/src/cmd/7l/asm.go
+++ b/src/cmd/7l/asm.go
@@ -317,14 +317,12 @@ func asmb() {
 			fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
 		}
 
-		if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
-			dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
-			ld.Cseek(int64(dwarfoff))
+		dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+		ld.Cseek(int64(dwarfoff))
 
-			ld.Segdwarf.Fileoff = uint64(ld.Cpos())
-			ld.Dwarfemitdebugsections()
-			ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
-		}
+		ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+		ld.Dwarfemitdebugsections()
+		ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
 
 		machlink = uint32(ld.Domacholink())
 	}
@@ -351,7 +349,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
diff --git a/src/cmd/8l/asm.go b/src/cmd/8l/asm.go
index a63c51f58d..a736d43686 100644
--- a/src/cmd/8l/asm.go
+++ b/src/cmd/8l/asm.go
@@ -551,7 +551,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 
 		case obj.Hwindows:
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/internal/ld/dwarf.go
index 476b329e7a..841ef9abf9 100644
--- a/src/cmd/internal/ld/dwarf.go
+++ b/src/cmd/internal/ld/dwarf.go
@@ -17,6 +17,7 @@ package ld
 import (
 	"cmd/internal/obj"
 	"fmt"
+	"os"
 	"strings"
 )
 
@@ -240,6 +241,7 @@ var abbrevs = [DW_NABRV]DWAbbrev{
 			{DW_AT_low_pc, DW_FORM_addr},
 			{DW_AT_high_pc, DW_FORM_addr},
 			{DW_AT_stmt_list, DW_FORM_data4},
+			{DW_AT_comp_dir, DW_FORM_string},
 		},
 	},
 
@@ -694,6 +696,9 @@ func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64)
 	if Iself && Thearch.Thechar == '6' {
 		addend = 0
 	}
+	if HEADTYPE == obj.Hdarwin {
+		addend += sym.Value
+	}
 	switch siz {
 	case 4:
 		Thearch.Lput(uint32(addend))
@@ -1547,6 +1552,13 @@ func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_len
 	}
 }
 
+func getCompilationDir() string {
+	if dir, err := os.Getwd(); err == nil {
+		return dir
+	}
+	return "/"
+}
+
 func writelines() {
 	if linesec == nil {
 		linesec = Linklookup(Ctxt, ".dwarfline", 0)
@@ -1571,6 +1583,9 @@ func writelines() {
 	newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0)
 	newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart-lineo, 0)
 	newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s)
+	// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
+	compDir := getCompilationDir()
+	newattr(dwinfo, DW_AT_comp_dir, DW_CLS_STRING, int64(len(compDir)), compDir)
 
 	// Write .debug_line Line Number Program Header (sec 6.2.4)
 	// Fields marked with (*) must be changed for 64-bit dwarf
@@ -2083,6 +2098,14 @@ func writedwarfreloc(s *LSym) int64 {
 	return start
 }
 
+func addmachodwarfsect(prev *Section, name string) *Section {
+	sect := addsection(&Segdwarf, name, 04)
+	sect.Extnum = prev.Extnum + 1
+	sym := Linklookup(Ctxt, name, 0)
+	sym.Sect = sect
+	return sect
+}
+
 /*
  * This is the main entry point for generating dwarf.  After emitting
  * the mandatory debug_abbrev section, it calls writelines() to set up
@@ -2097,8 +2120,32 @@ func Dwarfemitdebugsections() {
 		return
 	}
 
-	if Linkmode == LinkExternal && !Iself {
-		return
+	if Linkmode == LinkExternal {
+		if !Iself && HEADTYPE != obj.Hdarwin {
+			return
+		}
+		if HEADTYPE == obj.Hdarwin {
+			sect := Segdata.Sect
+			// find the last section.
+			for sect.Next != nil {
+				sect = sect.Next
+			}
+			sect = addmachodwarfsect(sect, ".debug_abbrev")
+			sect = addmachodwarfsect(sect, ".debug_line")
+			sect = addmachodwarfsect(sect, ".debug_frame")
+			sect = addmachodwarfsect(sect, ".debug_info")
+		}
+		infosym = Linklookup(Ctxt, ".debug_info", 0)
+		infosym.Hide = 1
+
+		abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
+		abbrevsym.Hide = 1
+
+		linesym = Linklookup(Ctxt, ".debug_line", 0)
+		linesym.Hide = 1
+
+		framesym = Linklookup(Ctxt, ".debug_frame", 0)
+		framesym.Hide = 1
 	}
 
 	// For diagnostic messages.
@@ -2191,6 +2238,15 @@ func Dwarfemitdebugsections() {
 	for Cpos()&7 != 0 {
 		Cput(0)
 	}
+	if HEADTYPE != obj.Hdarwin {
+		dwarfemitreloc()
+	}
+}
+
+func dwarfemitreloc() {
+	if Debug['w'] != 0 { // disable dwarf
+		return
+	}
 	inforeloco = writedwarfreloc(infosec)
 	inforelocsize = Cpos() - inforeloco
 	align(inforelocsize)
@@ -2263,18 +2319,6 @@ func dwarfaddshstrings(shstrtab *LSym) {
 			elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rel.debug_line")
 			elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rel.debug_frame")
 		}
-
-		infosym = Linklookup(Ctxt, ".debug_info", 0)
-		infosym.Hide = 1
-
-		abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
-		abbrevsym.Hide = 1
-
-		linesym = Linklookup(Ctxt, ".debug_line", 0)
-		linesym.Hide = 1
-
-		framesym = Linklookup(Ctxt, ".debug_frame", 0)
-		framesym.Hide = 1
 	}
 }
 
@@ -2420,14 +2464,15 @@ func dwarfaddelfheaders() {
 /*
  * Macho
  */
-func dwarfaddmachoheaders() {
+func dwarfaddmachoheaders(ms *MachoSeg) {
 	if Debug['w'] != 0 { // disable dwarf
 		return
 	}
 
 	// Zero vsize segments won't be loaded in memory, even so they
 	// have to be page aligned in the file.
-	fakestart := abbrevo &^ 0xfff
+	fakestart := Rnd(int64(Segdwarf.Fileoff), 0x1000)
+	addr := Segdata.Vaddr + Segdata.Length
 
 	nsect := 4
 	if pubnamessize > 0 {
@@ -2443,57 +2488,94 @@ func dwarfaddmachoheaders() {
 		nsect++
 	}
 
-	ms := newMachoSeg("__DWARF", nsect)
-	ms.fileoffset = uint64(fakestart)
-	ms.filesize = uint64(abbrevo) - uint64(fakestart)
-	ms.vaddr = ms.fileoffset + Segdata.Vaddr - Segdata.Fileoff
+	if Linkmode != LinkExternal {
+		ms = newMachoSeg("__DWARF", nsect)
+		ms.fileoffset = uint64(fakestart)
+		ms.filesize = Segdwarf.Filelen
+		ms.vaddr = addr
+	}
 
 	msect := newMachoSect(ms, "__debug_abbrev", "__DWARF")
 	msect.off = uint32(abbrevo)
 	msect.size = uint64(abbrevsize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if abbrevsym != nil {
+		abbrevsym.Value = int64(msect.addr)
+	}
 
 	msect = newMachoSect(ms, "__debug_line", "__DWARF")
 	msect.off = uint32(lineo)
 	msect.size = uint64(linesize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if linesym != nil {
+		linesym.Value = int64(msect.addr)
+	}
+	if linerelocsize > 0 {
+		msect.nreloc = uint32(len(linesec.R))
+		msect.reloc = uint32(linereloco)
+	}
 
 	msect = newMachoSect(ms, "__debug_frame", "__DWARF")
 	msect.off = uint32(frameo)
 	msect.size = uint64(framesize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if framesym != nil {
+		framesym.Value = int64(msect.addr)
+	}
+	if framerelocsize > 0 {
+		msect.nreloc = uint32(len(framesec.R))
+		msect.reloc = uint32(framereloco)
+	}
 
 	msect = newMachoSect(ms, "__debug_info", "__DWARF")
 	msect.off = uint32(infoo)
 	msect.size = uint64(infosize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if infosym != nil {
+		infosym.Value = int64(msect.addr)
+	}
+	if inforelocsize > 0 {
+		msect.nreloc = uint32(len(infosec.R))
+		msect.reloc = uint32(inforeloco)
+	}
 
 	if pubnamessize > 0 {
 		msect := newMachoSect(ms, "__debug_pubnames", "__DWARF")
 		msect.off = uint32(pubnameso)
 		msect.size = uint64(pubnamessize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
 	}
 
 	if pubtypessize > 0 {
 		msect := newMachoSect(ms, "__debug_pubtypes", "__DWARF")
 		msect.off = uint32(pubtypeso)
 		msect.size = uint64(pubtypessize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
 	}
 
 	if arangessize > 0 {
 		msect := newMachoSect(ms, "__debug_aranges", "__DWARF")
 		msect.off = uint32(arangeso)
 		msect.size = uint64(arangessize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
+		if arangesrelocsize > 0 {
+			msect.nreloc = uint32(len(arangessec.R))
+			msect.reloc = uint32(arangesreloco)
+		}
 	}
 
 	// TODO(lvd) fix gdb/python to load MachO (16 char section name limit)
@@ -2501,8 +2583,9 @@ func dwarfaddmachoheaders() {
 		msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF")
 		msect.off = uint32(gdbscripto)
 		msect.size = uint64(gdbscriptsize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
 	}
 }
 
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index 753e8eebd8..a36cd0f8f4 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -922,7 +922,7 @@ func hostlink() {
 	}
 
 	if HEADTYPE == obj.Hdarwin {
-		argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000")
+		argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000,-headerpad,1144")
 	}
 	if HEADTYPE == obj.Hopenbsd {
 		argv = append(argv, "-Wl,-nopie")
@@ -1029,6 +1029,25 @@ func hostlink() {
 	if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
 		Exitf("running %s failed: %v\n%s", argv[0], err, out)
 	}
+
+	if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin {
+		dsym := fmt.Sprintf("%s/go.dwarf", tmpdir)
+		if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil {
+			Ctxt.Cursym = nil
+			Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
+		}
+		combinedOutput := fmt.Sprintf("%s/go.combined", tmpdir)
+		if err := machoCombineDwarf(outfile, dsym, combinedOutput); err != nil {
+			Ctxt.Cursym = nil
+			Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+		}
+		origOutput := fmt.Sprintf("%s/go.orig", tmpdir)
+		os.Rename(outfile, origOutput)
+		if err := os.Rename(combinedOutput, outfile); err != nil {
+			Ctxt.Cursym = nil
+			Exitf("%s: rename(%s, %s) failed: %v", os.Args[0], combinedOutput, outfile, err)
+		}
+	}
 }
 
 func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) {
diff --git a/src/cmd/internal/ld/macho.go b/src/cmd/internal/ld/macho.go
index ceeb7b0f5d..0258aff104 100644
--- a/src/cmd/internal/ld/macho.go
+++ b/src/cmd/internal/ld/macho.go
@@ -443,7 +443,8 @@ func Asmbmacho() {
 		ms = newMachoSeg("", 40)
 
 		ms.fileoffset = Segtext.Fileoff
-		ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff
+		ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
+		ms.vsize = ms.filesize
 	}
 
 	/* segment for zero page */
@@ -561,8 +562,8 @@ func Asmbmacho() {
 	}
 
 	// TODO: dwarf headers go in ms too
-	if Debug['s'] == 0 && Linkmode != LinkExternal {
-		dwarfaddmachoheaders()
+	if Debug['s'] == 0 {
+		dwarfaddmachoheaders(ms)
 	}
 
 	a := machowrite()
@@ -850,4 +851,5 @@ func Machoemitreloc() {
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 		machorelocsect(sect, datap)
 	}
+	dwarfemitreloc()
 }
diff --git a/src/cmd/internal/ld/macho_combine_dwarf.go b/src/cmd/internal/ld/macho_combine_dwarf.go
new file mode 100644
index 0000000000..9134373a52
--- /dev/null
+++ b/src/cmd/internal/ld/macho_combine_dwarf.go
@@ -0,0 +1,369 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+	"bytes"
+	"debug/macho"
+	"encoding/binary"
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+	"unsafe"
+)
+
+var fakedwarf, realdwarf, linkseg *macho.Segment
+var dwarfstart, linkstart int64
+var linkoffset uint32
+var machHeader *macho.FileHeader
+var mappedHeader []byte
+
+const (
+	LC_LOAD_DYLINKER        = 0xe
+	LC_PREBOUND_DYLIB       = 0x10
+	LC_LOAD_WEAK_DYLIB      = 0x18
+	LC_UUID                 = 0x1b
+	LC_RPATH                = 0x8000001c
+	LC_CODE_SIGNATURE       = 0x1d
+	LC_SEGMENT_SPLIT_INFO   = 0x1e
+	LC_REEXPORT_DYLIB       = 0x8000001f
+	LC_ENCRYPTION_INFO      = 0x21
+	LC_DYLD_INFO            = 0x22
+	LC_DYLD_INFO_ONLY       = 0x80000022
+	LC_VERSION_MIN_MACOSX   = 0x24
+	LC_VERSION_MIN_IPHONEOS = 0x25
+	LC_FUNCTION_STARTS      = 0x26
+	LC_MAIN                 = 0x80000028
+	LC_DATA_IN_CODE         = 0x29
+	LC_SOURCE_VERSION       = 0x2A
+	LC_DYLIB_CODE_SIGN_DRS  = 0x2B
+	LC_ENCRYPTION_INFO_64   = 0x2C
+
+	dwarfMinAlign = 6  // 64 = 1 << 6
+	pageAlign     = 12 // 4096 = 1 << 12
+)
+
+type loadCmd struct {
+	Cmd macho.LoadCmd
+	Len uint32
+}
+
+type dyldInfoCmd struct {
+	Cmd                      macho.LoadCmd
+	Len                      uint32
+	RebaseOff, RebaseLen     uint32
+	BindOff, BindLen         uint32
+	WeakBindOff, WeakBindLen uint32
+	LazyBindOff, LazyBindLen uint32
+	ExportOff, ExportLen     uint32
+}
+
+type linkEditDataCmd struct {
+	Cmd              macho.LoadCmd
+	Len              uint32
+	DataOff, DataLen uint32
+}
+
+type encryptionInfoCmd struct {
+	Cmd                macho.LoadCmd
+	Len                uint32
+	CryptOff, CryptLen uint32
+	CryptId            uint32
+}
+
+type loadCmdReader struct {
+	offset, next int64
+	f            *os.File
+	order        binary.ByteOrder
+}
+
+func (r *loadCmdReader) Next() (cmd loadCmd, err error) {
+	r.offset = r.next
+	if _, err = r.f.Seek(r.offset, 0); err != nil {
+		return
+	}
+	if err = binary.Read(r.f, r.order, &cmd); err != nil {
+		return
+	}
+	r.next = r.offset + int64(cmd.Len)
+	return
+}
+
+func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
+	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+		return err
+	}
+	return binary.Read(r.f, r.order, data)
+}
+
+func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
+	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+		return err
+	}
+	return binary.Write(r.f, r.order, data)
+}
+
+// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable.
+// With internal linking, DWARF is embedded into the executable, this lets us do the
+// same for external linking.
+// inexe is the path to the executable with no DWARF. It must have enough room in the macho
+// header to add the DWARF sections. (Use ld's -headerpad option)
+// dsym is the path to the macho file containing DWARF from dsymutil.
+// outexe is the path where the combined executable should be saved.
+func machoCombineDwarf(inexe, dsym, outexe string) error {
+	exef, err := os.Open(inexe)
+	if err != nil {
+		return err
+	}
+	dwarff, err := os.Open(dsym)
+	if err != nil {
+		return err
+	}
+	outf, err := os.Create(outexe)
+	if err != nil {
+		return err
+	}
+	outf.Chmod(0755)
+
+	exem, err := macho.NewFile(exef)
+	if err != nil {
+		return err
+	}
+	dwarfm, err := macho.NewFile(dwarff)
+	if err != nil {
+		return err
+	}
+
+	// The string table needs to be the last thing in the file
+	// for code signing to work. So we'll need to move the
+	// linkedit section, but all the others can be copied directly.
+	linkseg = exem.Segment("__LINKEDIT")
+	if linkseg == nil {
+		return fmt.Errorf("missing __LINKEDIT segment")
+	}
+
+	if _, err = exef.Seek(0, 0); err != nil {
+		return err
+	}
+	if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
+		return err
+	}
+
+	realdwarf = dwarfm.Segment("__DWARF")
+	if realdwarf == nil {
+		return fmt.Errorf("missing __DWARF segment")
+	}
+
+	// Now copy the dwarf data into the output.
+	maxalign := uint32(dwarfMinAlign) //
+	for _, sect := range dwarfm.Sections {
+		if sect.Align > maxalign {
+			maxalign = sect.Align
+		}
+	}
+	dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
+	if _, err = outf.Seek(dwarfstart, 0); err != nil {
+		return err
+	}
+
+	if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
+		return err
+	}
+	if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
+		return err
+	}
+
+	// And finally the linkedit section.
+	if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil {
+		return err
+	}
+	linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+realdwarf.Filesz, pageAlign)
+	linkoffset = uint32(linkstart - int64(linkseg.Offset))
+	if _, err = outf.Seek(linkstart, 0); err != nil {
+		return err
+	}
+	if _, err := io.Copy(outf, exef); err != nil {
+		return err
+	}
+
+	// Now we need to update the headers.
+	cmdOffset := unsafe.Sizeof(exem.FileHeader)
+	is64bit := exem.Magic == macho.Magic64
+	if is64bit {
+		// mach_header_64 has one extra uint32.
+		cmdOffset += unsafe.Sizeof(exem.Magic)
+	}
+
+	textsect := exem.Section("__text")
+	if linkseg == nil {
+		return fmt.Errorf("missing __text section")
+	}
+
+	dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz)
+	availablePadding := int64(textsect.Offset) - dwarfCmdOffset
+	if availablePadding < int64(realdwarf.Len) {
+		return fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
+	}
+	// First, copy the dwarf load command into the header
+	if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil {
+		return err
+	}
+	if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
+		return err
+	}
+
+	if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
+		return err
+	}
+	if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
+		return err
+	}
+	if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
+		return err
+	}
+
+	reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
+	for i := uint32(0); i < exem.Ncmd; i++ {
+		cmd, err := reader.Next()
+		if err != nil {
+			return err
+		}
+		switch cmd.Cmd {
+		case macho.LoadCmdSegment64:
+			err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{})
+		case macho.LoadCmdSegment:
+			err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{})
+		case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
+			err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
+		case macho.LoadCmdSymtab:
+			err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff")
+		case macho.LoadCmdDysymtab:
+			err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
+		case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS:
+			err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff")
+		case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
+			err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff")
+		case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH:
+			// Nothing to update
+		default:
+			err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd)
+		}
+		if err != nil {
+			return err
+		}
+	}
+	return machoUpdateDwarfHeader(&reader)
+}
+
+// machoUpdateSegment updates the load command for a moved segment.
+// Only the linkedit segment should move, and it should have 0 sections.
+// seg should be a macho.Segment32 or macho.Segment64 as appropriate.
+// sect should be a macho.Section32 or macho.Section64 as appropriate.
+func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
+	if err := r.ReadAt(0, seg); err != nil {
+		return err
+	}
+	segValue := reflect.ValueOf(seg)
+	offset := reflect.Indirect(segValue).FieldByName("Offset")
+
+	// Only the linkedit segment moved, any thing before that is fine.
+	if offset.Uint() < linkseg.Offset {
+		return nil
+	}
+	offset.SetUint(offset.Uint() + uint64(linkoffset))
+	if err := r.WriteAt(0, seg); err != nil {
+		return err
+	}
+	// There shouldn't be any sections, but just to make sure...
+	return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
+}
+
+func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
+	iseg := reflect.Indirect(seg)
+	nsect := iseg.FieldByName("Nsect").Uint()
+	if nsect == 0 {
+		return nil
+	}
+	sectOffset := int64(iseg.Type().Size())
+
+	isect := reflect.Indirect(sect)
+	offsetField := isect.FieldByName("Offset")
+	reloffField := isect.FieldByName("Reloff")
+	sectSize := int64(isect.Type().Size())
+	for i := uint64(0); i < nsect; i++ {
+		if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
+			return err
+		}
+		if offsetField.Uint() != 0 {
+			offsetField.SetUint(offsetField.Uint() + delta)
+		}
+		if reloffField.Uint() != 0 {
+			reloffField.SetUint(reloffField.Uint() + delta)
+		}
+		if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
+			return err
+		}
+		sectOffset += sectSize
+	}
+	return nil
+}
+
+// machoUpdateDwarfHeader updates the DWARF segment load command.
+func machoUpdateDwarfHeader(r *loadCmdReader) error {
+	var seg, sect interface{}
+	cmd, err := r.Next()
+	if err != nil {
+		return err
+	}
+	if cmd.Cmd == macho.LoadCmdSegment64 {
+		seg = new(macho.Segment64)
+		sect = new(macho.Section64)
+	} else {
+		seg = new(macho.Segment32)
+		sect = new(macho.Section32)
+	}
+	if err := r.ReadAt(0, seg); err != nil {
+		return err
+	}
+	segValue := reflect.ValueOf(seg)
+	offset := reflect.Indirect(segValue).FieldByName("Offset")
+
+	delta := uint64(dwarfstart) - realdwarf.Offset
+	offset.SetUint(offset.Uint() + delta)
+	if err := r.WriteAt(0, seg); err != nil {
+		return err
+	}
+	return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
+}
+
+func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {
+	if err := r.ReadAt(0, cmd); err != nil {
+		return err
+	}
+	value := reflect.Indirect(reflect.ValueOf(cmd))
+
+	for _, name := range fields {
+		field := value.FieldByName(name)
+		fieldval := field.Uint()
+		if fieldval >= linkseg.Offset {
+			field.SetUint(fieldval + uint64(linkoffset))
+		}
+	}
+	if err := r.WriteAt(0, cmd); err != nil {
+		return err
+	}
+	return nil
+}
+
+func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 {
+	align := uint64(1 << alignExp)
+	if (origAddr % align) == (newAddr % align) {
+		return int64(newAddr)
+	}
+	padding := (align - (newAddr % align))
+	padding += origAddr % align
+	return int64(padding + newAddr)
+}

From b3241912ff62ab64ec3c61bc8c198b6e12890a77 Mon Sep 17 00:00:00 2001
From: Alexander Zolotov 
Date: Sat, 9 May 2015 16:41:32 +0300
Subject: [PATCH 154/232] cmd/go: run gofmt from current GOROOT

The existing implementation executes `gofmt` binary from PATH
environment variable on invocation `go fmt` command.
Relying on PATH might lead to confusions for users with several Go installations.
It's more appropriate to run `gofmt` from GOBIN (if defined) or GOROOT.

Fixes #10755

Change-Id: I56d42a747319c766f2911508fab3994c3a366d12
Reviewed-on: https://go-review.googlesource.com/9900
Reviewed-by: Rob Pike 
---
 src/cmd/go/fmt.go | 29 ++++++++++++++++++++++++++++-
 1 file changed, 28 insertions(+), 1 deletion(-)

diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go
index 65dc3ca599..e40b0dc65f 100644
--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -4,6 +4,12 @@
 
 package main
 
+import (
+	"os"
+	"path/filepath"
+	"runtime"
+)
+
 func init() {
 	addBuildFlagsNX(cmdFmt)
 }
@@ -29,10 +35,31 @@ See also: go fix, go vet.
 }
 
 func runFmt(cmd *Command, args []string) {
+	gofmt := gofmtPath()
 	for _, pkg := range packages(args) {
 		// Use pkg.gofiles instead of pkg.Dir so that
 		// the command only applies to this package,
 		// not to packages in subdirectories.
-		run(stringList("gofmt", "-l", "-w", relPaths(pkg.allgofiles)))
+		run(stringList(gofmt, "-l", "-w", relPaths(pkg.allgofiles)))
 	}
 }
+
+func gofmtPath() string {
+	gofmt := "gofmt"
+	if toolIsWindows {
+		gofmt += toolWindowsExtension
+	}
+
+	gofmtPath := filepath.Join(gobin, gofmt)
+	if _, err := os.Stat(gofmtPath); err == nil {
+		return gofmtPath
+	}
+
+	gofmtPath = filepath.Join(goroot, "bin", gofmt)
+	if _, err := os.Stat(gofmtPath); err == nil {
+		return gofmtPath
+	}
+
+	// fallback to looking for gofmt in $PATH
+	return "gofmt"
+}

From f763da3d3404673484bcc9d0d911c75a40e326c2 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Tue, 19 May 2015 16:58:14 -0400
Subject: [PATCH 155/232] cmd/internal/gc: remove incorrect "write barrier
 prohibited" error

Commit 9c9e36b pushed these errors down to where the write barriers
are actually emitted, but forgot to remove the original error that was
being pushed down.

Change-Id: I751752a896e78fb9e63d69f88e7fb8d1ff5d344c
Reviewed-on: https://go-review.googlesource.com/10264
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/walk.go | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/internal/gc/walk.go
index 36e4d66b33..b5b8611e5b 100644
--- a/src/cmd/internal/gc/walk.go
+++ b/src/cmd/internal/gc/walk.go
@@ -2206,9 +2206,6 @@ var applywritebarrier_bv Bvec
 
 func applywritebarrier(n *Node, init **NodeList) *Node {
 	if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
-		if Curfn != nil && Curfn.Func.Nowritebarrier {
-			Yyerror("write barrier prohibited")
-		}
 		if flag_race == 0 {
 			if Debug_wb > 1 {
 				Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))

From d6bbcea22a3d4cbcf8350b4b861f0d73ab142ac2 Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Tue, 19 May 2015 18:25:22 -0400
Subject: [PATCH 156/232] cmd/go: fix build

Change-Id: Ib6c121414c74f8a40eb87a52af8737502ce7216d
Reviewed-on: https://go-review.googlesource.com/10265
Reviewed-by: Josh Bleecher Snyder 
---
 src/cmd/go/fmt.go | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/cmd/go/fmt.go b/src/cmd/go/fmt.go
index e40b0dc65f..1722b9d568 100644
--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -7,7 +7,6 @@ package main
 import (
 	"os"
 	"path/filepath"
-	"runtime"
 )
 
 func init() {

From a1c1a763bc7c8d10ec30a7fa60ecf7d5f9a6f1c8 Mon Sep 17 00:00:00 2001
From: Didier Spezia 
Date: Thu, 14 May 2015 22:36:59 +0000
Subject: [PATCH 157/232] html/template: fix string iteration in replacement
 operations

In css, js, and html, the replacement operations are implemented
by iterating on strings (rune by rune). The for/range
statement is used. The length of the rune is required
and added to the index to properly slice the string.

This is potentially wrong because there is a discrepancy between
the result of utf8.RuneLen and the increment of the index
(set by the for/range statement). For invalid strings,
utf8.RuneLen('\ufffd') == 3, while the index is incremented
only by 1 byte.

htmlReplacer triggers a panic at slicing time for some
invalid strings.

Use a more robust iteration mechanism based on
utf8.DecodeRuneInString, and make sure the same
pattern is used for all similar functions in this
package.

Fixes #10799

Change-Id: Ibad3857b2819435d9fa564f06fc2ca8774102841
Reviewed-on: https://go-review.googlesource.com/10105
Reviewed-by: Rob Pike 
---
 src/html/template/css.go       | 76 ++++++++++++++--------------------
 src/html/template/html.go      | 13 +++---
 src/html/template/html_test.go |  9 ++--
 src/html/template/js.go        |  8 ++--
 4 files changed, 51 insertions(+), 55 deletions(-)

diff --git a/src/html/template/css.go b/src/html/template/css.go
index 634f183f79..318464835f 100644
--- a/src/html/template/css.go
+++ b/src/html/template/css.go
@@ -157,56 +157,20 @@ func isCSSSpace(b byte) bool {
 func cssEscaper(args ...interface{}) string {
 	s, _ := stringify(args...)
 	var b bytes.Buffer
-	written := 0
-	for i, r := range s {
+	r, w, written := rune(0), 0, 0
+	for i := 0; i < len(s); i += w {
+		// See comment in htmlEscaper.
+		r, w = utf8.DecodeRuneInString(s[i:])
 		var repl string
-		switch r {
-		case 0:
-			repl = `\0`
-		case '\t':
-			repl = `\9`
-		case '\n':
-			repl = `\a`
-		case '\f':
-			repl = `\c`
-		case '\r':
-			repl = `\d`
-		// Encode HTML specials as hex so the output can be embedded
-		// in HTML attributes without further encoding.
-		case '"':
-			repl = `\22`
-		case '&':
-			repl = `\26`
-		case '\'':
-			repl = `\27`
-		case '(':
-			repl = `\28`
-		case ')':
-			repl = `\29`
-		case '+':
-			repl = `\2b`
-		case '/':
-			repl = `\2f`
-		case ':':
-			repl = `\3a`
-		case ';':
-			repl = `\3b`
-		case '<':
-			repl = `\3c`
-		case '>':
-			repl = `\3e`
-		case '\\':
-			repl = `\\`
-		case '{':
-			repl = `\7b`
-		case '}':
-			repl = `\7d`
+		switch {
+		case int(r) < len(cssReplacementTable) && cssReplacementTable[r] != "":
+			repl = cssReplacementTable[r]
 		default:
 			continue
 		}
 		b.WriteString(s[written:i])
 		b.WriteString(repl)
-		written = i + utf8.RuneLen(r)
+		written = i + w
 		if repl != `\\` && (written == len(s) || isHex(s[written]) || isCSSSpace(s[written])) {
 			b.WriteByte(' ')
 		}
@@ -218,6 +182,30 @@ func cssEscaper(args ...interface{}) string {
 	return b.String()
 }
 
+var cssReplacementTable = []string{
+	0:    `\0`,
+	'\t': `\9`,
+	'\n': `\a`,
+	'\f': `\c`,
+	'\r': `\d`,
+	// Encode HTML specials as hex so the output can be embedded
+	// in HTML attributes without further encoding.
+	'"':  `\22`,
+	'&':  `\26`,
+	'\'': `\27`,
+	'(':  `\28`,
+	')':  `\29`,
+	'+':  `\2b`,
+	'/':  `\2f`,
+	':':  `\3a`,
+	';':  `\3b`,
+	'<':  `\3c`,
+	'>':  `\3e`,
+	'\\': `\\`,
+	'{':  `\7b`,
+	'}':  `\7d`,
+}
+
 var expressionBytes = []byte("expression")
 var mozBindingBytes = []byte("mozbinding")
 
diff --git a/src/html/template/html.go b/src/html/template/html.go
index 9c069efd1d..de4aa4abb2 100644
--- a/src/html/template/html.go
+++ b/src/html/template/html.go
@@ -138,21 +138,24 @@ var htmlNospaceNormReplacementTable = []string{
 // and when badRunes is true, certain bad runes are allowed through unescaped.
 func htmlReplacer(s string, replacementTable []string, badRunes bool) string {
 	written, b := 0, new(bytes.Buffer)
-	for i, r := range s {
+	r, w := rune(0), 0
+	for i := 0; i < len(s); i += w {
+		// Cannot use 'for range s' because we need to preserve the width
+		// of the runes in the input. If we see a decoding error, the input
+		// width will not be utf8.Runelen(r) and we will overrun the buffer.
+		r, w = utf8.DecodeRuneInString(s[i:])
 		if int(r) < len(replacementTable) {
 			if repl := replacementTable[r]; len(repl) != 0 {
 				b.WriteString(s[written:i])
 				b.WriteString(repl)
-				// Valid as long as replacementTable doesn't
-				// include anything above 0x7f.
-				written = i + utf8.RuneLen(r)
+				written = i + w
 			}
 		} else if badRunes {
 			// No-op.
 			// IE does not allow these ranges in unquoted attrs.
 		} else if 0xfdd0 <= r && r <= 0xfdef || 0xfff0 <= r && r <= 0xffff {
 			fmt.Fprintf(b, "%s&#x%x;", s[written:i], r)
-			written = i + utf8.RuneLen(r)
+			written = i + w
 		}
 	}
 	if written == 0 {
diff --git a/src/html/template/html_test.go b/src/html/template/html_test.go
index b9b9703875..f04ee04c27 100644
--- a/src/html/template/html_test.go
+++ b/src/html/template/html_test.go
@@ -19,7 +19,8 @@ func TestHTMLNospaceEscaper(t *testing.T) {
 		`PQRSTUVWXYZ[\]^_` +
 		"`abcdefghijklmno" +
 		"pqrstuvwxyz{|}~\x7f" +
-		"\u00A0\u0100\u2028\u2029\ufeff\ufdec\U0001D11E")
+		"\u00A0\u0100\u2028\u2029\ufeff\ufdec\U0001D11E" +
+		"erroneous\x960") // keep at the end
 
 	want := ("�\x01\x02\x03\x04\x05\x06\x07" +
 		"\x08	

\x0E\x0F" +
@@ -31,14 +32,16 @@ func TestHTMLNospaceEscaper(t *testing.T) {
 		`PQRSTUVWXYZ[\]^_` +
 		``abcdefghijklmno` +
 		`pqrstuvwxyz{|}~` + "\u007f" +
-		"\u00A0\u0100\u2028\u2029\ufeff﷬\U0001D11E")
+		"\u00A0\u0100\u2028\u2029\ufeff﷬\U0001D11E" +
+		"erroneous�0") // keep at the end
 
 	got := htmlNospaceEscaper(input)
 	if got != want {
 		t.Errorf("encode: want\n\t%q\nbut got\n\t%q", want, got)
 	}
 
-	got, want = html.UnescapeString(got), strings.Replace(input, "\x00", "\ufffd", 1)
+	r := strings.NewReplacer("\x00", "\ufffd", "\x96", "\ufffd")
+	got, want = html.UnescapeString(got), r.Replace(input)
 	if want != got {
 		t.Errorf("decode: want\n\t%q\nbut got\n\t%q", want, got)
 	}
diff --git a/src/html/template/js.go b/src/html/template/js.go
index 999a61ed07..f6d166b311 100644
--- a/src/html/template/js.go
+++ b/src/html/template/js.go
@@ -246,8 +246,10 @@ func jsRegexpEscaper(args ...interface{}) string {
 // `\u2029`.
 func replace(s string, replacementTable []string) string {
 	var b bytes.Buffer
-	written := 0
-	for i, r := range s {
+	r, w, written := rune(0), 0, 0
+	for i := 0; i < len(s); i += w {
+		// See comment in htmlEscaper.
+		r, w = utf8.DecodeRuneInString(s[i:])
 		var repl string
 		switch {
 		case int(r) < len(replacementTable) && replacementTable[r] != "":
@@ -261,7 +263,7 @@ func replace(s string, replacementTable []string) string {
 		}
 		b.WriteString(s[written:i])
 		b.WriteString(repl)
-		written = i + utf8.RuneLen(r)
+		written = i + w
 	}
 	if written == 0 {
 		return s

From 791bb4f5aefdf3d7ae8aea7d360dec6cb238698a Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Tue, 19 May 2015 03:39:30 -0400
Subject: [PATCH 158/232] cmd/internal/gc: handle 64-bit const i/j/k in
 cgen_slice on ARM

386 is not affected because it doesn't use ginscmp.

Fixes #10843.

Change-Id: I1b3a133bd1e5fabc85236f15d060dbaa4c391cf3
Reviewed-on: https://go-review.googlesource.com/10116
Reviewed-by: Russ Cox 
---
 src/cmd/internal/gc/cgen.go | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/internal/gc/cgen.go
index e003ea9f4f..ca58b1c6a3 100644
--- a/src/cmd/internal/gc/cgen.go
+++ b/src/cmd/internal/gc/cgen.go
@@ -2987,6 +2987,11 @@ func cgen_append(n, res *Node) {
 // If wb is true, need write barrier updating res's base pointer.
 // On systems with 32-bit ints, i, j, k are guaranteed to be 32-bit values.
 func cgen_slice(n, res *Node, wb bool) {
+	if Debug['g'] != 0 {
+		Dump("cgen_slice-n", n)
+		Dump("cgen_slice-res", res)
+	}
+
 	needFullUpdate := !samesafeexpr(n.Left, res)
 
 	// orderexpr has made sure that x is safe (but possibly expensive)
@@ -3250,6 +3255,16 @@ func cgen_slice(n, res *Node, wb bool) {
 	}
 
 	compare := func(n1, n2 *Node) {
+		// n1 might be a 64-bit constant, even on 32-bit architectures,
+		// but it will be represented in 32 bits.
+		if Ctxt.Arch.Regsize == 4 && Is64(n1.Type) {
+			if mpcmpfixc(n1.Val.U.(*Mpint), 1<<31) >= 0 {
+				Fatal("missed slice out of bounds check")
+			}
+			var tmp Node
+			Nodconst(&tmp, indexRegType, Mpgetfix(n1.Val.U.(*Mpint)))
+			n1 = &tmp
+		}
 		p := Thearch.Ginscmp(OGT, indexRegType, n1, n2, -1)
 		panics = append(panics, p)
 	}

From c013417a45fbf950e8eb5a5e2ae37d2219c9e1d8 Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Tue, 19 May 2015 02:48:15 -0400
Subject: [PATCH 159/232] misc/cgo/testshared: when checking for RPATHs also
 look for DT_RUNPATH

On my systems, ld -rpath sets DT_RUNPATH instead of DT_RPATH.

Change-Id: I5047e795fb7ef9336f5fa13ba24bb6245c0b0582
Reviewed-on: https://go-review.googlesource.com/10260
Reviewed-by: Michael Hudson-Doyle 
Reviewed-by: Ian Lance Taylor 
---
 misc/cgo/testshared/shared_test.go | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go
index 81b9dffb07..fd577b03b5 100644
--- a/misc/cgo/testshared/shared_test.go
+++ b/misc/cgo/testshared/shared_test.go
@@ -192,10 +192,12 @@ func AssertIsLinkedTo(t *testing.T, path, lib string) {
 }
 
 func AssertHasRPath(t *testing.T, path, dir string) {
-	for _, dynstring := range dynStrings(path, elf.DT_RPATH) {
-		for _, rpath := range strings.Split(dynstring, ":") {
-			if filepath.Clean(rpath) == filepath.Clean(dir) {
-				return
+	for _, tag := range []elf.DynTag{elf.DT_RPATH, elf.DT_RUNPATH} {
+		for _, dynstring := range dynStrings(path, tag) {
+			for _, rpath := range strings.Split(dynstring, ":") {
+				if filepath.Clean(rpath) == filepath.Clean(dir) {
+					return
+				}
 			}
 		}
 	}

From 7bdb4a28a87f24a6c00ae3194598934e0fa142f0 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Tue, 19 May 2015 23:49:24 +0000
Subject: [PATCH 160/232] Revert "cmd/internal/ld: output dwarf in external
 link mode on darwin"

This reverts commit 8b83306cf20abed54d7cb23a3f3091b7e6202056.

Change-Id: I3fb998bdf11eceef13e3997e336d86e7c5d47a60
Reviewed-on: https://go-review.googlesource.com/10254
Reviewed-by: Minux Ma 
---
 src/cmd/5l/asm.go                          |  14 +-
 src/cmd/6l/asm.go                          |   2 +-
 src/cmd/7l/asm.go                          |  14 +-
 src/cmd/8l/asm.go                          |   2 +-
 src/cmd/internal/ld/dwarf.go               | 155 ++-------
 src/cmd/internal/ld/lib.go                 |  21 +-
 src/cmd/internal/ld/macho.go               |   8 +-
 src/cmd/internal/ld/macho_combine_dwarf.go | 369 ---------------------
 8 files changed, 58 insertions(+), 527 deletions(-)
 delete mode 100644 src/cmd/internal/ld/macho_combine_dwarf.go

diff --git a/src/cmd/5l/asm.go b/src/cmd/5l/asm.go
index 70d6790fc1..1b69671b9f 100644
--- a/src/cmd/5l/asm.go
+++ b/src/cmd/5l/asm.go
@@ -533,12 +533,14 @@ func asmb() {
 			fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
 		}
 
-		dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
-		ld.Cseek(int64(dwarfoff))
+		if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
+			dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+			ld.Cseek(int64(dwarfoff))
 
-		ld.Segdwarf.Fileoff = uint64(ld.Cpos())
-		ld.Dwarfemitdebugsections()
-		ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
+			ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+			ld.Dwarfemitdebugsections()
+			ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
+		}
 
 		machlink = uint32(ld.Domacholink())
 	}
@@ -565,7 +567,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
diff --git a/src/cmd/6l/asm.go b/src/cmd/6l/asm.go
index 02b4c7cdd2..5520a5acf1 100644
--- a/src/cmd/6l/asm.go
+++ b/src/cmd/6l/asm.go
@@ -710,7 +710,7 @@ func asmb() {
 			symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = int64(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
 
 		case obj.Hlinux,
 			obj.Hfreebsd,
diff --git a/src/cmd/7l/asm.go b/src/cmd/7l/asm.go
index 064ff56283..3dfb8c666d 100644
--- a/src/cmd/7l/asm.go
+++ b/src/cmd/7l/asm.go
@@ -317,12 +317,14 @@ func asmb() {
 			fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
 		}
 
-		dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
-		ld.Cseek(int64(dwarfoff))
+		if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
+			dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+			ld.Cseek(int64(dwarfoff))
 
-		ld.Segdwarf.Fileoff = uint64(ld.Cpos())
-		ld.Dwarfemitdebugsections()
-		ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
+			ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+			ld.Dwarfemitdebugsections()
+			ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
+		}
 
 		machlink = uint32(ld.Domacholink())
 	}
@@ -349,7 +351,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
diff --git a/src/cmd/8l/asm.go b/src/cmd/8l/asm.go
index a736d43686..a63c51f58d 100644
--- a/src/cmd/8l/asm.go
+++ b/src/cmd/8l/asm.go
@@ -551,7 +551,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
 
 		case obj.Hwindows:
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/internal/ld/dwarf.go
index 841ef9abf9..476b329e7a 100644
--- a/src/cmd/internal/ld/dwarf.go
+++ b/src/cmd/internal/ld/dwarf.go
@@ -17,7 +17,6 @@ package ld
 import (
 	"cmd/internal/obj"
 	"fmt"
-	"os"
 	"strings"
 )
 
@@ -241,7 +240,6 @@ var abbrevs = [DW_NABRV]DWAbbrev{
 			{DW_AT_low_pc, DW_FORM_addr},
 			{DW_AT_high_pc, DW_FORM_addr},
 			{DW_AT_stmt_list, DW_FORM_data4},
-			{DW_AT_comp_dir, DW_FORM_string},
 		},
 	},
 
@@ -696,9 +694,6 @@ func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64)
 	if Iself && Thearch.Thechar == '6' {
 		addend = 0
 	}
-	if HEADTYPE == obj.Hdarwin {
-		addend += sym.Value
-	}
 	switch siz {
 	case 4:
 		Thearch.Lput(uint32(addend))
@@ -1552,13 +1547,6 @@ func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_len
 	}
 }
 
-func getCompilationDir() string {
-	if dir, err := os.Getwd(); err == nil {
-		return dir
-	}
-	return "/"
-}
-
 func writelines() {
 	if linesec == nil {
 		linesec = Linklookup(Ctxt, ".dwarfline", 0)
@@ -1583,9 +1571,6 @@ func writelines() {
 	newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0)
 	newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart-lineo, 0)
 	newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s)
-	// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
-	compDir := getCompilationDir()
-	newattr(dwinfo, DW_AT_comp_dir, DW_CLS_STRING, int64(len(compDir)), compDir)
 
 	// Write .debug_line Line Number Program Header (sec 6.2.4)
 	// Fields marked with (*) must be changed for 64-bit dwarf
@@ -2098,14 +2083,6 @@ func writedwarfreloc(s *LSym) int64 {
 	return start
 }
 
-func addmachodwarfsect(prev *Section, name string) *Section {
-	sect := addsection(&Segdwarf, name, 04)
-	sect.Extnum = prev.Extnum + 1
-	sym := Linklookup(Ctxt, name, 0)
-	sym.Sect = sect
-	return sect
-}
-
 /*
  * This is the main entry point for generating dwarf.  After emitting
  * the mandatory debug_abbrev section, it calls writelines() to set up
@@ -2120,32 +2097,8 @@ func Dwarfemitdebugsections() {
 		return
 	}
 
-	if Linkmode == LinkExternal {
-		if !Iself && HEADTYPE != obj.Hdarwin {
-			return
-		}
-		if HEADTYPE == obj.Hdarwin {
-			sect := Segdata.Sect
-			// find the last section.
-			for sect.Next != nil {
-				sect = sect.Next
-			}
-			sect = addmachodwarfsect(sect, ".debug_abbrev")
-			sect = addmachodwarfsect(sect, ".debug_line")
-			sect = addmachodwarfsect(sect, ".debug_frame")
-			sect = addmachodwarfsect(sect, ".debug_info")
-		}
-		infosym = Linklookup(Ctxt, ".debug_info", 0)
-		infosym.Hide = 1
-
-		abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
-		abbrevsym.Hide = 1
-
-		linesym = Linklookup(Ctxt, ".debug_line", 0)
-		linesym.Hide = 1
-
-		framesym = Linklookup(Ctxt, ".debug_frame", 0)
-		framesym.Hide = 1
+	if Linkmode == LinkExternal && !Iself {
+		return
 	}
 
 	// For diagnostic messages.
@@ -2238,15 +2191,6 @@ func Dwarfemitdebugsections() {
 	for Cpos()&7 != 0 {
 		Cput(0)
 	}
-	if HEADTYPE != obj.Hdarwin {
-		dwarfemitreloc()
-	}
-}
-
-func dwarfemitreloc() {
-	if Debug['w'] != 0 { // disable dwarf
-		return
-	}
 	inforeloco = writedwarfreloc(infosec)
 	inforelocsize = Cpos() - inforeloco
 	align(inforelocsize)
@@ -2319,6 +2263,18 @@ func dwarfaddshstrings(shstrtab *LSym) {
 			elfstrdbg[ElfStrRelDebugLine] = Addstring(shstrtab, ".rel.debug_line")
 			elfstrdbg[ElfStrRelDebugFrame] = Addstring(shstrtab, ".rel.debug_frame")
 		}
+
+		infosym = Linklookup(Ctxt, ".debug_info", 0)
+		infosym.Hide = 1
+
+		abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
+		abbrevsym.Hide = 1
+
+		linesym = Linklookup(Ctxt, ".debug_line", 0)
+		linesym.Hide = 1
+
+		framesym = Linklookup(Ctxt, ".debug_frame", 0)
+		framesym.Hide = 1
 	}
 }
 
@@ -2464,15 +2420,14 @@ func dwarfaddelfheaders() {
 /*
  * Macho
  */
-func dwarfaddmachoheaders(ms *MachoSeg) {
+func dwarfaddmachoheaders() {
 	if Debug['w'] != 0 { // disable dwarf
 		return
 	}
 
 	// Zero vsize segments won't be loaded in memory, even so they
 	// have to be page aligned in the file.
-	fakestart := Rnd(int64(Segdwarf.Fileoff), 0x1000)
-	addr := Segdata.Vaddr + Segdata.Length
+	fakestart := abbrevo &^ 0xfff
 
 	nsect := 4
 	if pubnamessize > 0 {
@@ -2488,94 +2443,57 @@ func dwarfaddmachoheaders(ms *MachoSeg) {
 		nsect++
 	}
 
-	if Linkmode != LinkExternal {
-		ms = newMachoSeg("__DWARF", nsect)
-		ms.fileoffset = uint64(fakestart)
-		ms.filesize = Segdwarf.Filelen
-		ms.vaddr = addr
-	}
+	ms := newMachoSeg("__DWARF", nsect)
+	ms.fileoffset = uint64(fakestart)
+	ms.filesize = uint64(abbrevo) - uint64(fakestart)
+	ms.vaddr = ms.fileoffset + Segdata.Vaddr - Segdata.Fileoff
 
 	msect := newMachoSect(ms, "__debug_abbrev", "__DWARF")
 	msect.off = uint32(abbrevo)
 	msect.size = uint64(abbrevsize)
-	msect.addr = addr
-	addr += msect.size
-	msect.flag = 0x02000000
-	if abbrevsym != nil {
-		abbrevsym.Value = int64(msect.addr)
-	}
+	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+	ms.filesize += msect.size
 
 	msect = newMachoSect(ms, "__debug_line", "__DWARF")
 	msect.off = uint32(lineo)
 	msect.size = uint64(linesize)
-	msect.addr = addr
-	addr += msect.size
-	msect.flag = 0x02000000
-	if linesym != nil {
-		linesym.Value = int64(msect.addr)
-	}
-	if linerelocsize > 0 {
-		msect.nreloc = uint32(len(linesec.R))
-		msect.reloc = uint32(linereloco)
-	}
+	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+	ms.filesize += msect.size
 
 	msect = newMachoSect(ms, "__debug_frame", "__DWARF")
 	msect.off = uint32(frameo)
 	msect.size = uint64(framesize)
-	msect.addr = addr
-	addr += msect.size
-	msect.flag = 0x02000000
-	if framesym != nil {
-		framesym.Value = int64(msect.addr)
-	}
-	if framerelocsize > 0 {
-		msect.nreloc = uint32(len(framesec.R))
-		msect.reloc = uint32(framereloco)
-	}
+	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+	ms.filesize += msect.size
 
 	msect = newMachoSect(ms, "__debug_info", "__DWARF")
 	msect.off = uint32(infoo)
 	msect.size = uint64(infosize)
-	msect.addr = addr
-	addr += msect.size
-	msect.flag = 0x02000000
-	if infosym != nil {
-		infosym.Value = int64(msect.addr)
-	}
-	if inforelocsize > 0 {
-		msect.nreloc = uint32(len(infosec.R))
-		msect.reloc = uint32(inforeloco)
-	}
+	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+	ms.filesize += msect.size
 
 	if pubnamessize > 0 {
 		msect := newMachoSect(ms, "__debug_pubnames", "__DWARF")
 		msect.off = uint32(pubnameso)
 		msect.size = uint64(pubnamessize)
-		msect.addr = addr
-		addr += msect.size
-		msect.flag = 0x02000000
+		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+		ms.filesize += msect.size
 	}
 
 	if pubtypessize > 0 {
 		msect := newMachoSect(ms, "__debug_pubtypes", "__DWARF")
 		msect.off = uint32(pubtypeso)
 		msect.size = uint64(pubtypessize)
-		msect.addr = addr
-		addr += msect.size
-		msect.flag = 0x02000000
+		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+		ms.filesize += msect.size
 	}
 
 	if arangessize > 0 {
 		msect := newMachoSect(ms, "__debug_aranges", "__DWARF")
 		msect.off = uint32(arangeso)
 		msect.size = uint64(arangessize)
-		msect.addr = addr
-		addr += msect.size
-		msect.flag = 0x02000000
-		if arangesrelocsize > 0 {
-			msect.nreloc = uint32(len(arangessec.R))
-			msect.reloc = uint32(arangesreloco)
-		}
+		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+		ms.filesize += msect.size
 	}
 
 	// TODO(lvd) fix gdb/python to load MachO (16 char section name limit)
@@ -2583,9 +2501,8 @@ func dwarfaddmachoheaders(ms *MachoSeg) {
 		msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF")
 		msect.off = uint32(gdbscripto)
 		msect.size = uint64(gdbscriptsize)
-		msect.addr = addr
-		addr += msect.size
-		msect.flag = 0x02000000
+		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
+		ms.filesize += msect.size
 	}
 }
 
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index a36cd0f8f4..753e8eebd8 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -922,7 +922,7 @@ func hostlink() {
 	}
 
 	if HEADTYPE == obj.Hdarwin {
-		argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000,-headerpad,1144")
+		argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000")
 	}
 	if HEADTYPE == obj.Hopenbsd {
 		argv = append(argv, "-Wl,-nopie")
@@ -1029,25 +1029,6 @@ func hostlink() {
 	if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
 		Exitf("running %s failed: %v\n%s", argv[0], err, out)
 	}
-
-	if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin {
-		dsym := fmt.Sprintf("%s/go.dwarf", tmpdir)
-		if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil {
-			Ctxt.Cursym = nil
-			Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
-		}
-		combinedOutput := fmt.Sprintf("%s/go.combined", tmpdir)
-		if err := machoCombineDwarf(outfile, dsym, combinedOutput); err != nil {
-			Ctxt.Cursym = nil
-			Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
-		}
-		origOutput := fmt.Sprintf("%s/go.orig", tmpdir)
-		os.Rename(outfile, origOutput)
-		if err := os.Rename(combinedOutput, outfile); err != nil {
-			Ctxt.Cursym = nil
-			Exitf("%s: rename(%s, %s) failed: %v", os.Args[0], combinedOutput, outfile, err)
-		}
-	}
 }
 
 func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) {
diff --git a/src/cmd/internal/ld/macho.go b/src/cmd/internal/ld/macho.go
index 0258aff104..ceeb7b0f5d 100644
--- a/src/cmd/internal/ld/macho.go
+++ b/src/cmd/internal/ld/macho.go
@@ -443,8 +443,7 @@ func Asmbmacho() {
 		ms = newMachoSeg("", 40)
 
 		ms.fileoffset = Segtext.Fileoff
-		ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
-		ms.vsize = ms.filesize
+		ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff
 	}
 
 	/* segment for zero page */
@@ -562,8 +561,8 @@ func Asmbmacho() {
 	}
 
 	// TODO: dwarf headers go in ms too
-	if Debug['s'] == 0 {
-		dwarfaddmachoheaders(ms)
+	if Debug['s'] == 0 && Linkmode != LinkExternal {
+		dwarfaddmachoheaders()
 	}
 
 	a := machowrite()
@@ -851,5 +850,4 @@ func Machoemitreloc() {
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 		machorelocsect(sect, datap)
 	}
-	dwarfemitreloc()
 }
diff --git a/src/cmd/internal/ld/macho_combine_dwarf.go b/src/cmd/internal/ld/macho_combine_dwarf.go
deleted file mode 100644
index 9134373a52..0000000000
--- a/src/cmd/internal/ld/macho_combine_dwarf.go
+++ /dev/null
@@ -1,369 +0,0 @@
-// Copyright 2015 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package ld
-
-import (
-	"bytes"
-	"debug/macho"
-	"encoding/binary"
-	"fmt"
-	"io"
-	"os"
-	"reflect"
-	"unsafe"
-)
-
-var fakedwarf, realdwarf, linkseg *macho.Segment
-var dwarfstart, linkstart int64
-var linkoffset uint32
-var machHeader *macho.FileHeader
-var mappedHeader []byte
-
-const (
-	LC_LOAD_DYLINKER        = 0xe
-	LC_PREBOUND_DYLIB       = 0x10
-	LC_LOAD_WEAK_DYLIB      = 0x18
-	LC_UUID                 = 0x1b
-	LC_RPATH                = 0x8000001c
-	LC_CODE_SIGNATURE       = 0x1d
-	LC_SEGMENT_SPLIT_INFO   = 0x1e
-	LC_REEXPORT_DYLIB       = 0x8000001f
-	LC_ENCRYPTION_INFO      = 0x21
-	LC_DYLD_INFO            = 0x22
-	LC_DYLD_INFO_ONLY       = 0x80000022
-	LC_VERSION_MIN_MACOSX   = 0x24
-	LC_VERSION_MIN_IPHONEOS = 0x25
-	LC_FUNCTION_STARTS      = 0x26
-	LC_MAIN                 = 0x80000028
-	LC_DATA_IN_CODE         = 0x29
-	LC_SOURCE_VERSION       = 0x2A
-	LC_DYLIB_CODE_SIGN_DRS  = 0x2B
-	LC_ENCRYPTION_INFO_64   = 0x2C
-
-	dwarfMinAlign = 6  // 64 = 1 << 6
-	pageAlign     = 12 // 4096 = 1 << 12
-)
-
-type loadCmd struct {
-	Cmd macho.LoadCmd
-	Len uint32
-}
-
-type dyldInfoCmd struct {
-	Cmd                      macho.LoadCmd
-	Len                      uint32
-	RebaseOff, RebaseLen     uint32
-	BindOff, BindLen         uint32
-	WeakBindOff, WeakBindLen uint32
-	LazyBindOff, LazyBindLen uint32
-	ExportOff, ExportLen     uint32
-}
-
-type linkEditDataCmd struct {
-	Cmd              macho.LoadCmd
-	Len              uint32
-	DataOff, DataLen uint32
-}
-
-type encryptionInfoCmd struct {
-	Cmd                macho.LoadCmd
-	Len                uint32
-	CryptOff, CryptLen uint32
-	CryptId            uint32
-}
-
-type loadCmdReader struct {
-	offset, next int64
-	f            *os.File
-	order        binary.ByteOrder
-}
-
-func (r *loadCmdReader) Next() (cmd loadCmd, err error) {
-	r.offset = r.next
-	if _, err = r.f.Seek(r.offset, 0); err != nil {
-		return
-	}
-	if err = binary.Read(r.f, r.order, &cmd); err != nil {
-		return
-	}
-	r.next = r.offset + int64(cmd.Len)
-	return
-}
-
-func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
-	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
-		return err
-	}
-	return binary.Read(r.f, r.order, data)
-}
-
-func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
-	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
-		return err
-	}
-	return binary.Write(r.f, r.order, data)
-}
-
-// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable.
-// With internal linking, DWARF is embedded into the executable, this lets us do the
-// same for external linking.
-// inexe is the path to the executable with no DWARF. It must have enough room in the macho
-// header to add the DWARF sections. (Use ld's -headerpad option)
-// dsym is the path to the macho file containing DWARF from dsymutil.
-// outexe is the path where the combined executable should be saved.
-func machoCombineDwarf(inexe, dsym, outexe string) error {
-	exef, err := os.Open(inexe)
-	if err != nil {
-		return err
-	}
-	dwarff, err := os.Open(dsym)
-	if err != nil {
-		return err
-	}
-	outf, err := os.Create(outexe)
-	if err != nil {
-		return err
-	}
-	outf.Chmod(0755)
-
-	exem, err := macho.NewFile(exef)
-	if err != nil {
-		return err
-	}
-	dwarfm, err := macho.NewFile(dwarff)
-	if err != nil {
-		return err
-	}
-
-	// The string table needs to be the last thing in the file
-	// for code signing to work. So we'll need to move the
-	// linkedit section, but all the others can be copied directly.
-	linkseg = exem.Segment("__LINKEDIT")
-	if linkseg == nil {
-		return fmt.Errorf("missing __LINKEDIT segment")
-	}
-
-	if _, err = exef.Seek(0, 0); err != nil {
-		return err
-	}
-	if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
-		return err
-	}
-
-	realdwarf = dwarfm.Segment("__DWARF")
-	if realdwarf == nil {
-		return fmt.Errorf("missing __DWARF segment")
-	}
-
-	// Now copy the dwarf data into the output.
-	maxalign := uint32(dwarfMinAlign) //
-	for _, sect := range dwarfm.Sections {
-		if sect.Align > maxalign {
-			maxalign = sect.Align
-		}
-	}
-	dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
-	if _, err = outf.Seek(dwarfstart, 0); err != nil {
-		return err
-	}
-
-	if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
-		return err
-	}
-	if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
-		return err
-	}
-
-	// And finally the linkedit section.
-	if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil {
-		return err
-	}
-	linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+realdwarf.Filesz, pageAlign)
-	linkoffset = uint32(linkstart - int64(linkseg.Offset))
-	if _, err = outf.Seek(linkstart, 0); err != nil {
-		return err
-	}
-	if _, err := io.Copy(outf, exef); err != nil {
-		return err
-	}
-
-	// Now we need to update the headers.
-	cmdOffset := unsafe.Sizeof(exem.FileHeader)
-	is64bit := exem.Magic == macho.Magic64
-	if is64bit {
-		// mach_header_64 has one extra uint32.
-		cmdOffset += unsafe.Sizeof(exem.Magic)
-	}
-
-	textsect := exem.Section("__text")
-	if linkseg == nil {
-		return fmt.Errorf("missing __text section")
-	}
-
-	dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz)
-	availablePadding := int64(textsect.Offset) - dwarfCmdOffset
-	if availablePadding < int64(realdwarf.Len) {
-		return fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
-	}
-	// First, copy the dwarf load command into the header
-	if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil {
-		return err
-	}
-	if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
-		return err
-	}
-
-	if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
-		return err
-	}
-	if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
-		return err
-	}
-	if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
-		return err
-	}
-
-	reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
-	for i := uint32(0); i < exem.Ncmd; i++ {
-		cmd, err := reader.Next()
-		if err != nil {
-			return err
-		}
-		switch cmd.Cmd {
-		case macho.LoadCmdSegment64:
-			err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{})
-		case macho.LoadCmdSegment:
-			err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{})
-		case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
-			err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
-		case macho.LoadCmdSymtab:
-			err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff")
-		case macho.LoadCmdDysymtab:
-			err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
-		case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS:
-			err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff")
-		case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
-			err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff")
-		case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH:
-			// Nothing to update
-		default:
-			err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd)
-		}
-		if err != nil {
-			return err
-		}
-	}
-	return machoUpdateDwarfHeader(&reader)
-}
-
-// machoUpdateSegment updates the load command for a moved segment.
-// Only the linkedit segment should move, and it should have 0 sections.
-// seg should be a macho.Segment32 or macho.Segment64 as appropriate.
-// sect should be a macho.Section32 or macho.Section64 as appropriate.
-func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
-	if err := r.ReadAt(0, seg); err != nil {
-		return err
-	}
-	segValue := reflect.ValueOf(seg)
-	offset := reflect.Indirect(segValue).FieldByName("Offset")
-
-	// Only the linkedit segment moved, any thing before that is fine.
-	if offset.Uint() < linkseg.Offset {
-		return nil
-	}
-	offset.SetUint(offset.Uint() + uint64(linkoffset))
-	if err := r.WriteAt(0, seg); err != nil {
-		return err
-	}
-	// There shouldn't be any sections, but just to make sure...
-	return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
-}
-
-func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
-	iseg := reflect.Indirect(seg)
-	nsect := iseg.FieldByName("Nsect").Uint()
-	if nsect == 0 {
-		return nil
-	}
-	sectOffset := int64(iseg.Type().Size())
-
-	isect := reflect.Indirect(sect)
-	offsetField := isect.FieldByName("Offset")
-	reloffField := isect.FieldByName("Reloff")
-	sectSize := int64(isect.Type().Size())
-	for i := uint64(0); i < nsect; i++ {
-		if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
-			return err
-		}
-		if offsetField.Uint() != 0 {
-			offsetField.SetUint(offsetField.Uint() + delta)
-		}
-		if reloffField.Uint() != 0 {
-			reloffField.SetUint(reloffField.Uint() + delta)
-		}
-		if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
-			return err
-		}
-		sectOffset += sectSize
-	}
-	return nil
-}
-
-// machoUpdateDwarfHeader updates the DWARF segment load command.
-func machoUpdateDwarfHeader(r *loadCmdReader) error {
-	var seg, sect interface{}
-	cmd, err := r.Next()
-	if err != nil {
-		return err
-	}
-	if cmd.Cmd == macho.LoadCmdSegment64 {
-		seg = new(macho.Segment64)
-		sect = new(macho.Section64)
-	} else {
-		seg = new(macho.Segment32)
-		sect = new(macho.Section32)
-	}
-	if err := r.ReadAt(0, seg); err != nil {
-		return err
-	}
-	segValue := reflect.ValueOf(seg)
-	offset := reflect.Indirect(segValue).FieldByName("Offset")
-
-	delta := uint64(dwarfstart) - realdwarf.Offset
-	offset.SetUint(offset.Uint() + delta)
-	if err := r.WriteAt(0, seg); err != nil {
-		return err
-	}
-	return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
-}
-
-func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {
-	if err := r.ReadAt(0, cmd); err != nil {
-		return err
-	}
-	value := reflect.Indirect(reflect.ValueOf(cmd))
-
-	for _, name := range fields {
-		field := value.FieldByName(name)
-		fieldval := field.Uint()
-		if fieldval >= linkseg.Offset {
-			field.SetUint(fieldval + uint64(linkoffset))
-		}
-	}
-	if err := r.WriteAt(0, cmd); err != nil {
-		return err
-	}
-	return nil
-}
-
-func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 {
-	align := uint64(1 << alignExp)
-	if (origAddr % align) == (newAddr % align) {
-		return int64(newAddr)
-	}
-	padding := (align - (newAddr % align))
-	padding += origAddr % align
-	return int64(padding + newAddr)
-}

From 17177a0daa7d08c7031fe10b9be8d070db5fe278 Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Mon, 18 May 2015 17:27:33 +0900
Subject: [PATCH 161/232] net: fix data race in TestSocket{Conn,PacketConn}

Fixes #10891.

Change-Id: Ie432c9c5520ac29cea8fe6452628ec467567eea5
Reviewed-on: https://go-review.googlesource.com/10194
Reviewed-by: Ian Lance Taylor 
---
 src/net/file_bsd_test.go   | 5 +++--
 src/net/file_linux_test.go | 5 +++--
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/net/file_bsd_test.go b/src/net/file_bsd_test.go
index 6e6cf126ad..ffe3c612b4 100644
--- a/src/net/file_bsd_test.go
+++ b/src/net/file_bsd_test.go
@@ -49,8 +49,11 @@ func TestSocketConn(t *testing.T) {
 	defer c.Close()
 
 	const N = 3
+	var wg sync.WaitGroup
+	wg.Add(2 * N)
 	for i := 0; i < N; i++ {
 		go func(i int) {
+			defer wg.Done()
 			l := syscall.SizeofRtMsghdr + syscall.SizeofSockaddrInet4
 			if freebsd32o64 {
 				l += syscall.SizeofRtMetrics // see syscall/route_freebsd_32bit.go
@@ -73,8 +76,6 @@ func TestSocketConn(t *testing.T) {
 			}
 		}(i + 1)
 	}
-	var wg sync.WaitGroup
-	wg.Add(N)
 	for i := 0; i < N; i++ {
 		go func() {
 			defer wg.Done()
diff --git a/src/net/file_linux_test.go b/src/net/file_linux_test.go
index 58f74d2cc5..e04fea38f6 100644
--- a/src/net/file_linux_test.go
+++ b/src/net/file_linux_test.go
@@ -59,9 +59,12 @@ func TestSocketPacketConn(t *testing.T) {
 	defer c.Close()
 
 	const N = 3
+	var wg sync.WaitGroup
+	wg.Add(2 * N)
 	dst := &netlinkAddr{PID: 0}
 	for i := 0; i < N; i++ {
 		go func() {
+			defer wg.Done()
 			l := syscall.NLMSG_HDRLEN + syscall.SizeofRtGenmsg
 			b := make([]byte, l)
 			*(*uint32)(unsafe.Pointer(&b[0:4][0])) = uint32(l)
@@ -76,8 +79,6 @@ func TestSocketPacketConn(t *testing.T) {
 			}
 		}()
 	}
-	var wg sync.WaitGroup
-	wg.Add(N)
 	for i := 0; i < N; i++ {
 		go func() {
 			defer wg.Done()

From 7eec656bfd111531f548dbb5c7f3b6d18525f4ab Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Wed, 13 May 2015 12:44:45 +0900
Subject: [PATCH 162/232] net: fix the series of TestLookup and external tests

On Windows, we need to make sure that the node under test has external
connectivity.

Fixes #10795.

Change-Id: I99f2336180c7b56474fa90a4a6cdd5a6c4dd3805
Reviewed-on: https://go-review.googlesource.com/10006
Reviewed-by: Ian Lance Taylor 
---
 src/net/external_test.go |  43 ++-----
 src/net/lookup_test.go   | 236 +++++++++++++++++++++++++++------------
 src/net/main_test.go     |   6 +
 3 files changed, 178 insertions(+), 107 deletions(-)

diff --git a/src/net/external_test.go b/src/net/external_test.go
index 20611ff420..d5ff2be20a 100644
--- a/src/net/external_test.go
+++ b/src/net/external_test.go
@@ -15,33 +15,23 @@ func TestResolveGoogle(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
-	if !supportsIPv4 && !supportsIPv6 {
-		t.Skip("ipv4 and ipv6 are not supported")
+	if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
+		t.Skip("both IPv4 and IPv6 are required")
 	}
 
 	for _, network := range []string{"tcp", "tcp4", "tcp6"} {
 		addr, err := ResolveTCPAddr(network, "www.google.com:http")
 		if err != nil {
-			switch {
-			case network == "tcp" && !supportsIPv4:
-				fallthrough
-			case network == "tcp4" && !supportsIPv4:
-				t.Logf("skipping test; ipv4 is not supported: %v", err)
-			case network == "tcp6" && !supportsIPv6:
-				t.Logf("skipping test; ipv6 is not supported: %v", err)
-			default:
-				t.Error(err)
-			}
+			t.Error(err)
 			continue
 		}
-
 		switch {
 		case network == "tcp" && addr.IP.To4() == nil:
 			fallthrough
 		case network == "tcp4" && addr.IP.To4() == nil:
-			t.Errorf("got %v; want an ipv4 address on %s", addr, network)
+			t.Errorf("got %v; want an IPv4 address on %s", addr, network)
 		case network == "tcp6" && (addr.IP.To16() == nil || addr.IP.To4() != nil):
-			t.Errorf("got %v; want an ipv6 address on %s", addr, network)
+			t.Errorf("got %v; want an IPv6 address on %s", addr, network)
 		}
 	}
 }
@@ -73,8 +63,8 @@ func TestDialGoogle(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
-	if !supportsIPv4 && !supportsIPv6 {
-		t.Skip("ipv4 and ipv6 are not supported")
+	if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
+		t.Skip("both IPv4 and IPv6 are required")
 	}
 
 	var err error
@@ -84,25 +74,6 @@ func TestDialGoogle(t *testing.T) {
 	}
 	for _, tt := range dialGoogleTests {
 		for _, network := range tt.networks {
-			switch {
-			case network == "tcp4" && !supportsIPv4:
-				t.Log("skipping test; ipv4 is not supported")
-				continue
-			case network == "tcp4" && !*testIPv4:
-				fallthrough
-			case tt.unreachableNetwork == "tcp6" && !*testIPv4:
-				t.Log("disabled; use -ipv4 to enable")
-				continue
-			case network == "tcp6" && !supportsIPv6:
-				t.Log("skipping test; ipv6 is not supported")
-				continue
-			case network == "tcp6" && !*testIPv6:
-				fallthrough
-			case tt.unreachableNetwork == "tcp4" && !*testIPv6:
-				t.Log("disabled; use -ipv6 to enable")
-				continue
-			}
-
 			disableSocketConnect(tt.unreachableNetwork)
 			for _, addr := range tt.addrs {
 				if err := fetchGoogle(tt.dial, network, addr); err != nil {
diff --git a/src/net/lookup_test.go b/src/net/lookup_test.go
index 1f36184d55..064bc0b9f1 100644
--- a/src/net/lookup_test.go
+++ b/src/net/lookup_test.go
@@ -23,17 +23,34 @@ func lookupLocalhost(fn func(string) ([]IPAddr, error), host string) ([]IPAddr,
 	}
 }
 
+// The Lookup APIs use various sources such as local database, DNS or
+// mDNS, and may use platform-dependent DNS stub resolver if possible.
+// The APIs accept any of forms for a query; host name in various
+// encodings, UTF-8 encoded net name, domain name, FQDN or absolute
+// FQDN, but the result would be one of the forms and it depends on
+// the circumstances.
+
 var lookupGoogleSRVTests = []struct {
 	service, proto, name string
 	cname, target        string
 }{
 	{
 		"xmpp-server", "tcp", "google.com",
-		".google.com", ".google.com",
+		"google.com", "google.com",
 	},
 	{
-		"", "", "_xmpp-server._tcp.google.com", // non-standard back door
-		".google.com", ".google.com",
+		"xmpp-server", "tcp", "google.com.",
+		"google.com", "google.com",
+	},
+
+	// non-standard back door
+	{
+		"", "", "_xmpp-server._tcp.google.com",
+		"google.com", "google.com",
+	},
+	{
+		"", "", "_xmpp-server._tcp.google.com.",
+		"google.com", "google.com",
 	},
 }
 
@@ -41,6 +58,9 @@ func TestLookupGoogleSRV(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !*testIPv4 {
+		t.Skip("IPv4 is required")
+	}
 
 	for _, tt := range lookupGoogleSRVTests {
 		cname, srvs, err := LookupSRV(tt.service, tt.proto, tt.name)
@@ -50,88 +70,126 @@ func TestLookupGoogleSRV(t *testing.T) {
 		if len(srvs) == 0 {
 			t.Error("got no record")
 		}
-		if !strings.Contains(cname, tt.cname) {
-			t.Errorf("got %q; want %q", cname, tt.cname)
+		if !strings.HasSuffix(cname, tt.cname) && !strings.HasSuffix(cname, tt.cname+".") {
+			t.Errorf("got %s; want %s", cname, tt.cname)
 		}
 		for _, srv := range srvs {
-			if !strings.Contains(srv.Target, tt.target) {
-				t.Errorf("got %v; want a record containing %q", srv, tt.target)
+			if !strings.HasSuffix(srv.Target, tt.target) && !strings.HasSuffix(srv.Target, tt.target+".") {
+				t.Errorf("got %v; want a record containing %s", srv, tt.target)
 			}
 		}
 	}
 }
 
+var lookupGmailMXTests = []struct {
+	name, host string
+}{
+	{"gmail.com", "google.com"},
+	{"gmail.com.", "google.com"},
+}
+
 func TestLookupGmailMX(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !*testIPv4 {
+		t.Skip("IPv4 is required")
+	}
 
-	mxs, err := LookupMX("gmail.com")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(mxs) == 0 {
-		t.Error("got no record")
-	}
-	for _, mx := range mxs {
-		if !strings.Contains(mx.Host, ".google.com") {
-			t.Errorf("got %v; want a record containing .google.com.", mx)
+	for _, tt := range lookupGmailMXTests {
+		mxs, err := LookupMX(tt.name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(mxs) == 0 {
+			t.Error("got no record")
+		}
+		for _, mx := range mxs {
+			if !strings.HasSuffix(mx.Host, tt.host) && !strings.HasSuffix(mx.Host, tt.host+".") {
+				t.Errorf("got %v; want a record containing %s", mx, tt.host)
+			}
 		}
 	}
 }
 
+var lookupGmailNSTests = []struct {
+	name, host string
+}{
+	{"gmail.com", "google.com"},
+	{"gmail.com.", "google.com"},
+}
+
 func TestLookupGmailNS(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !*testIPv4 {
+		t.Skip("IPv4 is required")
+	}
 
-	nss, err := LookupNS("gmail.com")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(nss) == 0 {
-		t.Error("got no record")
-	}
-	for _, ns := range nss {
-		if !strings.Contains(ns.Host, ".google.com") {
-			t.Errorf("got %v; want a record containing .google.com.", ns)
+	for _, tt := range lookupGmailNSTests {
+		nss, err := LookupNS(tt.name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(nss) == 0 {
+			t.Error("got no record")
+		}
+		for _, ns := range nss {
+			if !strings.HasSuffix(ns.Host, tt.host) && !strings.HasSuffix(ns.Host, tt.host+".") {
+				t.Errorf("got %v; want a record containing %s", ns, tt.host)
+			}
 		}
 	}
 }
 
+var lookupGmailTXTTests = []struct {
+	name, txt, host string
+}{
+	{"gmail.com", "spf", "google.com"},
+	{"gmail.com.", "spf", "google.com"},
+}
+
 func TestLookupGmailTXT(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !*testIPv4 {
+		t.Skip("IPv4 is required")
+	}
 
-	txts, err := LookupTXT("gmail.com")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(txts) == 0 {
-		t.Error("got no record")
-	}
-	for _, txt := range txts {
-		if !strings.Contains(txt, "spf") {
-			t.Errorf("got %q; want a spf record", txt)
+	for _, tt := range lookupGmailTXTTests {
+		txts, err := LookupTXT(tt.name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(txts) == 0 {
+			t.Error("got no record")
+		}
+		for _, txt := range txts {
+			if !strings.Contains(txt, tt.txt) || (!strings.HasSuffix(txt, tt.host) && !strings.HasSuffix(txt, tt.host+".")) {
+				t.Errorf("got %s; want a record containing %s, %s", txt, tt.txt, tt.host)
+			}
 		}
 	}
 }
 
 var lookupGooglePublicDNSAddrs = []struct {
-	addr string
-	name string
+	addr, name string
 }{
-	{"8.8.8.8", ".google.com."},
-	{"8.8.4.4", ".google.com."},
-	{"2001:4860:4860::8888", ".google.com."},
-	{"2001:4860:4860::8844", ".google.com."},
+	{"8.8.8.8", ".google.com"},
+	{"8.8.4.4", ".google.com"},
+	{"2001:4860:4860::8888", ".google.com"},
+	{"2001:4860:4860::8844", ".google.com"},
 }
 
 func TestLookupGooglePublicDNSAddr(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !supportsIPv6 || !*testIPv4 || !*testIPv6 {
+		t.Skip("both IPv4 and IPv6 are required")
+	}
 
 	for _, tt := range lookupGooglePublicDNSAddrs {
 		names, err := LookupAddr(tt.addr)
@@ -142,61 +200,97 @@ func TestLookupGooglePublicDNSAddr(t *testing.T) {
 			t.Error("got no record")
 		}
 		for _, name := range names {
-			if !strings.HasSuffix(name, tt.name) {
-				t.Errorf("got %q; want a record containing %q", name, tt.name)
+			if !strings.HasSuffix(name, tt.name) && !strings.HasSuffix(name, tt.name+".") {
+				t.Errorf("got %s; want a record containing %s", name, tt.name)
 			}
 		}
 	}
 }
 
+var lookupIANACNAMETests = []struct {
+	name, cname string
+}{
+	{"www.iana.org", "icann.org"},
+	{"www.iana.org.", "icann.org"},
+}
+
 func TestLookupIANACNAME(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !*testIPv4 {
+		t.Skip("IPv4 is required")
+	}
 
-	cname, err := LookupCNAME("www.iana.org")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if !strings.HasSuffix(cname, ".icann.org.") {
-		t.Errorf("got %q; want a record containing .icann.org.", cname)
+	for _, tt := range lookupIANACNAMETests {
+		cname, err := LookupCNAME(tt.name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !strings.HasSuffix(cname, tt.cname) && !strings.HasSuffix(cname, tt.cname+".") {
+			t.Errorf("got %s; want a record containing %s", cname, tt.cname)
+		}
 	}
 }
 
+var lookupGoogleHostTests = []struct {
+	name string
+}{
+	{"google.com"},
+	{"google.com."},
+}
+
 func TestLookupGoogleHost(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !*testIPv4 {
+		t.Skip("IPv4 is required")
+	}
 
-	addrs, err := LookupHost("google.com")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(addrs) == 0 {
-		t.Error("got no record")
-	}
-	for _, addr := range addrs {
-		if ParseIP(addr) == nil {
-			t.Errorf("got %q; want a literal ip address", addr)
+	for _, tt := range lookupGoogleHostTests {
+		addrs, err := LookupHost(tt.name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(addrs) == 0 {
+			t.Error("got no record")
+		}
+		for _, addr := range addrs {
+			if ParseIP(addr) == nil {
+				t.Errorf("got %q; want a literal IP address", addr)
+			}
 		}
 	}
 }
 
+var lookupGoogleIPTests = []struct {
+	name string
+}{
+	{"google.com"},
+	{"google.com."},
+}
+
 func TestLookupGoogleIP(t *testing.T) {
 	if testing.Short() || !*testExternal {
 		t.Skip("avoid external network")
 	}
+	if !supportsIPv4 || !*testIPv4 {
+		t.Skip("IPv4 is required")
+	}
 
-	ips, err := LookupIP("google.com")
-	if err != nil {
-		t.Fatal(err)
-	}
-	if len(ips) == 0 {
-		t.Error("got no record")
-	}
-	for _, ip := range ips {
-		if ip.To4() == nil && ip.To16() == nil {
-			t.Errorf("got %v; want an ip address", ip)
+	for _, tt := range lookupGoogleIPTests {
+		ips, err := LookupIP(tt.name)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if len(ips) == 0 {
+			t.Error("got no record")
+		}
+		for _, ip := range ips {
+			if ip.To4() == nil && ip.To16() == nil {
+				t.Errorf("got %v; want an IP address", ip)
+			}
 		}
 	}
 }
diff --git a/src/net/main_test.go b/src/net/main_test.go
index 5e2f3da0e6..ceec08911e 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -30,10 +30,16 @@ var (
 
 	// If external IPv4 connectivity exists, we can try dialing
 	// non-node/interface local scope IPv4 addresses.
+	// On Windows, Lookup APIs may not return IPv4-related
+	// resource records when a node has no external IPv4
+	// connectivity.
 	testIPv4 = flag.Bool("ipv4", true, "assume external IPv4 connectivity exists")
 
 	// If external IPv6 connectivity exists, we can try dialing
 	// non-node/interface local scope IPv6 addresses.
+	// On Windows, Lookup APIs may not return IPv6-related
+	// resource records when a node has no external IPv6
+	// connectivity.
 	testIPv6 = flag.Bool("ipv6", false, "assume external IPv6 connectivity exists")
 )
 

From ae3e3610d5ea9814fcc8bff5c4cea51795465565 Mon Sep 17 00:00:00 2001
From: Michael Hudson-Doyle 
Date: Mon, 18 May 2015 13:03:22 +1200
Subject: [PATCH 163/232] cmd/go: change Package.Shlib to be the absolute path
 of the shared library

Makes little difference internally but makes go list output more useful.

Change-Id: I1fa1f839107de08818427382b2aef8dc4d765b36
Reviewed-on: https://go-review.googlesource.com/10192
Reviewed-by: Ian Lance Taylor 
Run-TryBot: Ian Lance Taylor 
---
 src/cmd/go/build.go | 4 ++--
 src/cmd/go/pkg.go   | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index fda126b008..738f748391 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -782,8 +782,8 @@ func (b *builder) action1(mode buildMode, depMode buildMode, p *Package, looksha
 			b.actionCache[key] = a
 			return a
 		}
-		pkgs := readpkglist(filepath.Join(p.build.PkgTargetRoot, shlib))
-		a = b.libaction(shlib, pkgs, modeInstall, depMode)
+		pkgs := readpkglist(shlib)
+		a = b.libaction(filepath.Base(shlib), pkgs, modeInstall, depMode)
 		b.actionCache[key2] = a
 		b.actionCache[key] = a
 		return a
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 9466aad6a6..f9cf14fd2c 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -536,7 +536,8 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 			shlibnamefile := p.target[:len(p.target)-2] + ".shlibname"
 			shlib, err := ioutil.ReadFile(shlibnamefile)
 			if err == nil {
-				p.Shlib = strings.TrimSpace(string(shlib))
+				libname := strings.TrimSpace(string(shlib))
+				p.Shlib = filepath.Join(p.build.PkgTargetRoot, libname)
 			} else if !os.IsNotExist(err) {
 				fatalf("unexpected error reading %s: %v", shlibnamefile, err)
 			}

From 197aa9e64ddcd1cdcb5f4843413da935a954d9f3 Mon Sep 17 00:00:00 2001
From: Rick Hudson 
Date: Wed, 20 May 2015 11:40:17 -0400
Subject: [PATCH 164/232] runtime: remove unused quiesce code

This is dead code. If you want to quiesce the system the
preferred way is to use forEachP(func(*p){}).

Change-Id: Ic7677a5dd55e3639b99e78ddeb2c71dd1dd091fa
Reviewed-on: https://go-review.googlesource.com/10267
Reviewed-by: Austin Clements 
---
 src/runtime/mbarrier.go |  2 +-
 src/runtime/mgc.go      |  1 -
 src/runtime/mgcmark.go  |  2 +-
 src/runtime/proc1.go    | 63 -----------------------------------------
 4 files changed, 2 insertions(+), 66 deletions(-)

diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index d3e4809737..77b50095a0 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -60,7 +60,7 @@ func gcmarkwb_m(slot *uintptr, ptr uintptr) {
 	default:
 		throw("gcphasework in bad gcphase")
 
-	case _GCoff, _GCquiesce, _GCstw, _GCsweep, _GCscan:
+	case _GCoff, _GCstw, _GCsweep, _GCscan:
 		// ok
 
 	case _GCmark, _GCmarktermination:
diff --git a/src/runtime/mgc.go b/src/runtime/mgc.go
index ebecc4ffa8..db5b2dcd36 100644
--- a/src/runtime/mgc.go
+++ b/src/runtime/mgc.go
@@ -206,7 +206,6 @@ var gcBlackenEnabled uint32
 
 const (
 	_GCoff             = iota // GC not running, write barrier disabled
-	_GCquiesce                // unused state
 	_GCstw                    // unused state
 	_GCscan                   // GC collecting roots into workbufs, write barrier disabled
 	_GCmark                   // GC marking from workbufs, write barrier ENABLED
diff --git a/src/runtime/mgcmark.go b/src/runtime/mgcmark.go
index 0c4e6eba51..62fa33895b 100644
--- a/src/runtime/mgcmark.go
+++ b/src/runtime/mgcmark.go
@@ -261,7 +261,7 @@ func gcphasework(gp *g) {
 	switch gcphase {
 	default:
 		throw("gcphasework in bad gcphase")
-	case _GCoff, _GCquiesce, _GCstw, _GCsweep:
+	case _GCoff, _GCstw, _GCsweep:
 		// No work.
 	case _GCscan:
 		// scan the stack, mark the objects, put pointers in work buffers
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 54d6698b3f..27281406b8 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -465,69 +465,6 @@ func stopscanstart(gp *g) {
 	}
 }
 
-// Runs on g0 and does the actual work after putting the g back on the run queue.
-func mquiesce(gpmaster *g) {
-	// enqueue the calling goroutine.
-	restartg(gpmaster)
-
-	activeglen := len(allgs)
-	for i := 0; i < activeglen; i++ {
-		gp := allgs[i]
-		if readgstatus(gp) == _Gdead {
-			gp.gcworkdone = true // noop scan.
-		} else {
-			gp.gcworkdone = false
-		}
-		stopscanstart(gp)
-	}
-
-	// Check that the G's gcwork (such as scanning) has been done. If not do it now.
-	// You can end up doing work here if the page trap on a Grunning Goroutine has
-	// not been sprung or in some race situations. For example a runnable goes dead
-	// and is started up again with a gp->gcworkdone set to false.
-	for i := 0; i < activeglen; i++ {
-		gp := allgs[i]
-		for !gp.gcworkdone {
-			status := readgstatus(gp)
-			if status == _Gdead {
-				//do nothing, scan not needed.
-				gp.gcworkdone = true // scan is a noop
-				break
-			}
-			if status == _Grunning && gp.stackguard0 == uintptr(stackPreempt) && notetsleep(&sched.stopnote, 100*1000) { // nanosecond arg
-				noteclear(&sched.stopnote)
-			} else {
-				stopscanstart(gp)
-			}
-		}
-	}
-
-	for i := 0; i < activeglen; i++ {
-		gp := allgs[i]
-		status := readgstatus(gp)
-		if isscanstatus(status) {
-			print("mstopandscang:bottom: post scan bad status gp=", gp, " has status ", hex(status), "\n")
-			dumpgstatus(gp)
-		}
-		if !gp.gcworkdone && status != _Gdead {
-			print("mstopandscang:bottom: post scan gp=", gp, "->gcworkdone still false\n")
-			dumpgstatus(gp)
-		}
-	}
-
-	schedule() // Never returns.
-}
-
-// quiesce moves all the goroutines to a GC safepoint which for now is a at preemption point.
-// If the global gcphase is GCmark quiesce will ensure that all of the goroutine's stacks
-// have been scanned before it returns.
-func quiesce(mastergp *g) {
-	castogscanstatus(mastergp, _Grunning, _Gscanenqueue)
-	// Now move this to the g0 (aka m) stack.
-	// g0 will potentially scan this thread and put mastergp on the runqueue
-	mcall(mquiesce)
-}
-
 // stopTheWorld stops all P's from executing goroutines, interrupting
 // all goroutines at GC safe points and records reason as the reason
 // for the stop. On return, only the current goroutine's P is running.

From 8401b19e7e6bf60d66b2d71cd3fa2215c4649d31 Mon Sep 17 00:00:00 2001
From: Rob Pike 
Date: Wed, 20 May 2015 13:26:02 -0700
Subject: [PATCH 165/232] cmd/doc: fix handling of paths like ./fmt

An error in string slice offsets caused the loop to run forever if the
first character in the argument was a period.

Fixes #10833.

Change-Id: Iefb6aac5cff8864fe93d08e2600cb07d82c6f6df
Reviewed-on: https://go-review.googlesource.com/10285
Reviewed-by: Russ Cox 
---
 src/cmd/doc/main.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/cmd/doc/main.go b/src/cmd/doc/main.go
index b3be2a975b..18dafc298c 100644
--- a/src/cmd/doc/main.go
+++ b/src/cmd/doc/main.go
@@ -132,11 +132,12 @@ func parseArgs() (*build.Package, string, string) {
 	// slash+1: if there's no slash, the value is -1 and start is 0; otherwise
 	// start is the byte after the slash.
 	for start := slash + 1; start < len(arg); start = period + 1 {
-		period = start + strings.Index(arg[start:], ".")
+		period = strings.Index(arg[start:], ".")
 		symbol := ""
 		if period < 0 {
 			period = len(arg)
 		} else {
+			period += start
 			symbol = arg[period+1:]
 		}
 		// Have we identified a package already?

From ceb8fe45da7042b20189de0b66db5b33bb589f7b Mon Sep 17 00:00:00 2001
From: Alan Donovan 
Date: Wed, 20 May 2015 15:49:23 -0400
Subject: [PATCH 166/232] go/parser: parse incomplete selection "fmt." as a
 blank selection "fmt._"

Formerly it would return a BadExpr.

This prevents partial syntax from being discarded, and makes the error
recovery logic more consistent with other places where an identifier
was expected but not found.

+ test

Change-Id: I223c0c0589e7ceb7207ae951b8f71b9275a1eb73
Reviewed-on: https://go-review.googlesource.com/10269
Reviewed-by: Robert Griesemer 
---
 src/go/parser/interface.go   |  5 ++++-
 src/go/parser/parser.go      |  3 ++-
 src/go/parser/parser_test.go | 39 ++++++++++++++++++++++++++++++++++++
 3 files changed, 45 insertions(+), 2 deletions(-)

diff --git a/src/go/parser/interface.go b/src/go/parser/interface.go
index 49103058b5..f3bc4b9cc8 100644
--- a/src/go/parser/interface.go
+++ b/src/go/parser/interface.go
@@ -91,7 +91,10 @@ func ParseFile(fset *token.FileSet, filename string, src interface{}, mode Mode)
 	var p parser
 	defer func() {
 		if e := recover(); e != nil {
-			_ = e.(bailout) // re-panics if it's not a bailout
+			// resume same panic if it's not a bailout
+			if _, ok := e.(bailout); !ok {
+				panic(e)
+			}
 		}
 
 		// set result values
diff --git a/src/go/parser/parser.go b/src/go/parser/parser.go
index fb6ca76a77..18278ba4b7 100644
--- a/src/go/parser/parser.go
+++ b/src/go/parser/parser.go
@@ -1472,7 +1472,8 @@ L:
 				pos := p.pos
 				p.errorExpected(pos, "selector or type assertion")
 				p.next() // make progress
-				x = &ast.BadExpr{From: pos, To: p.pos}
+				sel := &ast.Ident{NamePos: pos, Name: "_"}
+				x = &ast.SelectorExpr{X: x, Sel: sel}
 			}
 		case token.LBRACK:
 			if lhs {
diff --git a/src/go/parser/parser_test.go b/src/go/parser/parser_test.go
index 4b960d9e57..c7bb36d789 100644
--- a/src/go/parser/parser_test.go
+++ b/src/go/parser/parser_test.go
@@ -492,3 +492,42 @@ func TestIssue9979(t *testing.T) {
 		})
 	}
 }
+
+// TestIncompleteSelection ensures that an incomplete selector
+// expression is parsed as a (blank) *ast.SelectorExpr, not a
+// *ast.BadExpr.
+func TestIncompleteSelection(t *testing.T) {
+	for _, src := range []string{
+		"package p; var _ = fmt.",             // at EOF
+		"package p; var _ = fmt.\ntype X int", // not at EOF
+	} {
+		fset := token.NewFileSet()
+		f, err := ParseFile(fset, "", src, 0)
+		if err == nil {
+			t.Errorf("ParseFile(%s) succeeded unexpectedly", src)
+			continue
+		}
+
+		const wantErr = "expected selector or type assertion"
+		if !strings.Contains(err.Error(), wantErr) {
+			t.Errorf("ParseFile returned wrong error %q, want %q", err, wantErr)
+		}
+
+		var sel *ast.SelectorExpr
+		ast.Inspect(f, func(n ast.Node) bool {
+			if n, ok := n.(*ast.SelectorExpr); ok {
+				sel = n
+			}
+			return true
+		})
+		if sel == nil {
+			t.Error("found no *ast.SelectorExpr")
+			continue
+		}
+		const wantSel = "&{fmt _}"
+		if fmt.Sprint(sel) != wantSel {
+			t.Errorf("found selector %s, want %s", sel, wantSel)
+			continue
+		}
+	}
+}

From 49894be7b10c4d24c77b4aba46a72bca0a43d06c Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Thu, 7 May 2015 19:10:29 +0900
Subject: [PATCH 167/232] net: document that ListenMulticastUDP is for simple
 applications

Also mentions golang.org/x/net/ipv4 and golang.org/x/net/ipv6.

Change-Id: I653deac7a5e2b129237655a72d6c91207f1b1685
Reviewed-on: https://go-review.googlesource.com/9779
Reviewed-by: Ian Lance Taylor 
---
 src/net/udpsock_plan9.go | 17 ++++++++++++-----
 src/net/udpsock_posix.go | 29 ++++++++++++++++++-----------
 2 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/src/net/udpsock_plan9.go b/src/net/udpsock_plan9.go
index 949f666d74..7bbcf6e4cb 100644
--- a/src/net/udpsock_plan9.go
+++ b/src/net/udpsock_plan9.go
@@ -200,9 +200,16 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
 }
 
 // ListenMulticastUDP listens for incoming multicast UDP packets
-// addressed to the group address gaddr on ifi, which specifies the
-// interface to join.  ListenMulticastUDP uses default multicast
-// interface if ifi is nil.
-func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
-	return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: syscall.EPLAN9}
+// addressed to the group address gaddr on the interface ifi.
+// Network must be "udp", "udp4" or "udp6".
+// ListenMulticastUDP uses the system-assigned multicast interface
+// when ifi is nil, although this is not recommended because the
+// assignment depends on platforms and sometimes it might require
+// routing configuration.
+//
+// ListenMulticastUDP is just for convenience of simple, small
+// applications. There are golang.org/x/net/ipv4 and
+// golang.org/x/net/ipv6 packages for general purpose uses.
+func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
+	return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: syscall.EPLAN9}
 }
diff --git a/src/net/udpsock_posix.go b/src/net/udpsock_posix.go
index 2e43068be3..36ada176a1 100644
--- a/src/net/udpsock_posix.go
+++ b/src/net/udpsock_posix.go
@@ -220,32 +220,39 @@ func ListenUDP(net string, laddr *UDPAddr) (*UDPConn, error) {
 }
 
 // ListenMulticastUDP listens for incoming multicast UDP packets
-// addressed to the group address gaddr on ifi, which specifies the
-// interface to join.  ListenMulticastUDP uses default multicast
-// interface if ifi is nil.
-func ListenMulticastUDP(net string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
-	switch net {
+// addressed to the group address gaddr on the interface ifi.
+// Network must be "udp", "udp4" or "udp6".
+// ListenMulticastUDP uses the system-assigned multicast interface
+// when ifi is nil, although this is not recommended because the
+// assignment depends on platforms and sometimes it might require
+// routing configuration.
+//
+// ListenMulticastUDP is just for convenience of simple, small
+// applications. There are golang.org/x/net/ipv4 and
+// golang.org/x/net/ipv6 packages for general purpose uses.
+func ListenMulticastUDP(network string, ifi *Interface, gaddr *UDPAddr) (*UDPConn, error) {
+	switch network {
 	case "udp", "udp4", "udp6":
 	default:
-		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: UnknownNetworkError(net)}
+		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: UnknownNetworkError(network)}
 	}
 	if gaddr == nil || gaddr.IP == nil {
-		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: errMissingAddress}
+		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: errMissingAddress}
 	}
-	fd, err := internetSocket(net, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
+	fd, err := internetSocket(network, gaddr, nil, noDeadline, syscall.SOCK_DGRAM, 0, "listen")
 	if err != nil {
-		return nil, &OpError{Op: "listen", Net: net, Source: nil, Addr: gaddr, Err: err}
+		return nil, &OpError{Op: "listen", Net: network, Source: nil, Addr: gaddr, Err: err}
 	}
 	c := newUDPConn(fd)
 	if ip4 := gaddr.IP.To4(); ip4 != nil {
 		if err := listenIPv4MulticastUDP(c, ifi, ip4); err != nil {
 			c.Close()
-			return nil, &OpError{Op: "listen", Net: net, Source: c.fd.laddr, Addr: &IPAddr{IP: ip4}, Err: err}
+			return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: ip4}, Err: err}
 		}
 	} else {
 		if err := listenIPv6MulticastUDP(c, ifi, gaddr.IP); err != nil {
 			c.Close()
-			return nil, &OpError{Op: "listen", Net: net, Source: c.fd.laddr, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
+			return nil, &OpError{Op: "listen", Net: network, Source: c.fd.laddr, Addr: &IPAddr{IP: gaddr.IP}, Err: err}
 		}
 	}
 	return c, nil

From 91191e7b7bc8c0e1a6d49c7a9b3adeb1ab39a423 Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Tue, 19 May 2015 00:19:04 -0400
Subject: [PATCH 168/232] encoding/gob: fix docs

Fixes #10908.

Change-Id: I5ac4bd90204bc230610dcced47ce5b2253e5a004
Reviewed-on: https://go-review.googlesource.com/10250
Reviewed-by: Rob Pike 
---
 src/encoding/gob/doc.go | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/encoding/gob/doc.go b/src/encoding/gob/doc.go
index d0acaba1ad..31223b6d43 100644
--- a/src/encoding/gob/doc.go
+++ b/src/encoding/gob/doc.go
@@ -6,7 +6,7 @@
 Package gob manages streams of gobs - binary values exchanged between an
 Encoder (transmitter) and a Decoder (receiver).  A typical use is transporting
 arguments and results of remote procedure calls (RPCs) such as those provided by
-package "rpc".
+package "net/rpc".
 
 The implementation compiles a custom codec for each data type in the stream and
 is most efficient when a single Encoder is used to transmit a stream of values,
@@ -83,7 +83,7 @@ allocated. Regardless, the length of the resulting slice reports the number of
 elements decoded.
 
 Functions and channels will not be sent in a gob. Attempting to encode such a value
-at top the level will fail. A struct field of chan or func type is treated exactly
+at the top level will fail. A struct field of chan or func type is treated exactly
 like an unexported field and is ignored.
 
 Gob can encode a value of any type implementing the GobEncoder or
@@ -111,11 +111,11 @@ A signed integer, i, is encoded within an unsigned integer, u.  Within u, bits 1
 upward contain the value; bit 0 says whether they should be complemented upon
 receipt.  The encode algorithm looks like this:
 
-	uint u;
+	var u uint
 	if i < 0 {
-		u = (^i << 1) | 1	// complement i, bit 0 is 1
+		u = (^uint(i) << 1) | 1 // complement i, bit 0 is 1
 	} else {
-		u = (i << 1)	// do not complement i, bit 0 is 0
+		u = (uint(i) << 1) // do not complement i, bit 0 is 0
 	}
 	encodeUnsigned(u)
 
@@ -137,9 +137,9 @@ All other slices and arrays are sent as an unsigned count followed by that many
 elements using the standard gob encoding for their type, recursively.
 
 Maps are sent as an unsigned count followed by that many key, element
-pairs. Empty but non-nil maps are sent, so if the sender has allocated
-a map, the receiver will allocate a map even if no elements are
-transmitted.
+pairs. Empty but non-nil maps are sent, so if the receiver has not allocated
+one already, one will always be allocated on receipt unless the transmitted map
+is nil and not at the top level.
 
 Structs are sent as a sequence of (field number, field value) pairs.  The field
 value is sent using the standard gob encoding for its type, recursively.  If a
@@ -246,7 +246,7 @@ where * signifies zero or more repetitions and the type id of a value must
 be predefined or be defined before the value in the stream.
 
 See "Gobs of data" for a design discussion of the gob wire format:
-http://golang.org/doc/articles/gobs_of_data.html
+http://blog.golang.org/gobs-of-data
 */
 package gob
 

From 719efc70eb84b74a93d236f7c7ddca9901f65436 Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Wed, 20 May 2015 11:50:48 -0400
Subject: [PATCH 169/232] runtime: make runtime.callers walk calling G, not g0

Currently runtime.callers invokes gentraceback with the pc and sp of
the G it is called from, but always passes g0 even if it was called
from a regular g. Right now this has no ill effects because
runtime.callers does not use either callback argument or the
_TraceJumpStack flag, but it makes the code fragile and will break
some upcoming changes.

Fix this by lifting the getg() call outside of the systemstack in
runtime.callers.

Change-Id: I4e1e927961c0e0cd4dcf28693be47df7bae9e122
Reviewed-on: https://go-review.googlesource.com/10292
Reviewed-by: Daniel Morsing 
Reviewed-by: Rick Hudson 
---
 src/runtime/traceback.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go
index 12b2a53603..5ed601e6f3 100644
--- a/src/runtime/traceback.go
+++ b/src/runtime/traceback.go
@@ -531,9 +531,10 @@ func traceback1(pc, sp, lr uintptr, gp *g, flags uint) {
 func callers(skip int, pcbuf []uintptr) int {
 	sp := getcallersp(unsafe.Pointer(&skip))
 	pc := uintptr(getcallerpc(unsafe.Pointer(&skip)))
+	gp := getg()
 	var n int
 	systemstack(func() {
-		n = gentraceback(pc, sp, 0, getg(), skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
+		n = gentraceback(pc, sp, 0, gp, skip, &pcbuf[0], len(pcbuf), nil, nil, 0)
 	})
 	return n
 }

From 5b66e5d0d853e292c8c90512b33257c3a6e089e1 Mon Sep 17 00:00:00 2001
From: Rick Hudson 
Date: Thu, 21 May 2015 09:09:35 -0400
Subject: [PATCH 170/232] runtime: turn work buffer tracing off by default
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

During development we ran with monitoring code turned
on by default. This CL turns the work buffer monitoring
off. Performance change on most go1 benchmarks is small
or insignificant.

name                   old mean              new mean              delta
BinaryTree17            3.35s × (0.99,1.01)   3.35s × (0.99,1.01)    ~    (p=0.841 n=5+5)
Fannkuch11              2.59s × (1.00,1.01)   2.55s × (1.00,1.00)  -1.65% (p=0.008 n=5+5)
FmtFprintfEmpty        52.5ns × (0.99,1.02)  53.2ns × (0.98,1.01)    ~    (p=0.063 n=5+5)
FmtFprintfString        181ns × (1.00,1.00)   180ns × (1.00,1.00)  -0.55% (p=0.029 n=4+4)
FmtFprintfInt           176ns × (1.00,1.01)   174ns × (1.00,1.00)  -0.91% (p=0.000 n=5+4)
FmtFprintfIntInt        298ns × (1.00,1.00)   299ns × (1.00,1.00)    ~    (p=0.143 n=4+4)
FmtFprintfPrefixedInt   250ns × (1.00,1.01)   246ns × (1.00,1.00)  -1.68% (p=0.000 n=5+4)
FmtFprintfFloat         340ns × (1.00,1.00)   340ns × (1.00,1.01)    ~    (p=0.643 n=5+5)
FmtManyArgs            1.16µs × (1.00,1.00)  1.15µs × (1.00,1.00)  -0.47% (p=0.016 n=5+5)
GobDecode              9.22ms × (1.00,1.00)  9.23ms × (1.00,1.00)    ~    (p=0.841 n=5+5)
GobEncode              7.00ms × (1.00,1.01)  7.09ms × (0.99,1.01)  +1.26% (p=0.016 n=5+5)
Gzip                    387ms × (1.00,1.00)   389ms × (0.99,1.02)    ~    (p=1.000 n=5+5)
Gunzip                 97.8ms × (1.00,1.00)  98.3ms × (1.00,1.00)  +0.51% (p=0.016 n=5+4)
HTTPClientServer       52.6µs × (1.00,1.01)  52.7µs × (1.00,1.01)    ~    (p=1.000 n=5+5)
JSONEncode             18.0ms × (0.99,1.02)  17.9ms × (1.00,1.00)    ~    (p=0.310 n=5+5)
JSONDecode             64.8ms × (0.99,1.02)  63.6ms × (1.00,1.00)  -1.94% (p=0.008 n=5+5)
Mandelbrot200          4.05ms × (1.00,1.00)  4.05ms × (1.00,1.00)    ~    (p=0.421 n=5+5)
GoParse                3.86ms × (1.00,1.01)  3.84ms × (0.99,1.01)    ~    (p=0.421 n=5+5)
RegexpMatchEasy0_32     101ns × (1.00,1.00)   102ns × (0.99,1.02)    ~    (p=0.238 n=4+5)
RegexpMatchEasy0_1K     346ns × (1.00,1.01)   345ns × (1.00,1.00)    ~    (p=0.333 n=5+4)
RegexpMatchEasy1_32    87.3ns × (0.99,1.02)  87.4ns × (1.00,1.00)    ~    (p=0.190 n=5+4)
RegexpMatchEasy1_1K     520ns × (1.00,1.00)   520ns × (1.00,1.01)    ~    (p=1.000 n=4+5)
RegexpMatchMedium_32    143ns × (1.00,1.00)   142ns × (1.00,1.00)  -0.70% (p=0.029 n=4+4)
RegexpMatchMedium_1K   43.2µs × (1.00,1.01)  43.2µs × (1.00,1.00)    ~    (p=0.841 n=5+5)
RegexpMatchHard_32     2.24µs × (1.00,1.01)  2.23µs × (1.00,1.01)  -0.63% (p=0.048 n=5+5)
RegexpMatchHard_1K     68.7µs × (1.00,1.00)  68.3µs × (1.00,1.00)  -0.56% (p=0.008 n=5+5)
Revcomp                 577ms × (1.00,1.01)   579ms × (1.00,1.00)    ~    (p=0.151 n=5+5)
Template               74.9ms × (1.00,1.00)  76.5ms × (1.00,1.00)  +2.11% (p=0.008 n=5+5)
TimeParse               359ns × (1.00,1.00)   362ns × (1.00,1.00)  +0.72% (p=0.008 n=5+5)
TimeFormat              369ns × (1.00,1.00)   371ns × (1.00,1.01)    ~    (p=0.071 n=5+5)

Change-Id: I4206a3f77a3d1450966b7a62ea7597aec44cb72f
Reviewed-on: https://go-review.googlesource.com/10294
Reviewed-by: Brad Fitzpatrick 
Reviewed-by: Austin Clements 
---
 src/runtime/mgcwork.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/runtime/mgcwork.go b/src/runtime/mgcwork.go
index 930c644c0a..b7feb847b4 100644
--- a/src/runtime/mgcwork.go
+++ b/src/runtime/mgcwork.go
@@ -7,7 +7,7 @@ package runtime
 import "unsafe"
 
 const (
-	_Debugwbufs  = true    // if true check wbufs consistency
+	_Debugwbufs  = false   // if true check wbufs consistency
 	_WorkbufSize = 1 * 256 // in bytes - if small wbufs are passed to GC in a timely fashion.
 )
 

From be59731d9fffb53a499d1ae64863e2a380f2dede Mon Sep 17 00:00:00 2001
From: Ryan Brown 
Date: Wed, 8 Apr 2015 12:55:34 -0700
Subject: [PATCH 171/232] cmd/internal/ld: output dwarf in external link mode
 on darwin

Fixes #8973

Change-Id: I746fae430db6d8f9ebd33586b8cffcb31d688cc8
Reviewed-on: https://go-review.googlesource.com/10284
Run-TryBot: Minux Ma 
TryBot-Result: Gobot Gobot 
Reviewed-by: Russ Cox 
---
 src/cmd/5l/asm.go                          |  14 +-
 src/cmd/6l/asm.go                          |   2 +-
 src/cmd/7l/asm.go                          |  14 +-
 src/cmd/8l/asm.go                          |   2 +-
 src/cmd/internal/ld/dwarf.go               | 144 ++++++--
 src/cmd/internal/ld/lib.go                 |  21 +-
 src/cmd/internal/ld/macho.go               |   8 +-
 src/cmd/internal/ld/macho_combine_dwarf.go | 369 +++++++++++++++++++++
 8 files changed, 528 insertions(+), 46 deletions(-)
 create mode 100644 src/cmd/internal/ld/macho_combine_dwarf.go

diff --git a/src/cmd/5l/asm.go b/src/cmd/5l/asm.go
index 1b69671b9f..70d6790fc1 100644
--- a/src/cmd/5l/asm.go
+++ b/src/cmd/5l/asm.go
@@ -533,14 +533,12 @@ func asmb() {
 			fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
 		}
 
-		if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
-			dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
-			ld.Cseek(int64(dwarfoff))
+		dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+		ld.Cseek(int64(dwarfoff))
 
-			ld.Segdwarf.Fileoff = uint64(ld.Cpos())
-			ld.Dwarfemitdebugsections()
-			ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
-		}
+		ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+		ld.Dwarfemitdebugsections()
+		ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
 
 		machlink = uint32(ld.Domacholink())
 	}
@@ -567,7 +565,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
diff --git a/src/cmd/6l/asm.go b/src/cmd/6l/asm.go
index 5520a5acf1..02b4c7cdd2 100644
--- a/src/cmd/6l/asm.go
+++ b/src/cmd/6l/asm.go
@@ -710,7 +710,7 @@ func asmb() {
 			symo = int64(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = int64(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = int64(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 
 		case obj.Hlinux,
 			obj.Hfreebsd,
diff --git a/src/cmd/7l/asm.go b/src/cmd/7l/asm.go
index 3dfb8c666d..064ff56283 100644
--- a/src/cmd/7l/asm.go
+++ b/src/cmd/7l/asm.go
@@ -317,14 +317,12 @@ func asmb() {
 			fmt.Fprintf(&ld.Bso, "%5.2f dwarf\n", obj.Cputime())
 		}
 
-		if ld.Debug['w'] == 0 { // TODO(minux): enable DWARF Support
-			dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
-			ld.Cseek(int64(dwarfoff))
+		dwarfoff := uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Length), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)))
+		ld.Cseek(int64(dwarfoff))
 
-			ld.Segdwarf.Fileoff = uint64(ld.Cpos())
-			ld.Dwarfemitdebugsections()
-			ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
-		}
+		ld.Segdwarf.Fileoff = uint64(ld.Cpos())
+		ld.Dwarfemitdebugsections()
+		ld.Segdwarf.Filelen = uint64(ld.Cpos()) - ld.Segdwarf.Fileoff
 
 		machlink = uint32(ld.Domacholink())
 	}
@@ -351,7 +349,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Rnd(int64(uint64(ld.HEADR)+ld.Segtext.Filelen), int64(ld.INITRND)) + ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND)) + int64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 		}
 
 		ld.Cseek(int64(symo))
diff --git a/src/cmd/8l/asm.go b/src/cmd/8l/asm.go
index a63c51f58d..a736d43686 100644
--- a/src/cmd/8l/asm.go
+++ b/src/cmd/8l/asm.go
@@ -551,7 +551,7 @@ func asmb() {
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
 
 		case obj.Hdarwin:
-			symo = uint32(ld.Segdata.Fileoff + uint64(ld.Rnd(int64(ld.Segdata.Filelen), int64(ld.INITRND))) + uint64(machlink))
+			symo = uint32(ld.Segdwarf.Fileoff + uint64(ld.Rnd(int64(ld.Segdwarf.Filelen), int64(ld.INITRND))) + uint64(machlink))
 
 		case obj.Hwindows:
 			symo = uint32(ld.Segdata.Fileoff + ld.Segdata.Filelen)
diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/internal/ld/dwarf.go
index 476b329e7a..b8fb2e6b55 100644
--- a/src/cmd/internal/ld/dwarf.go
+++ b/src/cmd/internal/ld/dwarf.go
@@ -17,6 +17,7 @@ package ld
 import (
 	"cmd/internal/obj"
 	"fmt"
+	"os"
 	"strings"
 )
 
@@ -240,6 +241,7 @@ var abbrevs = [DW_NABRV]DWAbbrev{
 			{DW_AT_low_pc, DW_FORM_addr},
 			{DW_AT_high_pc, DW_FORM_addr},
 			{DW_AT_stmt_list, DW_FORM_data4},
+			{DW_AT_comp_dir, DW_FORM_string},
 		},
 	},
 
@@ -694,6 +696,9 @@ func adddwarfrel(sec *LSym, sym *LSym, offsetbase int64, siz int, addend int64)
 	if Iself && Thearch.Thechar == '6' {
 		addend = 0
 	}
+	if HEADTYPE == obj.Hdarwin {
+		addend += sym.Value
+	}
 	switch siz {
 	case 4:
 		Thearch.Lput(uint32(addend))
@@ -1547,6 +1552,13 @@ func flushunit(dwinfo *DWDie, pc int64, pcsym *LSym, unitstart int64, header_len
 	}
 }
 
+func getCompilationDir() string {
+	if dir, err := os.Getwd(); err == nil {
+		return dir
+	}
+	return "/"
+}
+
 func writelines() {
 	if linesec == nil {
 		linesec = Linklookup(Ctxt, ".dwarfline", 0)
@@ -1571,6 +1583,9 @@ func writelines() {
 	newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT, int64(lang), 0)
 	newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart-lineo, 0)
 	newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s.Value, s)
+	// OS X linker requires compilation dir or absolute path in comp unit name to output debug info.
+	compDir := getCompilationDir()
+	newattr(dwinfo, DW_AT_comp_dir, DW_CLS_STRING, int64(len(compDir)), compDir)
 
 	// Write .debug_line Line Number Program Header (sec 6.2.4)
 	// Fields marked with (*) must be changed for 64-bit dwarf
@@ -2083,6 +2098,14 @@ func writedwarfreloc(s *LSym) int64 {
 	return start
 }
 
+func addmachodwarfsect(prev *Section, name string) *Section {
+	sect := addsection(&Segdwarf, name, 04)
+	sect.Extnum = prev.Extnum + 1
+	sym := Linklookup(Ctxt, name, 0)
+	sym.Sect = sect
+	return sect
+}
+
 /*
  * This is the main entry point for generating dwarf.  After emitting
  * the mandatory debug_abbrev section, it calls writelines() to set up
@@ -2097,8 +2120,33 @@ func Dwarfemitdebugsections() {
 		return
 	}
 
-	if Linkmode == LinkExternal && !Iself {
-		return
+	if Linkmode == LinkExternal {
+		if !Iself && HEADTYPE != obj.Hdarwin {
+			return
+		}
+		if HEADTYPE == obj.Hdarwin {
+			sect := Segdata.Sect
+			// find the last section.
+			for sect.Next != nil {
+				sect = sect.Next
+			}
+			sect = addmachodwarfsect(sect, ".debug_abbrev")
+			sect = addmachodwarfsect(sect, ".debug_line")
+			sect = addmachodwarfsect(sect, ".debug_frame")
+			sect = addmachodwarfsect(sect, ".debug_info")
+
+			infosym = Linklookup(Ctxt, ".debug_info", 0)
+			infosym.Hide = 1
+
+			abbrevsym = Linklookup(Ctxt, ".debug_abbrev", 0)
+			abbrevsym.Hide = 1
+
+			linesym = Linklookup(Ctxt, ".debug_line", 0)
+			linesym.Hide = 1
+
+			framesym = Linklookup(Ctxt, ".debug_frame", 0)
+			framesym.Hide = 1
+		}
 	}
 
 	// For diagnostic messages.
@@ -2191,6 +2239,15 @@ func Dwarfemitdebugsections() {
 	for Cpos()&7 != 0 {
 		Cput(0)
 	}
+	if HEADTYPE != obj.Hdarwin {
+		dwarfemitreloc()
+	}
+}
+
+func dwarfemitreloc() {
+	if Debug['w'] != 0 { // disable dwarf
+		return
+	}
 	inforeloco = writedwarfreloc(infosec)
 	inforelocsize = Cpos() - inforeloco
 	align(inforelocsize)
@@ -2420,14 +2477,15 @@ func dwarfaddelfheaders() {
 /*
  * Macho
  */
-func dwarfaddmachoheaders() {
+func dwarfaddmachoheaders(ms *MachoSeg) {
 	if Debug['w'] != 0 { // disable dwarf
 		return
 	}
 
 	// Zero vsize segments won't be loaded in memory, even so they
 	// have to be page aligned in the file.
-	fakestart := abbrevo &^ 0xfff
+	fakestart := Rnd(int64(Segdwarf.Fileoff), 0x1000)
+	addr := Segdata.Vaddr + Segdata.Length
 
 	nsect := 4
 	if pubnamessize > 0 {
@@ -2443,57 +2501,94 @@ func dwarfaddmachoheaders() {
 		nsect++
 	}
 
-	ms := newMachoSeg("__DWARF", nsect)
-	ms.fileoffset = uint64(fakestart)
-	ms.filesize = uint64(abbrevo) - uint64(fakestart)
-	ms.vaddr = ms.fileoffset + Segdata.Vaddr - Segdata.Fileoff
+	if Linkmode != LinkExternal {
+		ms = newMachoSeg("__DWARF", nsect)
+		ms.fileoffset = uint64(fakestart)
+		ms.filesize = Segdwarf.Filelen
+		ms.vaddr = addr
+	}
 
 	msect := newMachoSect(ms, "__debug_abbrev", "__DWARF")
 	msect.off = uint32(abbrevo)
 	msect.size = uint64(abbrevsize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if abbrevsym != nil {
+		abbrevsym.Value = int64(msect.addr)
+	}
 
 	msect = newMachoSect(ms, "__debug_line", "__DWARF")
 	msect.off = uint32(lineo)
 	msect.size = uint64(linesize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if linesym != nil {
+		linesym.Value = int64(msect.addr)
+	}
+	if linerelocsize > 0 {
+		msect.nreloc = uint32(len(linesec.R))
+		msect.reloc = uint32(linereloco)
+	}
 
 	msect = newMachoSect(ms, "__debug_frame", "__DWARF")
 	msect.off = uint32(frameo)
 	msect.size = uint64(framesize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if framesym != nil {
+		framesym.Value = int64(msect.addr)
+	}
+	if framerelocsize > 0 {
+		msect.nreloc = uint32(len(framesec.R))
+		msect.reloc = uint32(framereloco)
+	}
 
 	msect = newMachoSect(ms, "__debug_info", "__DWARF")
 	msect.off = uint32(infoo)
 	msect.size = uint64(infosize)
-	msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-	ms.filesize += msect.size
+	msect.addr = addr
+	addr += msect.size
+	msect.flag = 0x02000000
+	if infosym != nil {
+		infosym.Value = int64(msect.addr)
+	}
+	if inforelocsize > 0 {
+		msect.nreloc = uint32(len(infosec.R))
+		msect.reloc = uint32(inforeloco)
+	}
 
 	if pubnamessize > 0 {
 		msect := newMachoSect(ms, "__debug_pubnames", "__DWARF")
 		msect.off = uint32(pubnameso)
 		msect.size = uint64(pubnamessize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
 	}
 
 	if pubtypessize > 0 {
 		msect := newMachoSect(ms, "__debug_pubtypes", "__DWARF")
 		msect.off = uint32(pubtypeso)
 		msect.size = uint64(pubtypessize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
 	}
 
 	if arangessize > 0 {
 		msect := newMachoSect(ms, "__debug_aranges", "__DWARF")
 		msect.off = uint32(arangeso)
 		msect.size = uint64(arangessize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
+		if arangesrelocsize > 0 {
+			msect.nreloc = uint32(len(arangessec.R))
+			msect.reloc = uint32(arangesreloco)
+		}
 	}
 
 	// TODO(lvd) fix gdb/python to load MachO (16 char section name limit)
@@ -2501,8 +2596,9 @@ func dwarfaddmachoheaders() {
 		msect := newMachoSect(ms, "__debug_gdb_scripts", "__DWARF")
 		msect.off = uint32(gdbscripto)
 		msect.size = uint64(gdbscriptsize)
-		msect.addr = uint64(msect.off) + Segdata.Vaddr - Segdata.Fileoff
-		ms.filesize += msect.size
+		msect.addr = addr
+		addr += msect.size
+		msect.flag = 0x02000000
 	}
 }
 
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/internal/ld/lib.go
index 753e8eebd8..a36cd0f8f4 100644
--- a/src/cmd/internal/ld/lib.go
+++ b/src/cmd/internal/ld/lib.go
@@ -922,7 +922,7 @@ func hostlink() {
 	}
 
 	if HEADTYPE == obj.Hdarwin {
-		argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000")
+		argv = append(argv, "-Wl,-no_pie,-pagezero_size,4000000,-headerpad,1144")
 	}
 	if HEADTYPE == obj.Hopenbsd {
 		argv = append(argv, "-Wl,-nopie")
@@ -1029,6 +1029,25 @@ func hostlink() {
 	if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
 		Exitf("running %s failed: %v\n%s", argv[0], err, out)
 	}
+
+	if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin {
+		dsym := fmt.Sprintf("%s/go.dwarf", tmpdir)
+		if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil {
+			Ctxt.Cursym = nil
+			Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out)
+		}
+		combinedOutput := fmt.Sprintf("%s/go.combined", tmpdir)
+		if err := machoCombineDwarf(outfile, dsym, combinedOutput); err != nil {
+			Ctxt.Cursym = nil
+			Exitf("%s: combining dwarf failed: %v", os.Args[0], err)
+		}
+		origOutput := fmt.Sprintf("%s/go.orig", tmpdir)
+		os.Rename(outfile, origOutput)
+		if err := os.Rename(combinedOutput, outfile); err != nil {
+			Ctxt.Cursym = nil
+			Exitf("%s: rename(%s, %s) failed: %v", os.Args[0], combinedOutput, outfile, err)
+		}
+	}
 }
 
 func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, whence int) {
diff --git a/src/cmd/internal/ld/macho.go b/src/cmd/internal/ld/macho.go
index ceeb7b0f5d..0258aff104 100644
--- a/src/cmd/internal/ld/macho.go
+++ b/src/cmd/internal/ld/macho.go
@@ -443,7 +443,8 @@ func Asmbmacho() {
 		ms = newMachoSeg("", 40)
 
 		ms.fileoffset = Segtext.Fileoff
-		ms.filesize = Segdata.Fileoff + Segdata.Filelen - Segtext.Fileoff
+		ms.filesize = Segdwarf.Fileoff + Segdwarf.Filelen - Segtext.Fileoff
+		ms.vsize = ms.filesize
 	}
 
 	/* segment for zero page */
@@ -561,8 +562,8 @@ func Asmbmacho() {
 	}
 
 	// TODO: dwarf headers go in ms too
-	if Debug['s'] == 0 && Linkmode != LinkExternal {
-		dwarfaddmachoheaders()
+	if Debug['s'] == 0 {
+		dwarfaddmachoheaders(ms)
 	}
 
 	a := machowrite()
@@ -850,4 +851,5 @@ func Machoemitreloc() {
 	for sect := Segdata.Sect; sect != nil; sect = sect.Next {
 		machorelocsect(sect, datap)
 	}
+	dwarfemitreloc()
 }
diff --git a/src/cmd/internal/ld/macho_combine_dwarf.go b/src/cmd/internal/ld/macho_combine_dwarf.go
new file mode 100644
index 0000000000..9134373a52
--- /dev/null
+++ b/src/cmd/internal/ld/macho_combine_dwarf.go
@@ -0,0 +1,369 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package ld
+
+import (
+	"bytes"
+	"debug/macho"
+	"encoding/binary"
+	"fmt"
+	"io"
+	"os"
+	"reflect"
+	"unsafe"
+)
+
+var fakedwarf, realdwarf, linkseg *macho.Segment
+var dwarfstart, linkstart int64
+var linkoffset uint32
+var machHeader *macho.FileHeader
+var mappedHeader []byte
+
+const (
+	LC_LOAD_DYLINKER        = 0xe
+	LC_PREBOUND_DYLIB       = 0x10
+	LC_LOAD_WEAK_DYLIB      = 0x18
+	LC_UUID                 = 0x1b
+	LC_RPATH                = 0x8000001c
+	LC_CODE_SIGNATURE       = 0x1d
+	LC_SEGMENT_SPLIT_INFO   = 0x1e
+	LC_REEXPORT_DYLIB       = 0x8000001f
+	LC_ENCRYPTION_INFO      = 0x21
+	LC_DYLD_INFO            = 0x22
+	LC_DYLD_INFO_ONLY       = 0x80000022
+	LC_VERSION_MIN_MACOSX   = 0x24
+	LC_VERSION_MIN_IPHONEOS = 0x25
+	LC_FUNCTION_STARTS      = 0x26
+	LC_MAIN                 = 0x80000028
+	LC_DATA_IN_CODE         = 0x29
+	LC_SOURCE_VERSION       = 0x2A
+	LC_DYLIB_CODE_SIGN_DRS  = 0x2B
+	LC_ENCRYPTION_INFO_64   = 0x2C
+
+	dwarfMinAlign = 6  // 64 = 1 << 6
+	pageAlign     = 12 // 4096 = 1 << 12
+)
+
+type loadCmd struct {
+	Cmd macho.LoadCmd
+	Len uint32
+}
+
+type dyldInfoCmd struct {
+	Cmd                      macho.LoadCmd
+	Len                      uint32
+	RebaseOff, RebaseLen     uint32
+	BindOff, BindLen         uint32
+	WeakBindOff, WeakBindLen uint32
+	LazyBindOff, LazyBindLen uint32
+	ExportOff, ExportLen     uint32
+}
+
+type linkEditDataCmd struct {
+	Cmd              macho.LoadCmd
+	Len              uint32
+	DataOff, DataLen uint32
+}
+
+type encryptionInfoCmd struct {
+	Cmd                macho.LoadCmd
+	Len                uint32
+	CryptOff, CryptLen uint32
+	CryptId            uint32
+}
+
+type loadCmdReader struct {
+	offset, next int64
+	f            *os.File
+	order        binary.ByteOrder
+}
+
+func (r *loadCmdReader) Next() (cmd loadCmd, err error) {
+	r.offset = r.next
+	if _, err = r.f.Seek(r.offset, 0); err != nil {
+		return
+	}
+	if err = binary.Read(r.f, r.order, &cmd); err != nil {
+		return
+	}
+	r.next = r.offset + int64(cmd.Len)
+	return
+}
+
+func (r loadCmdReader) ReadAt(offset int64, data interface{}) error {
+	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+		return err
+	}
+	return binary.Read(r.f, r.order, data)
+}
+
+func (r loadCmdReader) WriteAt(offset int64, data interface{}) error {
+	if _, err := r.f.Seek(r.offset+offset, 0); err != nil {
+		return err
+	}
+	return binary.Write(r.f, r.order, data)
+}
+
+// machoCombineDwarf merges dwarf info generated by dsymutil into a macho executable.
+// With internal linking, DWARF is embedded into the executable, this lets us do the
+// same for external linking.
+// inexe is the path to the executable with no DWARF. It must have enough room in the macho
+// header to add the DWARF sections. (Use ld's -headerpad option)
+// dsym is the path to the macho file containing DWARF from dsymutil.
+// outexe is the path where the combined executable should be saved.
+func machoCombineDwarf(inexe, dsym, outexe string) error {
+	exef, err := os.Open(inexe)
+	if err != nil {
+		return err
+	}
+	dwarff, err := os.Open(dsym)
+	if err != nil {
+		return err
+	}
+	outf, err := os.Create(outexe)
+	if err != nil {
+		return err
+	}
+	outf.Chmod(0755)
+
+	exem, err := macho.NewFile(exef)
+	if err != nil {
+		return err
+	}
+	dwarfm, err := macho.NewFile(dwarff)
+	if err != nil {
+		return err
+	}
+
+	// The string table needs to be the last thing in the file
+	// for code signing to work. So we'll need to move the
+	// linkedit section, but all the others can be copied directly.
+	linkseg = exem.Segment("__LINKEDIT")
+	if linkseg == nil {
+		return fmt.Errorf("missing __LINKEDIT segment")
+	}
+
+	if _, err = exef.Seek(0, 0); err != nil {
+		return err
+	}
+	if _, err := io.CopyN(outf, exef, int64(linkseg.Offset)); err != nil {
+		return err
+	}
+
+	realdwarf = dwarfm.Segment("__DWARF")
+	if realdwarf == nil {
+		return fmt.Errorf("missing __DWARF segment")
+	}
+
+	// Now copy the dwarf data into the output.
+	maxalign := uint32(dwarfMinAlign) //
+	for _, sect := range dwarfm.Sections {
+		if sect.Align > maxalign {
+			maxalign = sect.Align
+		}
+	}
+	dwarfstart = machoCalcStart(realdwarf.Offset, linkseg.Offset, maxalign)
+	if _, err = outf.Seek(dwarfstart, 0); err != nil {
+		return err
+	}
+
+	if _, err = dwarff.Seek(int64(realdwarf.Offset), 0); err != nil {
+		return err
+	}
+	if _, err := io.CopyN(outf, dwarff, int64(realdwarf.Filesz)); err != nil {
+		return err
+	}
+
+	// And finally the linkedit section.
+	if _, err = exef.Seek(int64(linkseg.Offset), 0); err != nil {
+		return err
+	}
+	linkstart = machoCalcStart(linkseg.Offset, uint64(dwarfstart)+realdwarf.Filesz, pageAlign)
+	linkoffset = uint32(linkstart - int64(linkseg.Offset))
+	if _, err = outf.Seek(linkstart, 0); err != nil {
+		return err
+	}
+	if _, err := io.Copy(outf, exef); err != nil {
+		return err
+	}
+
+	// Now we need to update the headers.
+	cmdOffset := unsafe.Sizeof(exem.FileHeader)
+	is64bit := exem.Magic == macho.Magic64
+	if is64bit {
+		// mach_header_64 has one extra uint32.
+		cmdOffset += unsafe.Sizeof(exem.Magic)
+	}
+
+	textsect := exem.Section("__text")
+	if linkseg == nil {
+		return fmt.Errorf("missing __text section")
+	}
+
+	dwarfCmdOffset := int64(cmdOffset) + int64(exem.FileHeader.Cmdsz)
+	availablePadding := int64(textsect.Offset) - dwarfCmdOffset
+	if availablePadding < int64(realdwarf.Len) {
+		return fmt.Errorf("No room to add dwarf info. Need at least %d padding bytes, found %d", realdwarf.Len, availablePadding)
+	}
+	// First, copy the dwarf load command into the header
+	if _, err = outf.Seek(dwarfCmdOffset, 0); err != nil {
+		return err
+	}
+	if _, err := io.CopyN(outf, bytes.NewReader(realdwarf.Raw()), int64(realdwarf.Len)); err != nil {
+		return err
+	}
+
+	if _, err = outf.Seek(int64(unsafe.Offsetof(exem.FileHeader.Ncmd)), 0); err != nil {
+		return err
+	}
+	if err = binary.Write(outf, exem.ByteOrder, exem.Ncmd+1); err != nil {
+		return err
+	}
+	if err = binary.Write(outf, exem.ByteOrder, exem.Cmdsz+realdwarf.Len); err != nil {
+		return err
+	}
+
+	reader := loadCmdReader{next: int64(cmdOffset), f: outf, order: exem.ByteOrder}
+	for i := uint32(0); i < exem.Ncmd; i++ {
+		cmd, err := reader.Next()
+		if err != nil {
+			return err
+		}
+		switch cmd.Cmd {
+		case macho.LoadCmdSegment64:
+			err = machoUpdateSegment(reader, &macho.Segment64{}, &macho.Section64{})
+		case macho.LoadCmdSegment:
+			err = machoUpdateSegment(reader, &macho.Segment32{}, &macho.Section32{})
+		case LC_DYLD_INFO, LC_DYLD_INFO_ONLY:
+			err = machoUpdateLoadCommand(reader, &dyldInfoCmd{}, "RebaseOff", "BindOff", "WeakBindOff", "LazyBindOff", "ExportOff")
+		case macho.LoadCmdSymtab:
+			err = machoUpdateLoadCommand(reader, &macho.SymtabCmd{}, "Symoff", "Stroff")
+		case macho.LoadCmdDysymtab:
+			err = machoUpdateLoadCommand(reader, &macho.DysymtabCmd{}, "Tocoffset", "Modtaboff", "Extrefsymoff", "Indirectsymoff", "Extreloff", "Locreloff")
+		case LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, LC_FUNCTION_STARTS, LC_DATA_IN_CODE, LC_DYLIB_CODE_SIGN_DRS:
+			err = machoUpdateLoadCommand(reader, &linkEditDataCmd{}, "DataOff")
+		case LC_ENCRYPTION_INFO, LC_ENCRYPTION_INFO_64:
+			err = machoUpdateLoadCommand(reader, &encryptionInfoCmd{}, "CryptOff")
+		case macho.LoadCmdDylib, macho.LoadCmdThread, macho.LoadCmdUnixThread, LC_PREBOUND_DYLIB, LC_UUID, LC_VERSION_MIN_MACOSX, LC_VERSION_MIN_IPHONEOS, LC_SOURCE_VERSION, LC_MAIN, LC_LOAD_DYLINKER, LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_RPATH:
+			// Nothing to update
+		default:
+			err = fmt.Errorf("Unknown load command 0x%x (%s)\n", int(cmd.Cmd), cmd.Cmd)
+		}
+		if err != nil {
+			return err
+		}
+	}
+	return machoUpdateDwarfHeader(&reader)
+}
+
+// machoUpdateSegment updates the load command for a moved segment.
+// Only the linkedit segment should move, and it should have 0 sections.
+// seg should be a macho.Segment32 or macho.Segment64 as appropriate.
+// sect should be a macho.Section32 or macho.Section64 as appropriate.
+func machoUpdateSegment(r loadCmdReader, seg, sect interface{}) error {
+	if err := r.ReadAt(0, seg); err != nil {
+		return err
+	}
+	segValue := reflect.ValueOf(seg)
+	offset := reflect.Indirect(segValue).FieldByName("Offset")
+
+	// Only the linkedit segment moved, any thing before that is fine.
+	if offset.Uint() < linkseg.Offset {
+		return nil
+	}
+	offset.SetUint(offset.Uint() + uint64(linkoffset))
+	if err := r.WriteAt(0, seg); err != nil {
+		return err
+	}
+	// There shouldn't be any sections, but just to make sure...
+	return machoUpdateSections(r, segValue, reflect.ValueOf(sect), uint64(linkoffset))
+}
+
+func machoUpdateSections(r loadCmdReader, seg, sect reflect.Value, delta uint64) error {
+	iseg := reflect.Indirect(seg)
+	nsect := iseg.FieldByName("Nsect").Uint()
+	if nsect == 0 {
+		return nil
+	}
+	sectOffset := int64(iseg.Type().Size())
+
+	isect := reflect.Indirect(sect)
+	offsetField := isect.FieldByName("Offset")
+	reloffField := isect.FieldByName("Reloff")
+	sectSize := int64(isect.Type().Size())
+	for i := uint64(0); i < nsect; i++ {
+		if err := r.ReadAt(sectOffset, sect.Interface()); err != nil {
+			return err
+		}
+		if offsetField.Uint() != 0 {
+			offsetField.SetUint(offsetField.Uint() + delta)
+		}
+		if reloffField.Uint() != 0 {
+			reloffField.SetUint(reloffField.Uint() + delta)
+		}
+		if err := r.WriteAt(sectOffset, sect.Interface()); err != nil {
+			return err
+		}
+		sectOffset += sectSize
+	}
+	return nil
+}
+
+// machoUpdateDwarfHeader updates the DWARF segment load command.
+func machoUpdateDwarfHeader(r *loadCmdReader) error {
+	var seg, sect interface{}
+	cmd, err := r.Next()
+	if err != nil {
+		return err
+	}
+	if cmd.Cmd == macho.LoadCmdSegment64 {
+		seg = new(macho.Segment64)
+		sect = new(macho.Section64)
+	} else {
+		seg = new(macho.Segment32)
+		sect = new(macho.Section32)
+	}
+	if err := r.ReadAt(0, seg); err != nil {
+		return err
+	}
+	segValue := reflect.ValueOf(seg)
+	offset := reflect.Indirect(segValue).FieldByName("Offset")
+
+	delta := uint64(dwarfstart) - realdwarf.Offset
+	offset.SetUint(offset.Uint() + delta)
+	if err := r.WriteAt(0, seg); err != nil {
+		return err
+	}
+	return machoUpdateSections(*r, segValue, reflect.ValueOf(sect), delta)
+}
+
+func machoUpdateLoadCommand(r loadCmdReader, cmd interface{}, fields ...string) error {
+	if err := r.ReadAt(0, cmd); err != nil {
+		return err
+	}
+	value := reflect.Indirect(reflect.ValueOf(cmd))
+
+	for _, name := range fields {
+		field := value.FieldByName(name)
+		fieldval := field.Uint()
+		if fieldval >= linkseg.Offset {
+			field.SetUint(fieldval + uint64(linkoffset))
+		}
+	}
+	if err := r.WriteAt(0, cmd); err != nil {
+		return err
+	}
+	return nil
+}
+
+func machoCalcStart(origAddr, newAddr uint64, alignExp uint32) int64 {
+	align := uint64(1 << alignExp)
+	if (origAddr % align) == (newAddr % align) {
+		return int64(newAddr)
+	}
+	padding := (align - (newAddr % align))
+	padding += origAddr % align
+	return int64(padding + newAddr)
+}

From 2a141dedc4bb7b33f5caee6e7b185dfbd20c92bc Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 21 May 2015 13:28:06 -0400
Subject: [PATCH 172/232] cmd/link: move to cmd/newlink

In preparation for making the current linker cmd/link.
If cmd/newlink is ever completed, it can be moved back.

See golang-dev thread titled "go tool compile, etc" for background.

Change-Id: I4029580f470038240c5181a37ea4202ba971f9ef
Reviewed-on: https://go-review.googlesource.com/10286
Reviewed-by: Rob Pike 
---
 misc/nacl/testzip.proto                             |   2 +-
 src/cmd/dist/build.go                               |   2 +-
 src/cmd/go/pkg.go                                   |   2 +-
 src/cmd/{link => newlink}/auto.go                   |   0
 src/cmd/{link => newlink}/auto_test.go              |   0
 src/cmd/{link => newlink}/dead.go                   |   0
 src/cmd/{link => newlink}/dead_test.go              |   0
 src/cmd/{link => newlink}/debug.go                  |   0
 src/cmd/{link => newlink}/hex_test.go               |   0
 src/cmd/{link => newlink}/layout.go                 |   0
 src/cmd/{link => newlink}/layout_test.go            |   0
 src/cmd/{link => newlink}/link_test.go              |   0
 src/cmd/{link => newlink}/load.go                   |   0
 src/cmd/{link => newlink}/macho.go                  |   0
 src/cmd/{link => newlink}/macho_test.go             |   0
 src/cmd/{link => newlink}/main.go                   |   0
 src/cmd/{link => newlink}/pclntab.go                |   0
 src/cmd/{link => newlink}/pclntab_test.go           |   0
 src/cmd/{link => newlink}/prog.go                   |   0
 src/cmd/{link => newlink}/prog_test.go              |   0
 src/cmd/{link => newlink}/runtime.go                |   0
 src/cmd/{link => newlink}/scan.go                   |   0
 src/cmd/{link => newlink}/testdata/Makefile         |   0
 src/cmd/{link => newlink}/testdata/autosection.6    | Bin
 src/cmd/{link => newlink}/testdata/autosection.s    |   0
 src/cmd/{link => newlink}/testdata/autoweak.6       | Bin
 src/cmd/{link => newlink}/testdata/autoweak.s       |   0
 src/cmd/{link => newlink}/testdata/dead.6           | Bin
 src/cmd/{link => newlink}/testdata/dead.s           |   0
 src/cmd/{link => newlink}/testdata/genpcln.go       |   0
 src/cmd/{link => newlink}/testdata/hello.6          | Bin
 src/cmd/{link => newlink}/testdata/hello.s          |   0
 src/cmd/{link => newlink}/testdata/layout.6         | Bin
 src/cmd/{link => newlink}/testdata/layout.s         |   0
 .../testdata/link.hello.darwin.amd64                |   0
 .../{link => newlink}/testdata/macho.amd64.exit9    |   0
 .../{link => newlink}/testdata/macho.amd64.hello    |   0
 .../{link => newlink}/testdata/macho.amd64.helloro  |   0
 src/cmd/{link => newlink}/testdata/pclntab.6        | Bin
 src/cmd/{link => newlink}/testdata/pclntab.s        |   0
 src/cmd/{link => newlink}/util.go                   |   0
 src/cmd/{link => newlink}/write.go                  |   0
 42 files changed, 3 insertions(+), 3 deletions(-)
 rename src/cmd/{link => newlink}/auto.go (100%)
 rename src/cmd/{link => newlink}/auto_test.go (100%)
 rename src/cmd/{link => newlink}/dead.go (100%)
 rename src/cmd/{link => newlink}/dead_test.go (100%)
 rename src/cmd/{link => newlink}/debug.go (100%)
 rename src/cmd/{link => newlink}/hex_test.go (100%)
 rename src/cmd/{link => newlink}/layout.go (100%)
 rename src/cmd/{link => newlink}/layout_test.go (100%)
 rename src/cmd/{link => newlink}/link_test.go (100%)
 rename src/cmd/{link => newlink}/load.go (100%)
 rename src/cmd/{link => newlink}/macho.go (100%)
 rename src/cmd/{link => newlink}/macho_test.go (100%)
 rename src/cmd/{link => newlink}/main.go (100%)
 rename src/cmd/{link => newlink}/pclntab.go (100%)
 rename src/cmd/{link => newlink}/pclntab_test.go (100%)
 rename src/cmd/{link => newlink}/prog.go (100%)
 rename src/cmd/{link => newlink}/prog_test.go (100%)
 rename src/cmd/{link => newlink}/runtime.go (100%)
 rename src/cmd/{link => newlink}/scan.go (100%)
 rename src/cmd/{link => newlink}/testdata/Makefile (100%)
 rename src/cmd/{link => newlink}/testdata/autosection.6 (100%)
 rename src/cmd/{link => newlink}/testdata/autosection.s (100%)
 rename src/cmd/{link => newlink}/testdata/autoweak.6 (100%)
 rename src/cmd/{link => newlink}/testdata/autoweak.s (100%)
 rename src/cmd/{link => newlink}/testdata/dead.6 (100%)
 rename src/cmd/{link => newlink}/testdata/dead.s (100%)
 rename src/cmd/{link => newlink}/testdata/genpcln.go (100%)
 rename src/cmd/{link => newlink}/testdata/hello.6 (100%)
 rename src/cmd/{link => newlink}/testdata/hello.s (100%)
 rename src/cmd/{link => newlink}/testdata/layout.6 (100%)
 rename src/cmd/{link => newlink}/testdata/layout.s (100%)
 rename src/cmd/{link => newlink}/testdata/link.hello.darwin.amd64 (100%)
 rename src/cmd/{link => newlink}/testdata/macho.amd64.exit9 (100%)
 rename src/cmd/{link => newlink}/testdata/macho.amd64.hello (100%)
 rename src/cmd/{link => newlink}/testdata/macho.amd64.helloro (100%)
 rename src/cmd/{link => newlink}/testdata/pclntab.6 (100%)
 rename src/cmd/{link => newlink}/testdata/pclntab.s (100%)
 rename src/cmd/{link => newlink}/util.go (100%)
 rename src/cmd/{link => newlink}/write.go (100%)

diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto
index 1c013c1784..8e53726ea5 100644
--- a/misc/nacl/testzip.proto
+++ b/misc/nacl/testzip.proto
@@ -35,7 +35,7 @@ go	src=..
 				gofmt_test.go
 				testdata
 					+
-			link
+			newlink
 				testdata
 					+
 		archive
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 64b2399972..47c0a0a1d1 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -374,7 +374,7 @@ var oldtool = []string{
 // Unreleased directories (relative to $GOROOT) that should
 // not be in release branches.
 var unreleased = []string{
-	"src/cmd/link",
+	"src/cmd/newlink",
 	"src/cmd/objwriter",
 	"src/debug/goobj",
 	"src/old",
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index f9cf14fd2c..41e66ef9c9 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -412,7 +412,7 @@ var goTools = map[string]targetDir{
 	"cmd/dist":                             toTool,
 	"cmd/doc":                              toTool,
 	"cmd/fix":                              toTool,
-	"cmd/link":                             toTool,
+	"cmd/newlink":                          toTool,
 	"cmd/nm":                               toTool,
 	"cmd/objdump":                          toTool,
 	"cmd/old5a":                            toTool,
diff --git a/src/cmd/link/auto.go b/src/cmd/newlink/auto.go
similarity index 100%
rename from src/cmd/link/auto.go
rename to src/cmd/newlink/auto.go
diff --git a/src/cmd/link/auto_test.go b/src/cmd/newlink/auto_test.go
similarity index 100%
rename from src/cmd/link/auto_test.go
rename to src/cmd/newlink/auto_test.go
diff --git a/src/cmd/link/dead.go b/src/cmd/newlink/dead.go
similarity index 100%
rename from src/cmd/link/dead.go
rename to src/cmd/newlink/dead.go
diff --git a/src/cmd/link/dead_test.go b/src/cmd/newlink/dead_test.go
similarity index 100%
rename from src/cmd/link/dead_test.go
rename to src/cmd/newlink/dead_test.go
diff --git a/src/cmd/link/debug.go b/src/cmd/newlink/debug.go
similarity index 100%
rename from src/cmd/link/debug.go
rename to src/cmd/newlink/debug.go
diff --git a/src/cmd/link/hex_test.go b/src/cmd/newlink/hex_test.go
similarity index 100%
rename from src/cmd/link/hex_test.go
rename to src/cmd/newlink/hex_test.go
diff --git a/src/cmd/link/layout.go b/src/cmd/newlink/layout.go
similarity index 100%
rename from src/cmd/link/layout.go
rename to src/cmd/newlink/layout.go
diff --git a/src/cmd/link/layout_test.go b/src/cmd/newlink/layout_test.go
similarity index 100%
rename from src/cmd/link/layout_test.go
rename to src/cmd/newlink/layout_test.go
diff --git a/src/cmd/link/link_test.go b/src/cmd/newlink/link_test.go
similarity index 100%
rename from src/cmd/link/link_test.go
rename to src/cmd/newlink/link_test.go
diff --git a/src/cmd/link/load.go b/src/cmd/newlink/load.go
similarity index 100%
rename from src/cmd/link/load.go
rename to src/cmd/newlink/load.go
diff --git a/src/cmd/link/macho.go b/src/cmd/newlink/macho.go
similarity index 100%
rename from src/cmd/link/macho.go
rename to src/cmd/newlink/macho.go
diff --git a/src/cmd/link/macho_test.go b/src/cmd/newlink/macho_test.go
similarity index 100%
rename from src/cmd/link/macho_test.go
rename to src/cmd/newlink/macho_test.go
diff --git a/src/cmd/link/main.go b/src/cmd/newlink/main.go
similarity index 100%
rename from src/cmd/link/main.go
rename to src/cmd/newlink/main.go
diff --git a/src/cmd/link/pclntab.go b/src/cmd/newlink/pclntab.go
similarity index 100%
rename from src/cmd/link/pclntab.go
rename to src/cmd/newlink/pclntab.go
diff --git a/src/cmd/link/pclntab_test.go b/src/cmd/newlink/pclntab_test.go
similarity index 100%
rename from src/cmd/link/pclntab_test.go
rename to src/cmd/newlink/pclntab_test.go
diff --git a/src/cmd/link/prog.go b/src/cmd/newlink/prog.go
similarity index 100%
rename from src/cmd/link/prog.go
rename to src/cmd/newlink/prog.go
diff --git a/src/cmd/link/prog_test.go b/src/cmd/newlink/prog_test.go
similarity index 100%
rename from src/cmd/link/prog_test.go
rename to src/cmd/newlink/prog_test.go
diff --git a/src/cmd/link/runtime.go b/src/cmd/newlink/runtime.go
similarity index 100%
rename from src/cmd/link/runtime.go
rename to src/cmd/newlink/runtime.go
diff --git a/src/cmd/link/scan.go b/src/cmd/newlink/scan.go
similarity index 100%
rename from src/cmd/link/scan.go
rename to src/cmd/newlink/scan.go
diff --git a/src/cmd/link/testdata/Makefile b/src/cmd/newlink/testdata/Makefile
similarity index 100%
rename from src/cmd/link/testdata/Makefile
rename to src/cmd/newlink/testdata/Makefile
diff --git a/src/cmd/link/testdata/autosection.6 b/src/cmd/newlink/testdata/autosection.6
similarity index 100%
rename from src/cmd/link/testdata/autosection.6
rename to src/cmd/newlink/testdata/autosection.6
diff --git a/src/cmd/link/testdata/autosection.s b/src/cmd/newlink/testdata/autosection.s
similarity index 100%
rename from src/cmd/link/testdata/autosection.s
rename to src/cmd/newlink/testdata/autosection.s
diff --git a/src/cmd/link/testdata/autoweak.6 b/src/cmd/newlink/testdata/autoweak.6
similarity index 100%
rename from src/cmd/link/testdata/autoweak.6
rename to src/cmd/newlink/testdata/autoweak.6
diff --git a/src/cmd/link/testdata/autoweak.s b/src/cmd/newlink/testdata/autoweak.s
similarity index 100%
rename from src/cmd/link/testdata/autoweak.s
rename to src/cmd/newlink/testdata/autoweak.s
diff --git a/src/cmd/link/testdata/dead.6 b/src/cmd/newlink/testdata/dead.6
similarity index 100%
rename from src/cmd/link/testdata/dead.6
rename to src/cmd/newlink/testdata/dead.6
diff --git a/src/cmd/link/testdata/dead.s b/src/cmd/newlink/testdata/dead.s
similarity index 100%
rename from src/cmd/link/testdata/dead.s
rename to src/cmd/newlink/testdata/dead.s
diff --git a/src/cmd/link/testdata/genpcln.go b/src/cmd/newlink/testdata/genpcln.go
similarity index 100%
rename from src/cmd/link/testdata/genpcln.go
rename to src/cmd/newlink/testdata/genpcln.go
diff --git a/src/cmd/link/testdata/hello.6 b/src/cmd/newlink/testdata/hello.6
similarity index 100%
rename from src/cmd/link/testdata/hello.6
rename to src/cmd/newlink/testdata/hello.6
diff --git a/src/cmd/link/testdata/hello.s b/src/cmd/newlink/testdata/hello.s
similarity index 100%
rename from src/cmd/link/testdata/hello.s
rename to src/cmd/newlink/testdata/hello.s
diff --git a/src/cmd/link/testdata/layout.6 b/src/cmd/newlink/testdata/layout.6
similarity index 100%
rename from src/cmd/link/testdata/layout.6
rename to src/cmd/newlink/testdata/layout.6
diff --git a/src/cmd/link/testdata/layout.s b/src/cmd/newlink/testdata/layout.s
similarity index 100%
rename from src/cmd/link/testdata/layout.s
rename to src/cmd/newlink/testdata/layout.s
diff --git a/src/cmd/link/testdata/link.hello.darwin.amd64 b/src/cmd/newlink/testdata/link.hello.darwin.amd64
similarity index 100%
rename from src/cmd/link/testdata/link.hello.darwin.amd64
rename to src/cmd/newlink/testdata/link.hello.darwin.amd64
diff --git a/src/cmd/link/testdata/macho.amd64.exit9 b/src/cmd/newlink/testdata/macho.amd64.exit9
similarity index 100%
rename from src/cmd/link/testdata/macho.amd64.exit9
rename to src/cmd/newlink/testdata/macho.amd64.exit9
diff --git a/src/cmd/link/testdata/macho.amd64.hello b/src/cmd/newlink/testdata/macho.amd64.hello
similarity index 100%
rename from src/cmd/link/testdata/macho.amd64.hello
rename to src/cmd/newlink/testdata/macho.amd64.hello
diff --git a/src/cmd/link/testdata/macho.amd64.helloro b/src/cmd/newlink/testdata/macho.amd64.helloro
similarity index 100%
rename from src/cmd/link/testdata/macho.amd64.helloro
rename to src/cmd/newlink/testdata/macho.amd64.helloro
diff --git a/src/cmd/link/testdata/pclntab.6 b/src/cmd/newlink/testdata/pclntab.6
similarity index 100%
rename from src/cmd/link/testdata/pclntab.6
rename to src/cmd/newlink/testdata/pclntab.6
diff --git a/src/cmd/link/testdata/pclntab.s b/src/cmd/newlink/testdata/pclntab.s
similarity index 100%
rename from src/cmd/link/testdata/pclntab.s
rename to src/cmd/newlink/testdata/pclntab.s
diff --git a/src/cmd/link/util.go b/src/cmd/newlink/util.go
similarity index 100%
rename from src/cmd/link/util.go
rename to src/cmd/newlink/util.go
diff --git a/src/cmd/link/write.go b/src/cmd/newlink/write.go
similarity index 100%
rename from src/cmd/link/write.go
rename to src/cmd/newlink/write.go

From 17eba6e6b72b9dbf24d73a84be22edd65c229631 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 21 May 2015 13:28:10 -0400
Subject: [PATCH 173/232] cmd/compile, cmd/link: create from 5g, 5l, etc

Trivial merging of 5g, 6g, ... into go tool compile,
and similarlly 5l, 6l, ... into go tool link.
The files compile/main.go and link/main.go are new.
Everything else in those directories is a move followed by
change of imports and package name.

This CL breaks the build. Manual fixups are in the next CL.

See golang-dev thread titled "go tool compile, etc" for background.

Change-Id: Id35ff5a5859ad9037c61275d637b1bd51df6828b
Reviewed-on: https://go-review.googlesource.com/10287
Reviewed-by: Dave Cheney 
Reviewed-by: Rob Pike 
---
 src/cmd/6l/z.go                               |  1 -
 .../{6g => compile/internal/amd64}/cgen.go    |  4 +--
 .../{6g => compile/internal/amd64}/galign.go  |  6 ++--
 .../{6g => compile/internal/amd64}/ggen.go    |  4 +--
 .../{6g => compile/internal/amd64}/gsubr.go   |  6 ++--
 .../{6g => compile/internal/amd64}/peep.go    |  4 +--
 .../{6g => compile/internal/amd64}/prog.go    |  4 +--
 src/cmd/{6g => compile/internal/amd64}/reg.go |  4 +--
 src/cmd/{5g => compile/internal/arm}/cgen.go  |  4 +--
 .../{5g => compile/internal/arm}/cgen64.go    |  4 +--
 .../{5g => compile/internal/arm}/galign.go    |  6 ++--
 src/cmd/{5g => compile/internal/arm}/ggen.go  |  4 +--
 src/cmd/{5g => compile/internal/arm}/gsubr.go |  4 +--
 src/cmd/{5g => compile/internal/arm}/peep.go  |  4 +--
 src/cmd/{5g => compile/internal/arm}/prog.go  |  4 +--
 src/cmd/{5g => compile/internal/arm}/reg.go   |  4 +--
 .../{7g => compile/internal/arm64}/cgen.go    |  4 +--
 .../{7g => compile/internal/arm64}/galign.go  |  6 ++--
 .../{7g => compile/internal/arm64}/ggen.go    |  4 +--
 .../{7g => compile/internal/arm64}/gsubr.go   |  4 +--
 .../{7g => compile/internal/arm64}/peep.go    |  4 +--
 .../{7g => compile/internal/arm64}/prog.go    |  4 +--
 src/cmd/{7g => compile/internal/arm64}/reg.go |  4 +--
 .../internal}/big/accuracy_string.go          |  0
 .../gc => compile/internal}/big/arith.go      |  0
 .../gc => compile/internal}/big/arith_decl.go |  0
 .../gc => compile/internal}/big/arith_test.go |  0
 .../gc => compile/internal}/big/bits_test.go  |  0
 .../internal}/big/calibrate_test.go           |  0
 .../gc => compile/internal}/big/decimal.go    |  0
 .../internal}/big/decimal_test.go             |  0
 .../internal}/big/example_test.go             |  0
 .../gc => compile/internal}/big/float.go      |  0
 .../gc => compile/internal}/big/float_test.go |  0
 .../gc => compile/internal}/big/floatconv.go  |  0
 .../internal}/big/floatconv_test.go           |  0
 .../internal}/big/floatexample_test.go        |  0
 .../gc => compile/internal}/big/ftoa.go       |  0
 .../gc => compile/internal}/big/gcd_test.go   |  0
 .../internal}/big/hilbert_test.go             |  0
 .../gc => compile/internal}/big/int.go        |  0
 .../gc => compile/internal}/big/int_test.go   |  0
 .../gc => compile/internal}/big/intconv.go    |  0
 .../internal}/big/intconv_test.go             |  0
 .../gc => compile/internal}/big/nat.go        |  0
 .../gc => compile/internal}/big/nat_test.go   |  0
 .../gc => compile/internal}/big/natconv.go    |  0
 .../internal}/big/natconv_test.go             |  0
 .../gc => compile/internal}/big/rat.go        |  0
 .../gc => compile/internal}/big/rat_test.go   |  0
 .../gc => compile/internal}/big/ratconv.go    |  0
 .../internal}/big/ratconv_test.go             |  0
 .../internal}/big/roundingmode_string.go      |  0
 .../gc => compile/internal}/big/vendor.bash   |  0
 src/cmd/{ => compile}/internal/gc/align.go    |  0
 src/cmd/{ => compile}/internal/gc/builtin.go  |  0
 .../internal/gc/builtin/runtime.go            |  0
 .../internal/gc/builtin/unsafe.go             |  0
 src/cmd/{ => compile}/internal/gc/bv.go       |  0
 src/cmd/{ => compile}/internal/gc/cgen.go     |  0
 src/cmd/{ => compile}/internal/gc/closure.go  |  0
 src/cmd/{ => compile}/internal/gc/const.go    |  2 +-
 src/cmd/{ => compile}/internal/gc/cplx.go     |  0
 src/cmd/{ => compile}/internal/gc/dcl.go      |  0
 src/cmd/{ => compile}/internal/gc/esc.go      |  0
 src/cmd/{ => compile}/internal/gc/export.go   |  0
 src/cmd/{ => compile}/internal/gc/fmt.go      |  0
 src/cmd/{ => compile}/internal/gc/gen.go      |  0
 src/cmd/{ => compile}/internal/gc/go.go       |  2 +-
 src/cmd/{ => compile}/internal/gc/go.y        |  0
 src/cmd/{ => compile}/internal/gc/gsubr.go    |  0
 src/cmd/{ => compile}/internal/gc/init.go     |  0
 src/cmd/{ => compile}/internal/gc/inl.go      |  0
 src/cmd/{ => compile}/internal/gc/lex.go      |  0
 .../{ => compile}/internal/gc/mkbuiltin.go    |  0
 src/cmd/{ => compile}/internal/gc/mparith2.go |  2 +-
 src/cmd/{ => compile}/internal/gc/mparith3.go |  2 +-
 src/cmd/{ => compile}/internal/gc/obj.go      |  0
 src/cmd/{ => compile}/internal/gc/opnames.go  |  0
 src/cmd/{ => compile}/internal/gc/order.go    |  0
 src/cmd/{ => compile}/internal/gc/pgen.go     |  0
 src/cmd/{ => compile}/internal/gc/plive.go    |  0
 src/cmd/{ => compile}/internal/gc/popt.go     |  0
 src/cmd/{ => compile}/internal/gc/racewalk.go |  0
 src/cmd/{ => compile}/internal/gc/range.go    |  0
 src/cmd/{ => compile}/internal/gc/reflect.go  |  0
 src/cmd/{ => compile}/internal/gc/reg.go      |  0
 src/cmd/{ => compile}/internal/gc/select.go   |  0
 src/cmd/{ => compile}/internal/gc/sinit.go    |  0
 src/cmd/{ => compile}/internal/gc/subr.go     |  0
 src/cmd/{ => compile}/internal/gc/swt.go      |  0
 src/cmd/{ => compile}/internal/gc/syntax.go   |  0
 .../{ => compile}/internal/gc/typecheck.go    |  0
 src/cmd/{ => compile}/internal/gc/unsafe.go   |  0
 src/cmd/{ => compile}/internal/gc/util.go     |  0
 src/cmd/{ => compile}/internal/gc/walk.go     |  0
 src/cmd/{ => compile}/internal/gc/y.go        |  0
 src/cmd/{ => compile}/internal/gc/y.output    |  0
 .../{9g => compile/internal/ppc64}/cgen.go    |  4 +--
 .../{9g => compile/internal/ppc64}/galign.go  |  6 ++--
 .../{9g => compile/internal/ppc64}/ggen.go    |  4 +--
 .../{9g => compile/internal/ppc64}/gsubr.go   |  6 ++--
 src/cmd/{9g => compile/internal/ppc64}/opt.go |  2 +-
 .../{9g => compile/internal/ppc64}/peep.go    |  4 +--
 .../{9g => compile/internal/ppc64}/prog.go    |  4 +--
 src/cmd/{9g => compile/internal/ppc64}/reg.go |  4 +--
 src/cmd/{8g => compile/internal/x86}/cgen.go  |  4 +--
 .../{8g => compile/internal/x86}/cgen64.go    |  4 +--
 .../{8g => compile/internal/x86}/galign.go    |  6 ++--
 src/cmd/{8g => compile/internal/x86}/ggen.go  |  4 +--
 src/cmd/{8g => compile/internal/x86}/gsubr.go |  6 ++--
 src/cmd/{8g => compile/internal/x86}/peep.go  |  4 +--
 src/cmd/{8g => compile/internal/x86}/prog.go  |  4 +--
 src/cmd/{8g => compile/internal/x86}/reg.go   |  4 +--
 src/cmd/compile/main.go                       | 34 +++++++++++++++++++
 src/cmd/{6l => link/internal/amd64}/asm.go    |  4 +--
 src/cmd/{6l => link/internal/amd64}/l.go      |  2 +-
 src/cmd/{6l => link/internal/amd64}/obj.go    |  6 ++--
 src/cmd/link/internal/amd64/z.go              |  1 +
 src/cmd/{5l => link/internal/arm}/asm.go      |  4 +--
 src/cmd/{5l => link/internal/arm}/l.go        |  2 +-
 src/cmd/{5l => link/internal/arm}/obj.go      |  6 ++--
 src/cmd/{7l => link/internal/arm64}/asm.go    |  4 +--
 src/cmd/{7l => link/internal/arm64}/l.go      |  2 +-
 src/cmd/{7l => link/internal/arm64}/obj.go    |  6 ++--
 src/cmd/{ => link}/internal/ld/ar.go          |  0
 src/cmd/{ => link}/internal/ld/arch.go        |  0
 src/cmd/{ => link}/internal/ld/data.go        |  0
 src/cmd/{ => link}/internal/ld/decodesym.go   |  0
 src/cmd/{ => link}/internal/ld/dwarf.go       |  0
 src/cmd/{ => link}/internal/ld/dwarf_defs.go  |  0
 src/cmd/{ => link}/internal/ld/elf.go         |  0
 src/cmd/{ => link}/internal/ld/go.go          |  0
 src/cmd/{ => link}/internal/ld/ld.go          |  0
 src/cmd/{ => link}/internal/ld/ldelf.go       |  0
 src/cmd/{ => link}/internal/ld/ldmacho.go     |  0
 src/cmd/{ => link}/internal/ld/ldpe.go        |  0
 src/cmd/{ => link}/internal/ld/lib.go         |  0
 src/cmd/{ => link}/internal/ld/link.go        |  0
 src/cmd/{ => link}/internal/ld/macho.go       |  0
 .../internal/ld/macho_combine_dwarf.go        |  0
 src/cmd/{ => link}/internal/ld/objfile.go     |  0
 src/cmd/{ => link}/internal/ld/pcln.go        |  0
 src/cmd/{ => link}/internal/ld/pe.go          |  0
 src/cmd/{ => link}/internal/ld/pobj.go        |  0
 src/cmd/{ => link}/internal/ld/sym.go         |  0
 src/cmd/{ => link}/internal/ld/symtab.go      |  0
 src/cmd/{ => link}/internal/ld/textflag.go    |  0
 src/cmd/{ => link}/internal/ld/util.go        |  0
 src/cmd/{9l => link/internal/ppc64}/asm.go    |  4 +--
 src/cmd/{9l => link/internal/ppc64}/l.go      |  2 +-
 src/cmd/{9l => link/internal/ppc64}/obj.go    |  6 ++--
 src/cmd/{8l => link/internal/x86}/asm.go      |  4 +--
 src/cmd/{8l => link/internal/x86}/l.go        |  2 +-
 src/cmd/{8l => link/internal/x86}/obj.go      |  6 ++--
 src/cmd/link/main.go                          | 34 +++++++++++++++++++
 156 files changed, 186 insertions(+), 118 deletions(-)
 delete mode 100644 src/cmd/6l/z.go
 rename src/cmd/{6g => compile/internal/amd64}/cgen.go (98%)
 rename src/cmd/{6g => compile/internal/amd64}/galign.go (98%)
 rename src/cmd/{6g => compile/internal/amd64}/ggen.go (99%)
 rename src/cmd/{6g => compile/internal/amd64}/gsubr.go (99%)
 rename src/cmd/{6g => compile/internal/amd64}/peep.go (99%)
 rename src/cmd/{6g => compile/internal/amd64}/prog.go (99%)
 rename src/cmd/{6g => compile/internal/amd64}/reg.go (98%)
 rename src/cmd/{5g => compile/internal/arm}/cgen.go (99%)
 rename src/cmd/{5g => compile/internal/arm}/cgen64.go (99%)
 rename src/cmd/{5g => compile/internal/arm}/galign.go (97%)
 rename src/cmd/{5g => compile/internal/arm}/ggen.go (99%)
 rename src/cmd/{5g => compile/internal/arm}/gsubr.go (99%)
 rename src/cmd/{5g => compile/internal/arm}/peep.go (99%)
 rename src/cmd/{5g => compile/internal/arm}/prog.go (99%)
 rename src/cmd/{5g => compile/internal/arm}/reg.go (98%)
 rename src/cmd/{7g => compile/internal/arm64}/cgen.go (98%)
 rename src/cmd/{7g => compile/internal/arm64}/galign.go (97%)
 rename src/cmd/{7g => compile/internal/arm64}/ggen.go (99%)
 rename src/cmd/{7g => compile/internal/arm64}/gsubr.go (99%)
 rename src/cmd/{7g => compile/internal/arm64}/peep.go (99%)
 rename src/cmd/{7g => compile/internal/arm64}/prog.go (99%)
 rename src/cmd/{7g => compile/internal/arm64}/reg.go (98%)
 rename src/cmd/{internal/gc => compile/internal}/big/accuracy_string.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/arith.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/arith_decl.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/arith_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/bits_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/calibrate_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/decimal.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/decimal_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/example_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/float.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/float_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/floatconv.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/floatconv_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/floatexample_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/ftoa.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/gcd_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/hilbert_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/int.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/int_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/intconv.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/intconv_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/nat.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/nat_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/natconv.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/natconv_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/rat.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/rat_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/ratconv.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/ratconv_test.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/roundingmode_string.go (100%)
 rename src/cmd/{internal/gc => compile/internal}/big/vendor.bash (100%)
 rename src/cmd/{ => compile}/internal/gc/align.go (100%)
 rename src/cmd/{ => compile}/internal/gc/builtin.go (100%)
 rename src/cmd/{ => compile}/internal/gc/builtin/runtime.go (100%)
 rename src/cmd/{ => compile}/internal/gc/builtin/unsafe.go (100%)
 rename src/cmd/{ => compile}/internal/gc/bv.go (100%)
 rename src/cmd/{ => compile}/internal/gc/cgen.go (100%)
 rename src/cmd/{ => compile}/internal/gc/closure.go (100%)
 rename src/cmd/{ => compile}/internal/gc/const.go (99%)
 rename src/cmd/{ => compile}/internal/gc/cplx.go (100%)
 rename src/cmd/{ => compile}/internal/gc/dcl.go (100%)
 rename src/cmd/{ => compile}/internal/gc/esc.go (100%)
 rename src/cmd/{ => compile}/internal/gc/export.go (100%)
 rename src/cmd/{ => compile}/internal/gc/fmt.go (100%)
 rename src/cmd/{ => compile}/internal/gc/gen.go (100%)
 rename src/cmd/{ => compile}/internal/gc/go.go (99%)
 rename src/cmd/{ => compile}/internal/gc/go.y (100%)
 rename src/cmd/{ => compile}/internal/gc/gsubr.go (100%)
 rename src/cmd/{ => compile}/internal/gc/init.go (100%)
 rename src/cmd/{ => compile}/internal/gc/inl.go (100%)
 rename src/cmd/{ => compile}/internal/gc/lex.go (100%)
 rename src/cmd/{ => compile}/internal/gc/mkbuiltin.go (100%)
 rename src/cmd/{ => compile}/internal/gc/mparith2.go (99%)
 rename src/cmd/{ => compile}/internal/gc/mparith3.go (99%)
 rename src/cmd/{ => compile}/internal/gc/obj.go (100%)
 rename src/cmd/{ => compile}/internal/gc/opnames.go (100%)
 rename src/cmd/{ => compile}/internal/gc/order.go (100%)
 rename src/cmd/{ => compile}/internal/gc/pgen.go (100%)
 rename src/cmd/{ => compile}/internal/gc/plive.go (100%)
 rename src/cmd/{ => compile}/internal/gc/popt.go (100%)
 rename src/cmd/{ => compile}/internal/gc/racewalk.go (100%)
 rename src/cmd/{ => compile}/internal/gc/range.go (100%)
 rename src/cmd/{ => compile}/internal/gc/reflect.go (100%)
 rename src/cmd/{ => compile}/internal/gc/reg.go (100%)
 rename src/cmd/{ => compile}/internal/gc/select.go (100%)
 rename src/cmd/{ => compile}/internal/gc/sinit.go (100%)
 rename src/cmd/{ => compile}/internal/gc/subr.go (100%)
 rename src/cmd/{ => compile}/internal/gc/swt.go (100%)
 rename src/cmd/{ => compile}/internal/gc/syntax.go (100%)
 rename src/cmd/{ => compile}/internal/gc/typecheck.go (100%)
 rename src/cmd/{ => compile}/internal/gc/unsafe.go (100%)
 rename src/cmd/{ => compile}/internal/gc/util.go (100%)
 rename src/cmd/{ => compile}/internal/gc/walk.go (100%)
 rename src/cmd/{ => compile}/internal/gc/y.go (100%)
 rename src/cmd/{ => compile}/internal/gc/y.output (100%)
 rename src/cmd/{9g => compile/internal/ppc64}/cgen.go (98%)
 rename src/cmd/{9g => compile/internal/ppc64}/galign.go (97%)
 rename src/cmd/{9g => compile/internal/ppc64}/ggen.go (99%)
 rename src/cmd/{9g => compile/internal/ppc64}/gsubr.go (99%)
 rename src/cmd/{9g => compile/internal/ppc64}/opt.go (96%)
 rename src/cmd/{9g => compile/internal/ppc64}/peep.go (99%)
 rename src/cmd/{9g => compile/internal/ppc64}/prog.go (99%)
 rename src/cmd/{9g => compile/internal/ppc64}/reg.go (98%)
 rename src/cmd/{8g => compile/internal/x86}/cgen.go (98%)
 rename src/cmd/{8g => compile/internal/x86}/cgen64.go (99%)
 rename src/cmd/{8g => compile/internal/x86}/galign.go (98%)
 rename src/cmd/{8g => compile/internal/x86}/ggen.go (99%)
 rename src/cmd/{8g => compile/internal/x86}/gsubr.go (99%)
 rename src/cmd/{8g => compile/internal/x86}/peep.go (99%)
 rename src/cmd/{8g => compile/internal/x86}/prog.go (99%)
 rename src/cmd/{8g => compile/internal/x86}/reg.go (98%)
 create mode 100644 src/cmd/compile/main.go
 rename src/cmd/{6l => link/internal/amd64}/asm.go (99%)
 rename src/cmd/{6l => link/internal/amd64}/l.go (99%)
 rename src/cmd/{6l => link/internal/amd64}/obj.go (99%)
 create mode 100644 src/cmd/link/internal/amd64/z.go
 rename src/cmd/{5l => link/internal/arm}/asm.go (99%)
 rename src/cmd/{5l => link/internal/arm}/l.go (99%)
 rename src/cmd/{5l => link/internal/arm}/obj.go (98%)
 rename src/cmd/{7l => link/internal/arm64}/asm.go (99%)
 rename src/cmd/{7l => link/internal/arm64}/l.go (99%)
 rename src/cmd/{7l => link/internal/arm64}/obj.go (98%)
 rename src/cmd/{ => link}/internal/ld/ar.go (100%)
 rename src/cmd/{ => link}/internal/ld/arch.go (100%)
 rename src/cmd/{ => link}/internal/ld/data.go (100%)
 rename src/cmd/{ => link}/internal/ld/decodesym.go (100%)
 rename src/cmd/{ => link}/internal/ld/dwarf.go (100%)
 rename src/cmd/{ => link}/internal/ld/dwarf_defs.go (100%)
 rename src/cmd/{ => link}/internal/ld/elf.go (100%)
 rename src/cmd/{ => link}/internal/ld/go.go (100%)
 rename src/cmd/{ => link}/internal/ld/ld.go (100%)
 rename src/cmd/{ => link}/internal/ld/ldelf.go (100%)
 rename src/cmd/{ => link}/internal/ld/ldmacho.go (100%)
 rename src/cmd/{ => link}/internal/ld/ldpe.go (100%)
 rename src/cmd/{ => link}/internal/ld/lib.go (100%)
 rename src/cmd/{ => link}/internal/ld/link.go (100%)
 rename src/cmd/{ => link}/internal/ld/macho.go (100%)
 rename src/cmd/{ => link}/internal/ld/macho_combine_dwarf.go (100%)
 rename src/cmd/{ => link}/internal/ld/objfile.go (100%)
 rename src/cmd/{ => link}/internal/ld/pcln.go (100%)
 rename src/cmd/{ => link}/internal/ld/pe.go (100%)
 rename src/cmd/{ => link}/internal/ld/pobj.go (100%)
 rename src/cmd/{ => link}/internal/ld/sym.go (100%)
 rename src/cmd/{ => link}/internal/ld/symtab.go (100%)
 rename src/cmd/{ => link}/internal/ld/textflag.go (100%)
 rename src/cmd/{ => link}/internal/ld/util.go (100%)
 rename src/cmd/{9l => link/internal/ppc64}/asm.go (99%)
 rename src/cmd/{9l => link/internal/ppc64}/l.go (99%)
 rename src/cmd/{9l => link/internal/ppc64}/obj.go (98%)
 rename src/cmd/{8l => link/internal/x86}/asm.go (99%)
 rename src/cmd/{8l => link/internal/x86}/l.go (99%)
 rename src/cmd/{8l => link/internal/x86}/obj.go (99%)
 create mode 100644 src/cmd/link/main.go

diff --git a/src/cmd/6l/z.go b/src/cmd/6l/z.go
deleted file mode 100644
index 06ab7d0f9a..0000000000
--- a/src/cmd/6l/z.go
+++ /dev/null
@@ -1 +0,0 @@
-package main
diff --git a/src/cmd/6g/cgen.go b/src/cmd/compile/internal/amd64/cgen.go
similarity index 98%
rename from src/cmd/6g/cgen.go
rename to src/cmd/compile/internal/amd64/cgen.go
index 23e2d1b57f..71f8f88322 100644
--- a/src/cmd/6g/cgen.go
+++ b/src/cmd/compile/internal/amd64/cgen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/6g/galign.go b/src/cmd/compile/internal/amd64/galign.go
similarity index 98%
rename from src/cmd/6g/galign.go
rename to src/cmd/compile/internal/amd64/galign.go
index 17d78f399d..79bf94a075 100644
--- a/src/cmd/6g/galign.go
+++ b/src/cmd/compile/internal/amd64/galign.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
@@ -65,7 +65,7 @@ func betypeinit() {
 	}
 }
 
-func main() {
+func Main() {
 	if obj.Getgoos() == "nacl" {
 		resvd = append(resvd, x86.REG_BP, x86.REG_R15)
 	} else if obj.Framepointer_enabled != 0 {
diff --git a/src/cmd/6g/ggen.go b/src/cmd/compile/internal/amd64/ggen.go
similarity index 99%
rename from src/cmd/6g/ggen.go
rename to src/cmd/compile/internal/amd64/ggen.go
index e0e1b8a4df..6425633818 100644
--- a/src/cmd/6g/ggen.go
+++ b/src/cmd/compile/internal/amd64/ggen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/6g/gsubr.go b/src/cmd/compile/internal/amd64/gsubr.go
similarity index 99%
rename from src/cmd/6g/gsubr.go
rename to src/cmd/compile/internal/amd64/gsubr.go
index 9b9141468e..a8e4170bee 100644
--- a/src/cmd/6g/gsubr.go
+++ b/src/cmd/compile/internal/amd64/gsubr.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/gc"
-	"cmd/internal/gc/big"
+	"cmd/compile/internal/big"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 	"fmt"
diff --git a/src/cmd/6g/peep.go b/src/cmd/compile/internal/amd64/peep.go
similarity index 99%
rename from src/cmd/6g/peep.go
rename to src/cmd/compile/internal/amd64/peep.go
index cd07199ed1..19db68e944 100644
--- a/src/cmd/6g/peep.go
+++ b/src/cmd/compile/internal/amd64/peep.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 	"fmt"
diff --git a/src/cmd/6g/prog.go b/src/cmd/compile/internal/amd64/prog.go
similarity index 99%
rename from src/cmd/6g/prog.go
rename to src/cmd/compile/internal/amd64/prog.go
index 5f604742c3..00918c8691 100644
--- a/src/cmd/6g/prog.go
+++ b/src/cmd/compile/internal/amd64/prog.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/6g/reg.go b/src/cmd/compile/internal/amd64/reg.go
similarity index 98%
rename from src/cmd/6g/reg.go
rename to src/cmd/compile/internal/amd64/reg.go
index cab07b5b4e..7d4f40641d 100644
--- a/src/cmd/6g/reg.go
+++ b/src/cmd/compile/internal/amd64/reg.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/5g/cgen.go b/src/cmd/compile/internal/arm/cgen.go
similarity index 99%
rename from src/cmd/5g/cgen.go
rename to src/cmd/compile/internal/arm/cgen.go
index c0d7651584..8ea6c5f3f2 100644
--- a/src/cmd/5g/cgen.go
+++ b/src/cmd/compile/internal/arm/cgen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 )
diff --git a/src/cmd/5g/cgen64.go b/src/cmd/compile/internal/arm/cgen64.go
similarity index 99%
rename from src/cmd/5g/cgen64.go
rename to src/cmd/compile/internal/arm/cgen64.go
index c55e000adc..6c88b76e20 100644
--- a/src/cmd/5g/cgen64.go
+++ b/src/cmd/compile/internal/arm/cgen64.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 )
diff --git a/src/cmd/5g/galign.go b/src/cmd/compile/internal/arm/galign.go
similarity index 97%
rename from src/cmd/5g/galign.go
rename to src/cmd/compile/internal/arm/galign.go
index 55782e1dae..60a39d3fe4 100644
--- a/src/cmd/5g/galign.go
+++ b/src/cmd/compile/internal/arm/galign.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 )
@@ -37,7 +37,7 @@ func betypeinit() {
 	gc.Widthreg = 4
 }
 
-func main() {
+func Main() {
 	gc.Thearch.Thechar = thechar
 	gc.Thearch.Thestring = thestring
 	gc.Thearch.Thelinkarch = thelinkarch
diff --git a/src/cmd/5g/ggen.go b/src/cmd/compile/internal/arm/ggen.go
similarity index 99%
rename from src/cmd/5g/ggen.go
rename to src/cmd/compile/internal/arm/ggen.go
index 2ab5d521bb..6633351032 100644
--- a/src/cmd/5g/ggen.go
+++ b/src/cmd/compile/internal/arm/ggen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 )
diff --git a/src/cmd/5g/gsubr.go b/src/cmd/compile/internal/arm/gsubr.go
similarity index 99%
rename from src/cmd/5g/gsubr.go
rename to src/cmd/compile/internal/arm/gsubr.go
index 2a23580b58..5263f15ac2 100644
--- a/src/cmd/5g/gsubr.go
+++ b/src/cmd/compile/internal/arm/gsubr.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 	"fmt"
diff --git a/src/cmd/5g/peep.go b/src/cmd/compile/internal/arm/peep.go
similarity index 99%
rename from src/cmd/5g/peep.go
rename to src/cmd/compile/internal/arm/peep.go
index b76719d74e..66eba417c0 100644
--- a/src/cmd/5g/peep.go
+++ b/src/cmd/compile/internal/arm/peep.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 	"fmt"
diff --git a/src/cmd/5g/prog.go b/src/cmd/compile/internal/arm/prog.go
similarity index 99%
rename from src/cmd/5g/prog.go
rename to src/cmd/compile/internal/arm/prog.go
index c472cdf042..cdf9d29192 100644
--- a/src/cmd/5g/prog.go
+++ b/src/cmd/compile/internal/arm/prog.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm"
 )
diff --git a/src/cmd/5g/reg.go b/src/cmd/compile/internal/arm/reg.go
similarity index 98%
rename from src/cmd/5g/reg.go
rename to src/cmd/compile/internal/arm/reg.go
index 2afdf12416..b72ccc9815 100644
--- a/src/cmd/5g/reg.go
+++ b/src/cmd/compile/internal/arm/reg.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm
 
 import "cmd/internal/obj/arm"
-import "cmd/internal/gc"
+import "cmd/compile/internal/gc"
 
 const (
 	NREGVAR = 32
diff --git a/src/cmd/7g/cgen.go b/src/cmd/compile/internal/arm64/cgen.go
similarity index 98%
rename from src/cmd/7g/cgen.go
rename to src/cmd/compile/internal/arm64/cgen.go
index 6f268b4185..30326d73e2 100644
--- a/src/cmd/7g/cgen.go
+++ b/src/cmd/compile/internal/arm64/cgen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm64"
 )
diff --git a/src/cmd/7g/galign.go b/src/cmd/compile/internal/arm64/galign.go
similarity index 97%
rename from src/cmd/7g/galign.go
rename to src/cmd/compile/internal/arm64/galign.go
index 8a6184efd0..38def8f5a4 100644
--- a/src/cmd/7g/galign.go
+++ b/src/cmd/compile/internal/arm64/galign.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm64"
 )
@@ -37,7 +37,7 @@ func betypeinit() {
 	gc.Widthreg = 8
 }
 
-func main() {
+func Main() {
 	gc.Thearch.Thechar = thechar
 	gc.Thearch.Thestring = thestring
 	gc.Thearch.Thelinkarch = thelinkarch
diff --git a/src/cmd/7g/ggen.go b/src/cmd/compile/internal/arm64/ggen.go
similarity index 99%
rename from src/cmd/7g/ggen.go
rename to src/cmd/compile/internal/arm64/ggen.go
index ec2eb09e38..851ca4e30f 100644
--- a/src/cmd/7g/ggen.go
+++ b/src/cmd/compile/internal/arm64/ggen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm64"
 	"fmt"
diff --git a/src/cmd/7g/gsubr.go b/src/cmd/compile/internal/arm64/gsubr.go
similarity index 99%
rename from src/cmd/7g/gsubr.go
rename to src/cmd/compile/internal/arm64/gsubr.go
index 0f617079ad..0a14654d83 100644
--- a/src/cmd/7g/gsubr.go
+++ b/src/cmd/compile/internal/arm64/gsubr.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm64"
 	"fmt"
diff --git a/src/cmd/7g/peep.go b/src/cmd/compile/internal/arm64/peep.go
similarity index 99%
rename from src/cmd/7g/peep.go
rename to src/cmd/compile/internal/arm64/peep.go
index 49bc69b132..1c3b2891aa 100644
--- a/src/cmd/7g/peep.go
+++ b/src/cmd/compile/internal/arm64/peep.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm64"
 	"fmt"
diff --git a/src/cmd/7g/prog.go b/src/cmd/compile/internal/arm64/prog.go
similarity index 99%
rename from src/cmd/7g/prog.go
rename to src/cmd/compile/internal/arm64/prog.go
index 023f302e14..1106e788a5 100644
--- a/src/cmd/7g/prog.go
+++ b/src/cmd/compile/internal/arm64/prog.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/arm64"
 )
diff --git a/src/cmd/7g/reg.go b/src/cmd/compile/internal/arm64/reg.go
similarity index 98%
rename from src/cmd/7g/reg.go
rename to src/cmd/compile/internal/arm64/reg.go
index 0e5ac73499..7bc756b7bf 100644
--- a/src/cmd/7g/reg.go
+++ b/src/cmd/compile/internal/arm64/reg.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj/arm64"
 )
 
diff --git a/src/cmd/internal/gc/big/accuracy_string.go b/src/cmd/compile/internal/big/accuracy_string.go
similarity index 100%
rename from src/cmd/internal/gc/big/accuracy_string.go
rename to src/cmd/compile/internal/big/accuracy_string.go
diff --git a/src/cmd/internal/gc/big/arith.go b/src/cmd/compile/internal/big/arith.go
similarity index 100%
rename from src/cmd/internal/gc/big/arith.go
rename to src/cmd/compile/internal/big/arith.go
diff --git a/src/cmd/internal/gc/big/arith_decl.go b/src/cmd/compile/internal/big/arith_decl.go
similarity index 100%
rename from src/cmd/internal/gc/big/arith_decl.go
rename to src/cmd/compile/internal/big/arith_decl.go
diff --git a/src/cmd/internal/gc/big/arith_test.go b/src/cmd/compile/internal/big/arith_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/arith_test.go
rename to src/cmd/compile/internal/big/arith_test.go
diff --git a/src/cmd/internal/gc/big/bits_test.go b/src/cmd/compile/internal/big/bits_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/bits_test.go
rename to src/cmd/compile/internal/big/bits_test.go
diff --git a/src/cmd/internal/gc/big/calibrate_test.go b/src/cmd/compile/internal/big/calibrate_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/calibrate_test.go
rename to src/cmd/compile/internal/big/calibrate_test.go
diff --git a/src/cmd/internal/gc/big/decimal.go b/src/cmd/compile/internal/big/decimal.go
similarity index 100%
rename from src/cmd/internal/gc/big/decimal.go
rename to src/cmd/compile/internal/big/decimal.go
diff --git a/src/cmd/internal/gc/big/decimal_test.go b/src/cmd/compile/internal/big/decimal_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/decimal_test.go
rename to src/cmd/compile/internal/big/decimal_test.go
diff --git a/src/cmd/internal/gc/big/example_test.go b/src/cmd/compile/internal/big/example_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/example_test.go
rename to src/cmd/compile/internal/big/example_test.go
diff --git a/src/cmd/internal/gc/big/float.go b/src/cmd/compile/internal/big/float.go
similarity index 100%
rename from src/cmd/internal/gc/big/float.go
rename to src/cmd/compile/internal/big/float.go
diff --git a/src/cmd/internal/gc/big/float_test.go b/src/cmd/compile/internal/big/float_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/float_test.go
rename to src/cmd/compile/internal/big/float_test.go
diff --git a/src/cmd/internal/gc/big/floatconv.go b/src/cmd/compile/internal/big/floatconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/floatconv.go
rename to src/cmd/compile/internal/big/floatconv.go
diff --git a/src/cmd/internal/gc/big/floatconv_test.go b/src/cmd/compile/internal/big/floatconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/floatconv_test.go
rename to src/cmd/compile/internal/big/floatconv_test.go
diff --git a/src/cmd/internal/gc/big/floatexample_test.go b/src/cmd/compile/internal/big/floatexample_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/floatexample_test.go
rename to src/cmd/compile/internal/big/floatexample_test.go
diff --git a/src/cmd/internal/gc/big/ftoa.go b/src/cmd/compile/internal/big/ftoa.go
similarity index 100%
rename from src/cmd/internal/gc/big/ftoa.go
rename to src/cmd/compile/internal/big/ftoa.go
diff --git a/src/cmd/internal/gc/big/gcd_test.go b/src/cmd/compile/internal/big/gcd_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/gcd_test.go
rename to src/cmd/compile/internal/big/gcd_test.go
diff --git a/src/cmd/internal/gc/big/hilbert_test.go b/src/cmd/compile/internal/big/hilbert_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/hilbert_test.go
rename to src/cmd/compile/internal/big/hilbert_test.go
diff --git a/src/cmd/internal/gc/big/int.go b/src/cmd/compile/internal/big/int.go
similarity index 100%
rename from src/cmd/internal/gc/big/int.go
rename to src/cmd/compile/internal/big/int.go
diff --git a/src/cmd/internal/gc/big/int_test.go b/src/cmd/compile/internal/big/int_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/int_test.go
rename to src/cmd/compile/internal/big/int_test.go
diff --git a/src/cmd/internal/gc/big/intconv.go b/src/cmd/compile/internal/big/intconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/intconv.go
rename to src/cmd/compile/internal/big/intconv.go
diff --git a/src/cmd/internal/gc/big/intconv_test.go b/src/cmd/compile/internal/big/intconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/intconv_test.go
rename to src/cmd/compile/internal/big/intconv_test.go
diff --git a/src/cmd/internal/gc/big/nat.go b/src/cmd/compile/internal/big/nat.go
similarity index 100%
rename from src/cmd/internal/gc/big/nat.go
rename to src/cmd/compile/internal/big/nat.go
diff --git a/src/cmd/internal/gc/big/nat_test.go b/src/cmd/compile/internal/big/nat_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/nat_test.go
rename to src/cmd/compile/internal/big/nat_test.go
diff --git a/src/cmd/internal/gc/big/natconv.go b/src/cmd/compile/internal/big/natconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/natconv.go
rename to src/cmd/compile/internal/big/natconv.go
diff --git a/src/cmd/internal/gc/big/natconv_test.go b/src/cmd/compile/internal/big/natconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/natconv_test.go
rename to src/cmd/compile/internal/big/natconv_test.go
diff --git a/src/cmd/internal/gc/big/rat.go b/src/cmd/compile/internal/big/rat.go
similarity index 100%
rename from src/cmd/internal/gc/big/rat.go
rename to src/cmd/compile/internal/big/rat.go
diff --git a/src/cmd/internal/gc/big/rat_test.go b/src/cmd/compile/internal/big/rat_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/rat_test.go
rename to src/cmd/compile/internal/big/rat_test.go
diff --git a/src/cmd/internal/gc/big/ratconv.go b/src/cmd/compile/internal/big/ratconv.go
similarity index 100%
rename from src/cmd/internal/gc/big/ratconv.go
rename to src/cmd/compile/internal/big/ratconv.go
diff --git a/src/cmd/internal/gc/big/ratconv_test.go b/src/cmd/compile/internal/big/ratconv_test.go
similarity index 100%
rename from src/cmd/internal/gc/big/ratconv_test.go
rename to src/cmd/compile/internal/big/ratconv_test.go
diff --git a/src/cmd/internal/gc/big/roundingmode_string.go b/src/cmd/compile/internal/big/roundingmode_string.go
similarity index 100%
rename from src/cmd/internal/gc/big/roundingmode_string.go
rename to src/cmd/compile/internal/big/roundingmode_string.go
diff --git a/src/cmd/internal/gc/big/vendor.bash b/src/cmd/compile/internal/big/vendor.bash
similarity index 100%
rename from src/cmd/internal/gc/big/vendor.bash
rename to src/cmd/compile/internal/big/vendor.bash
diff --git a/src/cmd/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go
similarity index 100%
rename from src/cmd/internal/gc/align.go
rename to src/cmd/compile/internal/gc/align.go
diff --git a/src/cmd/internal/gc/builtin.go b/src/cmd/compile/internal/gc/builtin.go
similarity index 100%
rename from src/cmd/internal/gc/builtin.go
rename to src/cmd/compile/internal/gc/builtin.go
diff --git a/src/cmd/internal/gc/builtin/runtime.go b/src/cmd/compile/internal/gc/builtin/runtime.go
similarity index 100%
rename from src/cmd/internal/gc/builtin/runtime.go
rename to src/cmd/compile/internal/gc/builtin/runtime.go
diff --git a/src/cmd/internal/gc/builtin/unsafe.go b/src/cmd/compile/internal/gc/builtin/unsafe.go
similarity index 100%
rename from src/cmd/internal/gc/builtin/unsafe.go
rename to src/cmd/compile/internal/gc/builtin/unsafe.go
diff --git a/src/cmd/internal/gc/bv.go b/src/cmd/compile/internal/gc/bv.go
similarity index 100%
rename from src/cmd/internal/gc/bv.go
rename to src/cmd/compile/internal/gc/bv.go
diff --git a/src/cmd/internal/gc/cgen.go b/src/cmd/compile/internal/gc/cgen.go
similarity index 100%
rename from src/cmd/internal/gc/cgen.go
rename to src/cmd/compile/internal/gc/cgen.go
diff --git a/src/cmd/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
similarity index 100%
rename from src/cmd/internal/gc/closure.go
rename to src/cmd/compile/internal/gc/closure.go
diff --git a/src/cmd/internal/gc/const.go b/src/cmd/compile/internal/gc/const.go
similarity index 99%
rename from src/cmd/internal/gc/const.go
rename to src/cmd/compile/internal/gc/const.go
index 986e2c3337..b3605ab206 100644
--- a/src/cmd/internal/gc/const.go
+++ b/src/cmd/compile/internal/gc/const.go
@@ -5,7 +5,7 @@
 package gc
 
 import (
-	"cmd/internal/gc/big"
+	"cmd/compile/internal/big"
 	"cmd/internal/obj"
 	"strings"
 )
diff --git a/src/cmd/internal/gc/cplx.go b/src/cmd/compile/internal/gc/cplx.go
similarity index 100%
rename from src/cmd/internal/gc/cplx.go
rename to src/cmd/compile/internal/gc/cplx.go
diff --git a/src/cmd/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go
similarity index 100%
rename from src/cmd/internal/gc/dcl.go
rename to src/cmd/compile/internal/gc/dcl.go
diff --git a/src/cmd/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
similarity index 100%
rename from src/cmd/internal/gc/esc.go
rename to src/cmd/compile/internal/gc/esc.go
diff --git a/src/cmd/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go
similarity index 100%
rename from src/cmd/internal/gc/export.go
rename to src/cmd/compile/internal/gc/export.go
diff --git a/src/cmd/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go
similarity index 100%
rename from src/cmd/internal/gc/fmt.go
rename to src/cmd/compile/internal/gc/fmt.go
diff --git a/src/cmd/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go
similarity index 100%
rename from src/cmd/internal/gc/gen.go
rename to src/cmd/compile/internal/gc/gen.go
diff --git a/src/cmd/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go
similarity index 99%
rename from src/cmd/internal/gc/go.go
rename to src/cmd/compile/internal/gc/go.go
index 6a3379b896..dc33f62ba4 100644
--- a/src/cmd/internal/gc/go.go
+++ b/src/cmd/compile/internal/gc/go.go
@@ -6,7 +6,7 @@ package gc
 
 import (
 	"bytes"
-	"cmd/internal/gc/big"
+	"cmd/compile/internal/big"
 	"cmd/internal/obj"
 )
 
diff --git a/src/cmd/internal/gc/go.y b/src/cmd/compile/internal/gc/go.y
similarity index 100%
rename from src/cmd/internal/gc/go.y
rename to src/cmd/compile/internal/gc/go.y
diff --git a/src/cmd/internal/gc/gsubr.go b/src/cmd/compile/internal/gc/gsubr.go
similarity index 100%
rename from src/cmd/internal/gc/gsubr.go
rename to src/cmd/compile/internal/gc/gsubr.go
diff --git a/src/cmd/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go
similarity index 100%
rename from src/cmd/internal/gc/init.go
rename to src/cmd/compile/internal/gc/init.go
diff --git a/src/cmd/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go
similarity index 100%
rename from src/cmd/internal/gc/inl.go
rename to src/cmd/compile/internal/gc/inl.go
diff --git a/src/cmd/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
similarity index 100%
rename from src/cmd/internal/gc/lex.go
rename to src/cmd/compile/internal/gc/lex.go
diff --git a/src/cmd/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/gc/mkbuiltin.go
similarity index 100%
rename from src/cmd/internal/gc/mkbuiltin.go
rename to src/cmd/compile/internal/gc/mkbuiltin.go
diff --git a/src/cmd/internal/gc/mparith2.go b/src/cmd/compile/internal/gc/mparith2.go
similarity index 99%
rename from src/cmd/internal/gc/mparith2.go
rename to src/cmd/compile/internal/gc/mparith2.go
index 2456dbf60a..2c7e5176ac 100644
--- a/src/cmd/internal/gc/mparith2.go
+++ b/src/cmd/compile/internal/gc/mparith2.go
@@ -5,7 +5,7 @@
 package gc
 
 import (
-	"cmd/internal/gc/big"
+	"cmd/compile/internal/big"
 	"cmd/internal/obj"
 	"fmt"
 )
diff --git a/src/cmd/internal/gc/mparith3.go b/src/cmd/compile/internal/gc/mparith3.go
similarity index 99%
rename from src/cmd/internal/gc/mparith3.go
rename to src/cmd/compile/internal/gc/mparith3.go
index 2700b64a89..181e91c87d 100644
--- a/src/cmd/internal/gc/mparith3.go
+++ b/src/cmd/compile/internal/gc/mparith3.go
@@ -5,7 +5,7 @@
 package gc
 
 import (
-	"cmd/internal/gc/big"
+	"cmd/compile/internal/big"
 	"cmd/internal/obj"
 	"fmt"
 	"math"
diff --git a/src/cmd/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
similarity index 100%
rename from src/cmd/internal/gc/obj.go
rename to src/cmd/compile/internal/gc/obj.go
diff --git a/src/cmd/internal/gc/opnames.go b/src/cmd/compile/internal/gc/opnames.go
similarity index 100%
rename from src/cmd/internal/gc/opnames.go
rename to src/cmd/compile/internal/gc/opnames.go
diff --git a/src/cmd/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go
similarity index 100%
rename from src/cmd/internal/gc/order.go
rename to src/cmd/compile/internal/gc/order.go
diff --git a/src/cmd/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
similarity index 100%
rename from src/cmd/internal/gc/pgen.go
rename to src/cmd/compile/internal/gc/pgen.go
diff --git a/src/cmd/internal/gc/plive.go b/src/cmd/compile/internal/gc/plive.go
similarity index 100%
rename from src/cmd/internal/gc/plive.go
rename to src/cmd/compile/internal/gc/plive.go
diff --git a/src/cmd/internal/gc/popt.go b/src/cmd/compile/internal/gc/popt.go
similarity index 100%
rename from src/cmd/internal/gc/popt.go
rename to src/cmd/compile/internal/gc/popt.go
diff --git a/src/cmd/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
similarity index 100%
rename from src/cmd/internal/gc/racewalk.go
rename to src/cmd/compile/internal/gc/racewalk.go
diff --git a/src/cmd/internal/gc/range.go b/src/cmd/compile/internal/gc/range.go
similarity index 100%
rename from src/cmd/internal/gc/range.go
rename to src/cmd/compile/internal/gc/range.go
diff --git a/src/cmd/internal/gc/reflect.go b/src/cmd/compile/internal/gc/reflect.go
similarity index 100%
rename from src/cmd/internal/gc/reflect.go
rename to src/cmd/compile/internal/gc/reflect.go
diff --git a/src/cmd/internal/gc/reg.go b/src/cmd/compile/internal/gc/reg.go
similarity index 100%
rename from src/cmd/internal/gc/reg.go
rename to src/cmd/compile/internal/gc/reg.go
diff --git a/src/cmd/internal/gc/select.go b/src/cmd/compile/internal/gc/select.go
similarity index 100%
rename from src/cmd/internal/gc/select.go
rename to src/cmd/compile/internal/gc/select.go
diff --git a/src/cmd/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go
similarity index 100%
rename from src/cmd/internal/gc/sinit.go
rename to src/cmd/compile/internal/gc/sinit.go
diff --git a/src/cmd/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go
similarity index 100%
rename from src/cmd/internal/gc/subr.go
rename to src/cmd/compile/internal/gc/subr.go
diff --git a/src/cmd/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go
similarity index 100%
rename from src/cmd/internal/gc/swt.go
rename to src/cmd/compile/internal/gc/swt.go
diff --git a/src/cmd/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go
similarity index 100%
rename from src/cmd/internal/gc/syntax.go
rename to src/cmd/compile/internal/gc/syntax.go
diff --git a/src/cmd/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go
similarity index 100%
rename from src/cmd/internal/gc/typecheck.go
rename to src/cmd/compile/internal/gc/typecheck.go
diff --git a/src/cmd/internal/gc/unsafe.go b/src/cmd/compile/internal/gc/unsafe.go
similarity index 100%
rename from src/cmd/internal/gc/unsafe.go
rename to src/cmd/compile/internal/gc/unsafe.go
diff --git a/src/cmd/internal/gc/util.go b/src/cmd/compile/internal/gc/util.go
similarity index 100%
rename from src/cmd/internal/gc/util.go
rename to src/cmd/compile/internal/gc/util.go
diff --git a/src/cmd/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
similarity index 100%
rename from src/cmd/internal/gc/walk.go
rename to src/cmd/compile/internal/gc/walk.go
diff --git a/src/cmd/internal/gc/y.go b/src/cmd/compile/internal/gc/y.go
similarity index 100%
rename from src/cmd/internal/gc/y.go
rename to src/cmd/compile/internal/gc/y.go
diff --git a/src/cmd/internal/gc/y.output b/src/cmd/compile/internal/gc/y.output
similarity index 100%
rename from src/cmd/internal/gc/y.output
rename to src/cmd/compile/internal/gc/y.output
diff --git a/src/cmd/9g/cgen.go b/src/cmd/compile/internal/ppc64/cgen.go
similarity index 98%
rename from src/cmd/9g/cgen.go
rename to src/cmd/compile/internal/ppc64/cgen.go
index 5d24a6ff67..37dd6cefb2 100644
--- a/src/cmd/9g/cgen.go
+++ b/src/cmd/compile/internal/ppc64/cgen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
 )
diff --git a/src/cmd/9g/galign.go b/src/cmd/compile/internal/ppc64/galign.go
similarity index 97%
rename from src/cmd/9g/galign.go
rename to src/cmd/compile/internal/ppc64/galign.go
index 6e1612007b..73aef6fde9 100644
--- a/src/cmd/9g/galign.go
+++ b/src/cmd/compile/internal/ppc64/galign.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
 )
@@ -45,7 +45,7 @@ func betypeinit() {
 	gc.Widthreg = 8
 }
 
-func main() {
+func Main() {
 	gc.Thearch.Thechar = thechar
 	gc.Thearch.Thestring = thestring
 	gc.Thearch.Thelinkarch = thelinkarch
diff --git a/src/cmd/9g/ggen.go b/src/cmd/compile/internal/ppc64/ggen.go
similarity index 99%
rename from src/cmd/9g/ggen.go
rename to src/cmd/compile/internal/ppc64/ggen.go
index 3a10a2a760..1b936b8a5f 100644
--- a/src/cmd/9g/ggen.go
+++ b/src/cmd/compile/internal/ppc64/ggen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
 	"fmt"
diff --git a/src/cmd/9g/gsubr.go b/src/cmd/compile/internal/ppc64/gsubr.go
similarity index 99%
rename from src/cmd/9g/gsubr.go
rename to src/cmd/compile/internal/ppc64/gsubr.go
index 3a7c884fd1..2501972846 100644
--- a/src/cmd/9g/gsubr.go
+++ b/src/cmd/compile/internal/ppc64/gsubr.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/gc"
-	"cmd/internal/gc/big"
+	"cmd/compile/internal/big"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
 	"fmt"
diff --git a/src/cmd/9g/opt.go b/src/cmd/compile/internal/ppc64/opt.go
similarity index 96%
rename from src/cmd/9g/opt.go
rename to src/cmd/compile/internal/ppc64/opt.go
index 4a134f134f..1704f63c48 100644
--- a/src/cmd/9g/opt.go
+++ b/src/cmd/compile/internal/ppc64/opt.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package ppc64
 
 // Many Power ISA arithmetic and logical instructions come in four
 // standard variants.  These bits let us map between variants.
diff --git a/src/cmd/9g/peep.go b/src/cmd/compile/internal/ppc64/peep.go
similarity index 99%
rename from src/cmd/9g/peep.go
rename to src/cmd/compile/internal/ppc64/peep.go
index 94c9b1554b..16eeb39097 100644
--- a/src/cmd/9g/peep.go
+++ b/src/cmd/compile/internal/ppc64/peep.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
 	"fmt"
diff --git a/src/cmd/9g/prog.go b/src/cmd/compile/internal/ppc64/prog.go
similarity index 99%
rename from src/cmd/9g/prog.go
rename to src/cmd/compile/internal/ppc64/prog.go
index e28e389fac..c7e182769d 100644
--- a/src/cmd/9g/prog.go
+++ b/src/cmd/compile/internal/ppc64/prog.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/ppc64"
 )
diff --git a/src/cmd/9g/reg.go b/src/cmd/compile/internal/ppc64/reg.go
similarity index 98%
rename from src/cmd/9g/reg.go
rename to src/cmd/compile/internal/ppc64/reg.go
index fb0c2e37ec..fa1cb71975 100644
--- a/src/cmd/9g/reg.go
+++ b/src/cmd/compile/internal/ppc64/reg.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package ppc64
 
 import "cmd/internal/obj/ppc64"
-import "cmd/internal/gc"
+import "cmd/compile/internal/gc"
 
 const (
 	NREGVAR = 64 /* 32 general + 32 floating */
diff --git a/src/cmd/8g/cgen.go b/src/cmd/compile/internal/x86/cgen.go
similarity index 98%
rename from src/cmd/8g/cgen.go
rename to src/cmd/compile/internal/x86/cgen.go
index 48d9e9867a..1768674e42 100644
--- a/src/cmd/8g/cgen.go
+++ b/src/cmd/compile/internal/x86/cgen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package x86
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/8g/cgen64.go b/src/cmd/compile/internal/x86/cgen64.go
similarity index 99%
rename from src/cmd/8g/cgen64.go
rename to src/cmd/compile/internal/x86/cgen64.go
index 80a9642f75..0b061ffb60 100644
--- a/src/cmd/8g/cgen64.go
+++ b/src/cmd/compile/internal/x86/cgen64.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package x86
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/8g/galign.go b/src/cmd/compile/internal/x86/galign.go
similarity index 98%
rename from src/cmd/8g/galign.go
rename to src/cmd/compile/internal/x86/galign.go
index 3651f509c9..2b602e1bb3 100644
--- a/src/cmd/8g/galign.go
+++ b/src/cmd/compile/internal/x86/galign.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package x86
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 	"fmt"
@@ -39,7 +39,7 @@ func betypeinit() {
 	gc.Widthreg = 4
 }
 
-func main() {
+func Main() {
 	gc.Thearch.Thechar = thechar
 	gc.Thearch.Thestring = thestring
 	gc.Thearch.Thelinkarch = thelinkarch
diff --git a/src/cmd/8g/ggen.go b/src/cmd/compile/internal/x86/ggen.go
similarity index 99%
rename from src/cmd/8g/ggen.go
rename to src/cmd/compile/internal/x86/ggen.go
index bd2c13e867..dabc139f30 100644
--- a/src/cmd/8g/ggen.go
+++ b/src/cmd/compile/internal/x86/ggen.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package x86
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/8g/gsubr.go b/src/cmd/compile/internal/x86/gsubr.go
similarity index 99%
rename from src/cmd/8g/gsubr.go
rename to src/cmd/compile/internal/x86/gsubr.go
index 99bce6eaba..baf251781c 100644
--- a/src/cmd/8g/gsubr.go
+++ b/src/cmd/compile/internal/x86/gsubr.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package x86
 
 import (
-	"cmd/internal/gc"
-	"cmd/internal/gc/big"
+	"cmd/compile/internal/big"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 	"fmt"
diff --git a/src/cmd/8g/peep.go b/src/cmd/compile/internal/x86/peep.go
similarity index 99%
rename from src/cmd/8g/peep.go
rename to src/cmd/compile/internal/x86/peep.go
index e309aea785..8b50eab077 100644
--- a/src/cmd/8g/peep.go
+++ b/src/cmd/compile/internal/x86/peep.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package x86
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 	"fmt"
diff --git a/src/cmd/8g/prog.go b/src/cmd/compile/internal/x86/prog.go
similarity index 99%
rename from src/cmd/8g/prog.go
rename to src/cmd/compile/internal/x86/prog.go
index 1346c20f2b..f96a1aa945 100644
--- a/src/cmd/8g/prog.go
+++ b/src/cmd/compile/internal/x86/prog.go
@@ -2,10 +2,10 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-package main
+package x86
 
 import (
-	"cmd/internal/gc"
+	"cmd/compile/internal/gc"
 	"cmd/internal/obj"
 	"cmd/internal/obj/x86"
 )
diff --git a/src/cmd/8g/reg.go b/src/cmd/compile/internal/x86/reg.go
similarity index 98%
rename from src/cmd/8g/reg.go
rename to src/cmd/compile/internal/x86/reg.go
index 50b5b97ab1..8c97171e47 100644
--- a/src/cmd/8g/reg.go
+++ b/src/cmd/compile/internal/x86/reg.go
@@ -28,10 +28,10 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package x86
 
 import "cmd/internal/obj/x86"
-import "cmd/internal/gc"
+import "cmd/compile/internal/gc"
 
 const (
 	NREGVAR = 16 /* 8 integer + 8 floating */
diff --git a/src/cmd/compile/main.go b/src/cmd/compile/main.go
new file mode 100644
index 0000000000..7b69c34424
--- /dev/null
+++ b/src/cmd/compile/main.go
@@ -0,0 +1,34 @@
+// Copyright 2015 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"cmd/compile/internal/amd64"
+	"cmd/compile/internal/arm"
+	"cmd/compile/internal/arm64"
+	"cmd/compile/internal/ppc64"
+	"cmd/compile/internal/x86"
+	"cmd/internal/obj"
+	"fmt"
+	"os"
+)
+
+func main() {
+	switch obj.Getgoarch() {
+	default:
+		fmt.Fprintf(os.Stderr, "compile: unknown architecture %q\n", obj.Getgoarch())
+		os.Exit(2)
+	case "386":
+		x86.Main()
+	case "amd64", "amd64p32":
+		amd64.Main()
+	case "arm":
+		arm.Main()
+	case "arm64":
+		arm64.Main()
+	case "ppc64", "ppc64le":
+		ppc64.Main()
+	}
+}
diff --git a/src/cmd/6l/asm.go b/src/cmd/link/internal/amd64/asm.go
similarity index 99%
rename from src/cmd/6l/asm.go
rename to src/cmd/link/internal/amd64/asm.go
index 02b4c7cdd2..a4883f1a33 100644
--- a/src/cmd/6l/asm.go
+++ b/src/cmd/link/internal/amd64/asm.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"debug/elf"
 	"fmt"
 	"log"
diff --git a/src/cmd/6l/l.go b/src/cmd/link/internal/amd64/l.go
similarity index 99%
rename from src/cmd/6l/l.go
rename to src/cmd/link/internal/amd64/l.go
index 64466d126a..2537419eff 100644
--- a/src/cmd/6l/l.go
+++ b/src/cmd/link/internal/amd64/l.go
@@ -28,7 +28,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package amd64
 
 const (
 	thechar   = '6'
diff --git a/src/cmd/6l/obj.go b/src/cmd/link/internal/amd64/obj.go
similarity index 99%
rename from src/cmd/6l/obj.go
rename to src/cmd/link/internal/amd64/obj.go
index 1dc9e02a8b..e489bb75a9 100644
--- a/src/cmd/6l/obj.go
+++ b/src/cmd/link/internal/amd64/obj.go
@@ -28,18 +28,18 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package amd64
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"fmt"
 	"log"
 )
 
 // Reading object files.
 
-func main() {
+func Main() {
 	linkarchinit()
 	ld.Ldmain()
 }
diff --git a/src/cmd/link/internal/amd64/z.go b/src/cmd/link/internal/amd64/z.go
new file mode 100644
index 0000000000..f70035b9e3
--- /dev/null
+++ b/src/cmd/link/internal/amd64/z.go
@@ -0,0 +1 @@
+package amd64
diff --git a/src/cmd/5l/asm.go b/src/cmd/link/internal/arm/asm.go
similarity index 99%
rename from src/cmd/5l/asm.go
rename to src/cmd/link/internal/arm/asm.go
index 70d6790fc1..e310d29e9a 100644
--- a/src/cmd/5l/asm.go
+++ b/src/cmd/link/internal/arm/asm.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"fmt"
 	"log"
 )
diff --git a/src/cmd/5l/l.go b/src/cmd/link/internal/arm/l.go
similarity index 99%
rename from src/cmd/5l/l.go
rename to src/cmd/link/internal/arm/l.go
index adc8d286ae..4973772163 100644
--- a/src/cmd/5l/l.go
+++ b/src/cmd/link/internal/arm/l.go
@@ -28,7 +28,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm
 
 // Writing object files.
 
diff --git a/src/cmd/5l/obj.go b/src/cmd/link/internal/arm/obj.go
similarity index 98%
rename from src/cmd/5l/obj.go
rename to src/cmd/link/internal/arm/obj.go
index 9c9578343e..14fe7a64eb 100644
--- a/src/cmd/5l/obj.go
+++ b/src/cmd/link/internal/arm/obj.go
@@ -28,18 +28,18 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"fmt"
 	"log"
 )
 
 // Reading object files.
 
-func main() {
+func Main() {
 	linkarchinit()
 	ld.Ldmain()
 }
diff --git a/src/cmd/7l/asm.go b/src/cmd/link/internal/arm64/asm.go
similarity index 99%
rename from src/cmd/7l/asm.go
rename to src/cmd/link/internal/arm64/asm.go
index 064ff56283..9d76f0e0c3 100644
--- a/src/cmd/7l/asm.go
+++ b/src/cmd/link/internal/arm64/asm.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"encoding/binary"
 	"fmt"
 	"log"
diff --git a/src/cmd/7l/l.go b/src/cmd/link/internal/arm64/l.go
similarity index 99%
rename from src/cmd/7l/l.go
rename to src/cmd/link/internal/arm64/l.go
index 7227cc430f..8d0d57e72a 100644
--- a/src/cmd/7l/l.go
+++ b/src/cmd/link/internal/arm64/l.go
@@ -28,7 +28,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm64
 
 // Writing object files.
 
diff --git a/src/cmd/7l/obj.go b/src/cmd/link/internal/arm64/obj.go
similarity index 98%
rename from src/cmd/7l/obj.go
rename to src/cmd/link/internal/arm64/obj.go
index f88584b938..56f5815903 100644
--- a/src/cmd/7l/obj.go
+++ b/src/cmd/link/internal/arm64/obj.go
@@ -28,18 +28,18 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package arm64
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"fmt"
 	"log"
 )
 
 // Reading object files.
 
-func main() {
+func Main() {
 	linkarchinit()
 	ld.Ldmain()
 }
diff --git a/src/cmd/internal/ld/ar.go b/src/cmd/link/internal/ld/ar.go
similarity index 100%
rename from src/cmd/internal/ld/ar.go
rename to src/cmd/link/internal/ld/ar.go
diff --git a/src/cmd/internal/ld/arch.go b/src/cmd/link/internal/ld/arch.go
similarity index 100%
rename from src/cmd/internal/ld/arch.go
rename to src/cmd/link/internal/ld/arch.go
diff --git a/src/cmd/internal/ld/data.go b/src/cmd/link/internal/ld/data.go
similarity index 100%
rename from src/cmd/internal/ld/data.go
rename to src/cmd/link/internal/ld/data.go
diff --git a/src/cmd/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go
similarity index 100%
rename from src/cmd/internal/ld/decodesym.go
rename to src/cmd/link/internal/ld/decodesym.go
diff --git a/src/cmd/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go
similarity index 100%
rename from src/cmd/internal/ld/dwarf.go
rename to src/cmd/link/internal/ld/dwarf.go
diff --git a/src/cmd/internal/ld/dwarf_defs.go b/src/cmd/link/internal/ld/dwarf_defs.go
similarity index 100%
rename from src/cmd/internal/ld/dwarf_defs.go
rename to src/cmd/link/internal/ld/dwarf_defs.go
diff --git a/src/cmd/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go
similarity index 100%
rename from src/cmd/internal/ld/elf.go
rename to src/cmd/link/internal/ld/elf.go
diff --git a/src/cmd/internal/ld/go.go b/src/cmd/link/internal/ld/go.go
similarity index 100%
rename from src/cmd/internal/ld/go.go
rename to src/cmd/link/internal/ld/go.go
diff --git a/src/cmd/internal/ld/ld.go b/src/cmd/link/internal/ld/ld.go
similarity index 100%
rename from src/cmd/internal/ld/ld.go
rename to src/cmd/link/internal/ld/ld.go
diff --git a/src/cmd/internal/ld/ldelf.go b/src/cmd/link/internal/ld/ldelf.go
similarity index 100%
rename from src/cmd/internal/ld/ldelf.go
rename to src/cmd/link/internal/ld/ldelf.go
diff --git a/src/cmd/internal/ld/ldmacho.go b/src/cmd/link/internal/ld/ldmacho.go
similarity index 100%
rename from src/cmd/internal/ld/ldmacho.go
rename to src/cmd/link/internal/ld/ldmacho.go
diff --git a/src/cmd/internal/ld/ldpe.go b/src/cmd/link/internal/ld/ldpe.go
similarity index 100%
rename from src/cmd/internal/ld/ldpe.go
rename to src/cmd/link/internal/ld/ldpe.go
diff --git a/src/cmd/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
similarity index 100%
rename from src/cmd/internal/ld/lib.go
rename to src/cmd/link/internal/ld/lib.go
diff --git a/src/cmd/internal/ld/link.go b/src/cmd/link/internal/ld/link.go
similarity index 100%
rename from src/cmd/internal/ld/link.go
rename to src/cmd/link/internal/ld/link.go
diff --git a/src/cmd/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go
similarity index 100%
rename from src/cmd/internal/ld/macho.go
rename to src/cmd/link/internal/ld/macho.go
diff --git a/src/cmd/internal/ld/macho_combine_dwarf.go b/src/cmd/link/internal/ld/macho_combine_dwarf.go
similarity index 100%
rename from src/cmd/internal/ld/macho_combine_dwarf.go
rename to src/cmd/link/internal/ld/macho_combine_dwarf.go
diff --git a/src/cmd/internal/ld/objfile.go b/src/cmd/link/internal/ld/objfile.go
similarity index 100%
rename from src/cmd/internal/ld/objfile.go
rename to src/cmd/link/internal/ld/objfile.go
diff --git a/src/cmd/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
similarity index 100%
rename from src/cmd/internal/ld/pcln.go
rename to src/cmd/link/internal/ld/pcln.go
diff --git a/src/cmd/internal/ld/pe.go b/src/cmd/link/internal/ld/pe.go
similarity index 100%
rename from src/cmd/internal/ld/pe.go
rename to src/cmd/link/internal/ld/pe.go
diff --git a/src/cmd/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go
similarity index 100%
rename from src/cmd/internal/ld/pobj.go
rename to src/cmd/link/internal/ld/pobj.go
diff --git a/src/cmd/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go
similarity index 100%
rename from src/cmd/internal/ld/sym.go
rename to src/cmd/link/internal/ld/sym.go
diff --git a/src/cmd/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go
similarity index 100%
rename from src/cmd/internal/ld/symtab.go
rename to src/cmd/link/internal/ld/symtab.go
diff --git a/src/cmd/internal/ld/textflag.go b/src/cmd/link/internal/ld/textflag.go
similarity index 100%
rename from src/cmd/internal/ld/textflag.go
rename to src/cmd/link/internal/ld/textflag.go
diff --git a/src/cmd/internal/ld/util.go b/src/cmd/link/internal/ld/util.go
similarity index 100%
rename from src/cmd/internal/ld/util.go
rename to src/cmd/link/internal/ld/util.go
diff --git a/src/cmd/9l/asm.go b/src/cmd/link/internal/ppc64/asm.go
similarity index 99%
rename from src/cmd/9l/asm.go
rename to src/cmd/link/internal/ppc64/asm.go
index 45aa3f84c2..f070921ecf 100644
--- a/src/cmd/9l/asm.go
+++ b/src/cmd/link/internal/ppc64/asm.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"encoding/binary"
 	"fmt"
 	"log"
diff --git a/src/cmd/9l/l.go b/src/cmd/link/internal/ppc64/l.go
similarity index 99%
rename from src/cmd/9l/l.go
rename to src/cmd/link/internal/ppc64/l.go
index 8723eaeca4..1275a34dbb 100644
--- a/src/cmd/9l/l.go
+++ b/src/cmd/link/internal/ppc64/l.go
@@ -28,7 +28,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package ppc64
 
 // Writing object files.
 
diff --git a/src/cmd/9l/obj.go b/src/cmd/link/internal/ppc64/obj.go
similarity index 98%
rename from src/cmd/9l/obj.go
rename to src/cmd/link/internal/ppc64/obj.go
index 011f290298..d663b6ebae 100644
--- a/src/cmd/9l/obj.go
+++ b/src/cmd/link/internal/ppc64/obj.go
@@ -28,18 +28,18 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package ppc64
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"fmt"
 	"log"
 )
 
 // Reading object files.
 
-func main() {
+func Main() {
 	linkarchinit()
 	ld.Ldmain()
 }
diff --git a/src/cmd/8l/asm.go b/src/cmd/link/internal/x86/asm.go
similarity index 99%
rename from src/cmd/8l/asm.go
rename to src/cmd/link/internal/x86/asm.go
index a736d43686..7bb99ca8b5 100644
--- a/src/cmd/8l/asm.go
+++ b/src/cmd/link/internal/x86/asm.go
@@ -28,11 +28,11 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package x86
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"fmt"
 	"log"
 )
diff --git a/src/cmd/8l/l.go b/src/cmd/link/internal/x86/l.go
similarity index 99%
rename from src/cmd/8l/l.go
rename to src/cmd/link/internal/x86/l.go
index 5cb9f8d8af..8a811ff0a2 100644
--- a/src/cmd/8l/l.go
+++ b/src/cmd/link/internal/x86/l.go
@@ -28,7 +28,7 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package x86
 
 const (
 	thechar   = '8'
diff --git a/src/cmd/8l/obj.go b/src/cmd/link/internal/x86/obj.go
similarity index 99%
rename from src/cmd/8l/obj.go
rename to src/cmd/link/internal/x86/obj.go
index bea0d03cfe..ee408f70c6 100644
--- a/src/cmd/8l/obj.go
+++ b/src/cmd/link/internal/x86/obj.go
@@ -28,18 +28,18 @@
 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 // THE SOFTWARE.
 
-package main
+package x86
 
 import (
-	"cmd/internal/ld"
 	"cmd/internal/obj"
+	"cmd/link/internal/ld"
 	"fmt"
 	"log"
 )
 
 // Reading object files.
 
-func main() {
+func Main() {
 	linkarchinit()
 	ld.Ldmain()
 }
diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go
new file mode 100644
index 0000000000..0e6c34ee0a
--- /dev/null
+++ b/src/cmd/link/main.go
@@ -0,0 +1,34 @@
+// Copyright 2015 The Go Authors.  All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+	"cmd/internal/obj"
+	"cmd/link/internal/amd64"
+	"cmd/link/internal/arm"
+	"cmd/link/internal/arm64"
+	"cmd/link/internal/ppc64"
+	"cmd/link/internal/x86"
+	"fmt"
+	"os"
+)
+
+func main() {
+	switch obj.Getgoarch() {
+	default:
+		fmt.Fprintf(os.Stderr, "link: unknown architecture %q\n", obj.Getgoarch())
+		os.Exit(2)
+	case "386":
+		x86.Main()
+	case "amd64", "amd64p32":
+		amd64.Main()
+	case "arm":
+		arm.Main()
+	case "arm64":
+		arm64.Main()
+	case "ppc64", "ppc64le":
+		ppc64.Main()
+	}
+}

From 0f4132c907d9749a1a41a20af7856065ee10de9e Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 21 May 2015 13:28:13 -0400
Subject: [PATCH 174/232] all: build and use go tool compile, go tool link

This CL fixes the build to use the newly created go tool compile
and go tool link in place of go tool 5g, go tool 5l, and so on.

See golang-dev thread titled "go tool compile, etc" for background.

Although it was not a primary motivation, this conversion does
reduce the wall clock time and cpu time required for make.bash
by about 10%.

Change-Id: I79cbbdb676cab029db8aeefb99a53178ff55f98d
Reviewed-on: https://go-review.googlesource.com/10288
Reviewed-by: Rob Pike 
---
 src/cmd/dist/build.go           | 16 ++++------------
 src/cmd/dist/buildtool.go       | 28 +++++++++++++++-------------
 src/cmd/go/build.go             | 10 +++++-----
 src/cmd/pack/pack_test.go       | 10 +++++-----
 src/debug/gosym/pclntab_test.go |  2 +-
 test/bench/shootout/timing.sh   |  4 ++--
 test/fixedbugs/bug248.go        | 10 +++++-----
 test/fixedbugs/bug302.go        |  4 ++--
 test/fixedbugs/bug345.go        |  4 ++--
 test/fixedbugs/bug369.go        |  8 ++++----
 test/fixedbugs/issue9355.go     |  2 +-
 test/nosplit.go                 |  2 +-
 test/run.go                     | 15 +++++----------
 test/sinit_run.go               |  2 +-
 14 files changed, 53 insertions(+), 64 deletions(-)

diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index 47c0a0a1d1..d6cfaf02cb 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -540,7 +540,7 @@ func install(dir string) {
 		if elem == "go" {
 			elem = "go_bootstrap"
 		}
-		link = []string{fmt.Sprintf("%s/%sl", tooldir, gochar), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
+		link = []string{pathf("%s/link", tooldir), "-o", pathf("%s/%s%s", tooldir, elem, exe)}
 		targ = len(link) - 1
 	}
 	ttarg := mtime(link[targ])
@@ -675,7 +675,7 @@ func install(dir string) {
 	} else {
 		archive = b
 	}
-	compile := []string{pathf("%s/%sg", tooldir, gochar), "-pack", "-o", b, "-p", pkg}
+	compile := []string{pathf("%s/compile", tooldir), "-pack", "-o", b, "-p", pkg}
 	if dir == "runtime" {
 		compile = append(compile, "-+", "-asmhdr", pathf("%s/go_asm.h", workdir))
 	}
@@ -897,17 +897,9 @@ var buildorder = []string{
 // compilers but build only the $GOARCH ones.
 var cleantab = []string{
 	// Commands and C libraries.
-	"cmd/5g",
-	"cmd/5l",
-	"cmd/6g",
-	"cmd/6l",
-	"cmd/7g",
-	"cmd/7l",
-	"cmd/8g",
-	"cmd/8l",
-	"cmd/9g",
-	"cmd/9l",
+	"cmd/compile",
 	"cmd/go",
+	"cmd/link",
 	"cmd/old5a",
 	"cmd/old6a",
 	"cmd/old8a",
diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go
index 946229d827..2840f71749 100644
--- a/src/cmd/dist/buildtool.go
+++ b/src/cmd/dist/buildtool.go
@@ -23,31 +23,33 @@ import (
 // which are commands, and entries beginning with internal/, which are
 // packages supporting the commands.
 var bootstrapDirs = []string{
-	"5g",
-	"5l",
-	"6g",
-	"6l",
-	"7g",
-	"7l",
-	"8g",
-	"8l",
-	"9g",
-	"9l",
 	"asm",
 	"asm/internal/arch",
 	"asm/internal/asm",
 	"asm/internal/flags",
 	"asm/internal/lex",
+	"compile",
+	"compile/internal/amd64",
+	"compile/internal/arm",
+	"compile/internal/arm64",
+	"compile/internal/big",
+	"compile/internal/gc",
+	"compile/internal/ppc64",
+	"compile/internal/x86",
 	"internal/asm",
 	"internal/gcprog",
-	"internal/gc/big",
-	"internal/gc",
-	"internal/ld",
 	"internal/obj",
 	"internal/obj/arm",
 	"internal/obj/arm64",
 	"internal/obj/ppc64",
 	"internal/obj/x86",
+	"link",
+	"link/internal/amd64",
+	"link/internal/arm",
+	"link/internal/arm64",
+	"link/internal/ld",
+	"link/internal/ppc64",
+	"link/internal/x86",
 	"old5a",
 	"old6a",
 	"old8a",
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index 738f748391..aa9a408eff 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1532,7 +1532,7 @@ func (b *builder) linkShared(a *action) (err error) {
 		}
 		ldflags = append(ldflags, d.p.ImportPath+"="+d.target)
 	}
-	return b.run(".", a.target, nil, buildToolExec, tool(archChar()+"l"), "-o", a.target, importArgs, ldflags)
+	return b.run(".", a.target, nil, buildToolExec, tool("link"), "-o", a.target, importArgs, ldflags)
 }
 
 // install is the action for installing a single package or executable.
@@ -2109,11 +2109,11 @@ func (noToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error
 type gcToolchain struct{}
 
 func (gcToolchain) compiler() string {
-	return tool(archChar() + "g")
+	return tool("compile")
 }
 
 func (gcToolchain) linker() string {
-	return tool(archChar() + "l")
+	return tool("link")
 }
 
 func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool, importArgs []string, gofiles []string) (ofile string, output []byte, err error) {
@@ -2152,7 +2152,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
 		gcargs = append(gcargs, "-installsuffix", buildContext.InstallSuffix)
 	}
 
-	args := []interface{}{buildToolExec, tool(archChar() + "g"), "-o", ofile, "-trimpath", b.work, buildGcflags, gcargs, "-D", p.localPrefix, importArgs}
+	args := []interface{}{buildToolExec, tool("compile"), "-o", ofile, "-trimpath", b.work, buildGcflags, gcargs, "-D", p.localPrefix, importArgs}
 	if ofile == archive {
 		args = append(args, "-pack")
 	}
@@ -2333,7 +2333,7 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,
 	ldflags = setextld(ldflags, compiler)
 	ldflags = append(ldflags, "-buildmode="+ldBuildmode)
 	ldflags = append(ldflags, buildLdflags...)
-	return b.run(".", p.ImportPath, nil, buildToolExec, tool(archChar()+"l"), "-o", out, importArgs, ldflags, mainpkg)
+	return b.run(".", p.ImportPath, nil, buildToolExec, tool("link"), "-o", out, importArgs, ldflags, mainpkg)
 }
 
 func (gcToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go
index 9c33f4f98b..97992059b9 100644
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -230,9 +230,9 @@ func TestHello(t *testing.T) {
 	}
 
 	run("go", "build", "cmd/pack") // writes pack binary to dir
-	run("go", "tool", char+"g", "hello.go")
+	run("go", "tool", "compile", "hello.go")
 	run("./pack", "grc", "hello.a", "hello."+char)
-	run("go", "tool", char+"l", "-o", "a.out", "hello.a")
+	run("go", "tool", "link", "-o", "a.out", "hello.a")
 	out := run("./a.out")
 	if out != "hello world\n" {
 		t.Fatalf("incorrect output: %q, want %q", out, "hello world\n")
@@ -304,10 +304,10 @@ func TestLargeDefs(t *testing.T) {
 	}
 
 	run("go", "build", "cmd/pack") // writes pack binary to dir
-	run("go", "tool", char+"g", "large.go")
+	run("go", "tool", "compile", "large.go")
 	run("./pack", "grc", "large.a", "large."+char)
-	run("go", "tool", char+"g", "-I", ".", "main.go")
-	run("go", "tool", char+"l", "-L", ".", "-o", "a.out", "main."+char)
+	run("go", "tool", "compile", "-I", ".", "main.go")
+	run("go", "tool", "link", "-L", ".", "-o", "a.out", "main."+char)
 	out := run("./a.out")
 	if out != "ok\n" {
 		t.Fatalf("incorrect output: %q, want %q", out, "ok\n")
diff --git a/src/debug/gosym/pclntab_test.go b/src/debug/gosym/pclntab_test.go
index 6855a65bbe..c6943a631a 100644
--- a/src/debug/gosym/pclntab_test.go
+++ b/src/debug/gosym/pclntab_test.go
@@ -49,7 +49,7 @@ func dotest(self bool) bool {
 	// the resulting binary looks like it was built from pclinetest.s,
 	// but we have renamed it to keep it away from the go tool.
 	pclinetestBinary = filepath.Join(pclineTempDir, "pclinetest")
-	command := fmt.Sprintf("go tool asm -o %s.6 pclinetest.asm && go tool 6l -H linux -E main -o %s %s.6",
+	command := fmt.Sprintf("go tool asm -o %s.o pclinetest.asm && go tool link -H linux -E main -o %s %s.o",
 		pclinetestBinary, pclinetestBinary, pclinetestBinary)
 	cmd := exec.Command("sh", "-c", command)
 	cmd.Stdout = os.Stdout
diff --git a/test/bench/shootout/timing.sh b/test/bench/shootout/timing.sh
index a06c326c3e..b15825cc68 100755
--- a/test/bench/shootout/timing.sh
+++ b/test/bench/shootout/timing.sh
@@ -7,8 +7,8 @@ set -e
 
 eval $(go tool dist env)
 O=$GOCHAR
-GC="go tool ${O}g"
-LD="go tool ${O}l"
+GC="go tool compile"
+LD="go tool link"
 
 gccm=""
 case "$O" in
diff --git a/test/fixedbugs/bug248.go b/test/fixedbugs/bug248.go
index 3d9a408508..2dad1bc654 100644
--- a/test/fixedbugs/bug248.go
+++ b/test/fixedbugs/bug248.go
@@ -26,11 +26,11 @@ func main() {
 	err = os.Chdir(filepath.Join("fixedbugs", "bug248.dir"))
 	check(err)
 
-	run("go", "tool", a+"g", "bug0.go")
-	run("go", "tool", a+"g", "bug1.go")
-	run("go", "tool", a+"g", "bug2.go")
-	run(errchk, "go", "tool", a+"g", "-e", "bug3.go")
-	run("go", "tool", a+"l", "bug2."+a)
+	run("go", "tool", "compile", "bug0.go")
+	run("go", "tool", "compile", "bug1.go")
+	run("go", "tool", "compile", "bug2.go")
+	run(errchk, "go", "tool", "compile", "-e", "bug3.go")
+	run("go", "tool", "link", "bug2."+a)
 	run(fmt.Sprintf(".%c%s.out", filepath.Separator, a))
 
 	os.Remove("bug0." + a)
diff --git a/test/fixedbugs/bug302.go b/test/fixedbugs/bug302.go
index 327d52254c..faae665035 100644
--- a/test/fixedbugs/bug302.go
+++ b/test/fixedbugs/bug302.go
@@ -23,9 +23,9 @@ func main() {
 		os.Exit(1)
 	}
 
-	run("go", "tool", a+"g", filepath.Join("fixedbugs", "bug302.dir", "p.go"))
+	run("go", "tool", "compile", filepath.Join("fixedbugs", "bug302.dir", "p.go"))
 	run("go", "tool", "pack", "grc", "pp.a", "p."+a)
-	run("go", "tool", a+"g", "-I", ".", filepath.Join("fixedbugs", "bug302.dir", "main.go"))
+	run("go", "tool", "compile", "-I", ".", filepath.Join("fixedbugs", "bug302.dir", "main.go"))
 	os.Remove("p."+a)
 	os.Remove("pp.a")
 	os.Remove("main."+a)
diff --git a/test/fixedbugs/bug345.go b/test/fixedbugs/bug345.go
index 745144fbf2..3ef57bd112 100644
--- a/test/fixedbugs/bug345.go
+++ b/test/fixedbugs/bug345.go
@@ -26,8 +26,8 @@ func main() {
 	err = os.Chdir(filepath.Join(".", "fixedbugs", "bug345.dir"))
 	check(err)
 
-	run("go", "tool", a+"g", "io.go")
-	run(errchk, "go", "tool", a+"g", "-e", "main.go")
+	run("go", "tool", "compile", "io.go")
+	run(errchk, "go", "tool", "compile", "-e", "main.go")
 	os.Remove("io." + a)
 }
 
diff --git a/test/fixedbugs/bug369.go b/test/fixedbugs/bug369.go
index 519703fb1e..b85428be02 100644
--- a/test/fixedbugs/bug369.go
+++ b/test/fixedbugs/bug369.go
@@ -24,10 +24,10 @@ func main() {
 	err = os.Chdir(filepath.Join(".", "fixedbugs", "bug369.dir"))
 	check(err)
 
-	run("go", "tool", a+"g", "-N", "-o", "slow."+a, "pkg.go")
-	run("go", "tool", a+"g", "-o", "fast."+a, "pkg.go")
-	run("go", "tool", a+"g", "-o", "main."+a, "main.go")
-	run("go", "tool", a+"l", "-o", "a.exe", "main."+a)
+	run("go", "tool", "compile", "-N", "-o", "slow."+a, "pkg.go")
+	run("go", "tool", "compile", "-o", "fast."+a, "pkg.go")
+	run("go", "tool", "compile", "-o", "main."+a, "main.go")
+	run("go", "tool", "link", "-o", "a.exe", "main."+a)
 	run("." + string(filepath.Separator) + "a.exe")
 
 	os.Remove("slow." + a)
diff --git a/test/fixedbugs/issue9355.go b/test/fixedbugs/issue9355.go
index bdc0dd06c6..a841f773ee 100644
--- a/test/fixedbugs/issue9355.go
+++ b/test/fixedbugs/issue9355.go
@@ -26,7 +26,7 @@ func main() {
 	err = os.Chdir(filepath.Join("fixedbugs", "issue9355.dir"))
 	check(err)
 
-	out := run("go", "tool", a+"g", "-S", "a.go")
+	out := run("go", "tool", "compile", "-S", "a.go")
 	os.Remove("a." + a)
 
 	// 6g/8g print the offset as dec, but 5g/9g print the offset as hex.
diff --git a/test/nosplit.go b/test/nosplit.go
index bd7a8ddac3..0fc8dc47f0 100644
--- a/test/nosplit.go
+++ b/test/nosplit.go
@@ -193,7 +193,7 @@ func main() {
 		thechar = strings.TrimSpace(string(gochar))
 	}
 
-	version, err := exec.Command("go", "tool", thechar+"g", "-V").Output()
+	version, err := exec.Command("go", "tool", "compile", "-V").Output()
 	if err != nil {
 		bug()
 		fmt.Printf("running go tool %sg -V: %v\n", thechar, err)
diff --git a/test/run.go b/test/run.go
index 10ba7a8432..a3124bba94 100644
--- a/test/run.go
+++ b/test/run.go
@@ -41,9 +41,6 @@ var (
 )
 
 var (
-	// gc and ld are [568][gl].
-	gc, ld string
-
 	// letter is the build.ArchChar
 	letter string
 
@@ -87,8 +84,6 @@ func main() {
 	var err error
 	letter, err = build.ArchChar(build.Default.GOARCH)
 	check(err)
-	gc = letter + "g"
-	ld = letter + "l"
 
 	var tests []*test
 	if flag.NArg() > 0 {
@@ -192,11 +187,11 @@ func goFiles(dir string) []string {
 type runCmd func(...string) ([]byte, error)
 
 func compileFile(runcmd runCmd, longname string) (out []byte, err error) {
-	return runcmd("go", "tool", gc, "-e", longname)
+	return runcmd("go", "tool", "compile", "-e", longname)
 }
 
 func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err error) {
-	cmd := []string{"go", "tool", gc, "-e", "-D", ".", "-I", "."}
+	cmd := []string{"go", "tool", "compile", "-e", "-D", ".", "-I", "."}
 	for _, name := range names {
 		cmd = append(cmd, filepath.Join(dir, name))
 	}
@@ -205,7 +200,7 @@ func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err e
 
 func linkFile(runcmd runCmd, goname string) (err error) {
 	pfile := strings.Replace(goname, ".go", "."+letter, -1)
-	_, err = runcmd("go", "tool", ld, "-w", "-o", "a.exe", "-L", ".", pfile)
+	_, err = runcmd("go", "tool", "link", "-w", "-o", "a.exe", "-L", ".", pfile)
 	return
 }
 
@@ -506,7 +501,7 @@ func (t *test) run() {
 		t.err = fmt.Errorf("unimplemented action %q", action)
 
 	case "errorcheck":
-		cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter}
+		cmdline := []string{"go", "tool", "compile", "-e", "-o", "a." + letter}
 		cmdline = append(cmdline, flags...)
 		cmdline = append(cmdline, long)
 		out, err := runcmd(cmdline...)
@@ -669,7 +664,7 @@ func (t *test) run() {
 			t.err = fmt.Errorf("write tempfile:%s", err)
 			return
 		}
-		cmdline := []string{"go", "tool", gc, "-e", "-o", "a." + letter}
+		cmdline := []string{"go", "tool", "compile", "-e", "-o", "a." + letter}
 		cmdline = append(cmdline, flags...)
 		cmdline = append(cmdline, tfile)
 		out, err = runcmd(cmdline...)
diff --git a/test/sinit_run.go b/test/sinit_run.go
index b0a91ce5b1..cc437bfacf 100644
--- a/test/sinit_run.go
+++ b/test/sinit_run.go
@@ -24,7 +24,7 @@ func main() {
 		os.Exit(1)
 	}
 
-	cmd := exec.Command("go", "tool", letter+"g", "-S", "sinit.go")
+	cmd := exec.Command("go", "tool", "compile", "-S", "sinit.go")
 	out, err := cmd.CombinedOutput()
 	if err != nil {
 		fmt.Println(string(out))

From cf932cd897a3eaf3aa6fec3ba5d0ab1d9107eebb Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 21 May 2015 13:28:17 -0400
Subject: [PATCH 175/232] all: retire architecture letter in file names, public
 API

This CL removes the remaining visible uses of the "architecture letter" concept.
(They are no longer in tool names nor in source directory names.)

Because the architecture letter concept is now gone, delete GOCHAR
from "go env" output, and change go/build.ArchChar to return an
error always.

The architecture letter is still used in the compiler and linker sources
as a clumsy architecture enumeration, but that use is not visible to
Go users and can be cleaned up separately.

Change-Id: I4d97a38f372003fb610c9c5241bea440d9dbeb8d
Reviewed-on: https://go-review.googlesource.com/10289
Reviewed-by: Rob Pike 
---
 doc/go1.5.txt                                 |  2 +
 src/cmd/asm/internal/flags/flags.go           |  4 +-
 src/cmd/asm/main.go                           |  2 +-
 src/cmd/compile/internal/gc/lex.go            | 12 ++--
 src/cmd/compile/internal/gc/mkbuiltin.go      | 17 ++----
 src/cmd/compile/internal/gc/obj.go            |  3 +-
 src/cmd/dist/build.go                         | 51 +++-------------
 src/cmd/go/build.go                           | 56 +++++++-----------
 src/cmd/go/env.go                             |  4 --
 src/cmd/link/internal/ld/lib.go               |  2 +-
 src/cmd/link/internal/ld/pobj.go              |  5 +-
 src/cmd/pack/pack_test.go                     | 25 +-------
 src/go/build/build.go                         | 21 ++-----
 src/go/internal/gcimporter/gcimporter.go      |  2 +-
 src/go/internal/gcimporter/gcimporter_test.go |  3 +-
 test/bench/shootout/timing.sh                 | 59 +++++++++----------
 test/fixedbugs/bug248.go                      | 16 ++---
 test/fixedbugs/bug302.go                      | 14 +----
 test/fixedbugs/bug345.go                      |  6 +-
 test/fixedbugs/bug369.go                      | 20 +++----
 test/fixedbugs/issue9355.go                   |  7 +--
 test/run.go                                   | 13 +---
 test/sinit_run.go                             |  9 +--
 23 files changed, 114 insertions(+), 239 deletions(-)

diff --git a/doc/go1.5.txt b/doc/go1.5.txt
index 54a4c6e349..171c1601f0 100644
--- a/doc/go1.5.txt
+++ b/doc/go1.5.txt
@@ -1,6 +1,8 @@
 Overall:
 - toolchain in Go
 - new GC
+- go tool asm, go tool compile, go tool link
+- default output files changed: now file.o and a.out
 
 Language:
 - permit omission of key type in map composite literals where key is a composite literal (https://golang.org/cl/2591)
diff --git a/src/cmd/asm/internal/flags/flags.go b/src/cmd/asm/internal/flags/flags.go
index c74f26974a..bf5cb1eef3 100644
--- a/src/cmd/asm/internal/flags/flags.go
+++ b/src/cmd/asm/internal/flags/flags.go
@@ -51,7 +51,7 @@ func Usage() {
 	os.Exit(2)
 }
 
-func Parse(theChar int) {
+func Parse() {
 	flag.Usage = Usage
 	flag.Parse()
 	if flag.NArg() != 1 {
@@ -64,6 +64,6 @@ func Parse(theChar int) {
 		if strings.HasSuffix(input, ".s") {
 			input = input[:len(input)-2]
 		}
-		*OutputFile = fmt.Sprintf("%s.%c", input, theChar)
+		*OutputFile = fmt.Sprintf("%s.o", input)
 	}
 }
diff --git a/src/cmd/asm/main.go b/src/cmd/asm/main.go
index 32bdee6624..db0e28e2e5 100644
--- a/src/cmd/asm/main.go
+++ b/src/cmd/asm/main.go
@@ -29,7 +29,7 @@ func main() {
 		log.Fatalf("asm: unrecognized architecture %s", GOARCH)
 	}
 
-	flags.Parse(architecture.Thechar)
+	flags.Parse()
 
 	// Create object file, write header.
 	fd, err := os.Create(*flags.OutputFile)
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index f9211407fb..3b93207ef1 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -583,7 +583,7 @@ func findpkg(name string) (file string, ok bool) {
 		if obj.Access(file, 0) >= 0 {
 			return file, true
 		}
-		file = fmt.Sprintf("%s.%c", name, Thearch.Thechar)
+		file = fmt.Sprintf("%s.o", name)
 		if obj.Access(file, 0) >= 0 {
 			return file, true
 		}
@@ -605,7 +605,7 @@ func findpkg(name string) (file string, ok bool) {
 		if obj.Access(file, 0) >= 0 {
 			return file, true
 		}
-		file = fmt.Sprintf("%s/%s.%c", p.dir, name, Thearch.Thechar)
+		file = fmt.Sprintf("%s/%s.o", p.dir, name)
 		if obj.Access(file, 0) >= 0 {
 			return file, true
 		}
@@ -626,7 +626,7 @@ func findpkg(name string) (file string, ok bool) {
 		if obj.Access(file, 0) >= 0 {
 			return file, true
 		}
-		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.%c", goroot, goos, goarch, suffixsep, suffix, name, Thearch.Thechar)
+		file = fmt.Sprintf("%s/pkg/%s_%s%s%s/%s.o", goroot, goos, goarch, suffixsep, suffix, name)
 		if obj.Access(file, 0) >= 0 {
 			return file, true
 		}
@@ -637,7 +637,7 @@ func findpkg(name string) (file string, ok bool) {
 
 func fakeimport() {
 	importpkg = mkpkg("fake")
-	cannedimports("fake.6", "$$\n")
+	cannedimports("fake.o", "$$\n")
 }
 
 func importfile(f *Val, line int) {
@@ -679,7 +679,7 @@ func importfile(f *Val, line int) {
 		}
 
 		importpkg = mkpkg(f.U.(string))
-		cannedimports("unsafe.6", unsafeimport)
+		cannedimports("unsafe.o", unsafeimport)
 		imported_unsafe = 1
 		return
 	}
@@ -2596,6 +2596,6 @@ func mkpackage(pkgname string) {
 		if i := strings.LastIndex(p, "."); i >= 0 {
 			p = p[:i]
 		}
-		outfile = fmt.Sprintf("%s.%c", p, Thearch.Thechar)
+		outfile = fmt.Sprintf("%s.o", p)
 	}
 }
diff --git a/src/cmd/compile/internal/gc/mkbuiltin.go b/src/cmd/compile/internal/gc/mkbuiltin.go
index b2362a6f01..f4569b48c2 100644
--- a/src/cmd/compile/internal/gc/mkbuiltin.go
+++ b/src/cmd/compile/internal/gc/mkbuiltin.go
@@ -13,21 +13,14 @@ package main
 import (
 	"bufio"
 	"fmt"
-	"go/build"
 	"io"
 	"log"
 	"os"
 	"os/exec"
-	"runtime"
 	"strings"
 )
 
 func main() {
-	gochar, err := build.ArchChar(runtime.GOARCH)
-	if err != nil {
-		log.Fatal(err)
-	}
-
 	f, err := os.Create("builtin.go")
 	if err != nil {
 		log.Fatal(err)
@@ -40,7 +33,7 @@ func main() {
 	fmt.Fprintln(w, "package gc")
 
 	for _, name := range os.Args[1:] {
-		mkbuiltin(w, gochar, name)
+		mkbuiltin(w, name)
 	}
 
 	if err := w.Flush(); err != nil {
@@ -49,11 +42,11 @@ func main() {
 }
 
 // Compile .go file, import data from .6 file, and write Go string version.
-func mkbuiltin(w io.Writer, gochar string, name string) {
-	if err := exec.Command("go", "tool", gochar+"g", "-A", "builtin/"+name+".go").Run(); err != nil {
+func mkbuiltin(w io.Writer, name string) {
+	if err := exec.Command("go", "tool", "compile", "-A", "builtin/"+name+".go").Run(); err != nil {
 		log.Fatal(err)
 	}
-	obj := fmt.Sprintf("%s.%s", name, gochar)
+	obj := "name.o"
 	defer os.Remove(obj)
 
 	r, err := os.Open(obj)
@@ -77,7 +70,7 @@ Begin:
 	fmt.Fprintf(w, "\nconst %simport = \"\" +\n", name)
 
 	// sys.go claims to be in package PACKAGE to avoid
-	// conflicts during "6g sys.go".  Rename PACKAGE to $2.
+	// conflicts during "go tool compile sys.go".  Rename PACKAGE to $2.
 	replacer := strings.NewReplacer("PACKAGE", name)
 
 	// Process imports, stopping at $$ that closes them.
diff --git a/src/cmd/compile/internal/gc/obj.go b/src/cmd/compile/internal/gc/obj.go
index 2afd786dc1..9bb334ca34 100644
--- a/src/cmd/compile/internal/gc/obj.go
+++ b/src/cmd/compile/internal/gc/obj.go
@@ -102,8 +102,7 @@ func dumpobj() {
 			obj.Bputc(bout, 0)
 		}
 		obj.Bseek(bout, startobj-ArhdrSize, 0)
-		name := fmt.Sprintf("_go_.%c", Thearch.Thechar)
-		formathdr(arhdr[:], name, size)
+		formathdr(arhdr[:], "_go_.o", size)
 		bout.Write(arhdr[:])
 	}
 
diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go
index d6cfaf02cb..0cdb7d69f7 100644
--- a/src/cmd/dist/build.go
+++ b/src/cmd/dist/build.go
@@ -20,7 +20,6 @@ var (
 	goarch           string
 	gobin            string
 	gohostarch       string
-	gohostchar       string
 	gohostos         string
 	goos             string
 	goarm            string
@@ -30,10 +29,8 @@ var (
 	goextlinkenabled string
 	workdir          string
 	tooldir          string
-	gochar           string
 	oldgoos          string
 	oldgoarch        string
-	oldgochar        string
 	slash            string
 	exe              string
 	defaultcc        string
@@ -48,17 +45,13 @@ var (
 	vflag int  // verbosity
 )
 
-// The known architecture letters.
-var gochars = "5667899"
-
 // The known architectures.
 var okgoarch = []string{
-	// same order as gochars
-	"arm",
+	"386",
 	"amd64",
 	"amd64p32",
+	"arm",
 	"arm64",
-	"386",
 	"ppc64",
 	"ppc64le",
 }
@@ -147,22 +140,18 @@ func xinit() {
 		gohostarch = b
 	}
 
-	i := find(gohostarch, okgoarch)
-	if i < 0 {
+	if find(gohostarch, okgoarch) < 0 {
 		fatal("unknown $GOHOSTARCH %s", gohostarch)
 	}
-	gohostchar = gochars[i : i+1]
 
 	b = os.Getenv("GOARCH")
 	if b == "" {
 		b = gohostarch
 	}
 	goarch = b
-	i = find(goarch, okgoarch)
-	if i < 0 {
+	if find(goarch, okgoarch) < 0 {
 		fatal("unknown $GOARCH %s", goarch)
 	}
-	gochar = gochars[i : i+1]
 
 	b = os.Getenv("GO_EXTLINK_ENABLED")
 	if b != "" {
@@ -436,7 +425,7 @@ func setup() {
 	}
 
 	// If $GOBIN is set and has a Go compiler, it must be cleaned.
-	for _, char := range gochars {
+	for _, char := range "56789" {
 		if isfile(pathf("%s%s%c%s", gobin, slash, char, "g")) {
 			for _, old := range oldtool {
 				xremove(pathf("%s/%s", gobin, old))
@@ -703,11 +692,7 @@ func install(dir string) {
 		b := pathf("%s/%s", workdir, filepath.Base(p))
 
 		// Change the last character of the output file (which was c or s).
-		if gohostos == "plan9" {
-			b = b[:len(b)-1] + gohostchar
-		} else {
-			b = b[:len(b)-1] + "o"
-		}
+		b = b[:len(b)-1] + "o"
 		compile = append(compile, "-o", b, p)
 		bgrun(path, compile...)
 
@@ -1035,7 +1020,6 @@ func cmdenv() {
 	xprintf(format, "GOHOSTARCH", gohostarch)
 	xprintf(format, "GOHOSTOS", gohostos)
 	xprintf(format, "GOTOOLDIR", tooldir)
-	xprintf(format, "GOCHAR", gochar)
 	if goarch == "arm" {
 		xprintf(format, "GOARM", goarm)
 	}
@@ -1080,10 +1064,8 @@ func cmdbootstrap() {
 	// For the main bootstrap, building for host os/arch.
 	oldgoos = goos
 	oldgoarch = goarch
-	oldgochar = gochar
 	goos = gohostos
 	goarch = gohostarch
-	gochar = gohostchar
 	os.Setenv("GOHOSTARCH", gohostarch)
 	os.Setenv("GOHOSTOS", gohostos)
 	os.Setenv("GOARCH", goarch)
@@ -1097,37 +1079,22 @@ func cmdbootstrap() {
 	// than in a standard release like Go 1.4, so don't do this rebuild by default.
 	if false {
 		xprintf("##### Building Go toolchain using itself.\n")
-		for _, pattern := range buildorder {
-			if pattern == "cmd/go" {
+		for _, dir := range buildorder {
+			if dir == "cmd/go" {
 				break
 			}
-			dir := pattern
-			if strings.Contains(pattern, "%s") {
-				dir = fmt.Sprintf(pattern, gohostchar)
-			}
 			install(dir)
-			if oldgochar != gohostchar && strings.Contains(pattern, "%s") {
-				install(fmt.Sprintf(pattern, oldgochar))
-			}
 		}
 		xprintf("\n")
 	}
 
 	xprintf("##### Building compilers and go_bootstrap for host, %s/%s.\n", gohostos, gohostarch)
-	for _, pattern := range buildorder {
-		dir := pattern
-		if strings.Contains(pattern, "%s") {
-			dir = fmt.Sprintf(pattern, gohostchar)
-		}
+	for _, dir := range buildorder {
 		install(dir)
-		if oldgochar != gohostchar && strings.Contains(pattern, "%s") {
-			install(fmt.Sprintf(pattern, oldgochar))
-		}
 	}
 
 	goos = oldgoos
 	goarch = oldgoarch
-	gochar = oldgochar
 	os.Setenv("GOARCH", goarch)
 	os.Setenv("GOOS", goos)
 
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index aa9a408eff..17ff7e0cbb 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -547,9 +547,6 @@ var (
 	goarch    string
 	goos      string
 	exeSuffix string
-
-	archCharVal string
-	archCharErr error
 )
 
 func init() {
@@ -558,16 +555,6 @@ func init() {
 	if goos == "windows" {
 		exeSuffix = ".exe"
 	}
-	archCharVal, archCharErr = build.ArchChar(goarch)
-}
-
-// archChar returns the architecture character.  This is only needed
-// for the gc toolchain, so only fail if we actually need it.
-func archChar() string {
-	if archCharErr != nil {
-		fatalf("%s", archCharErr)
-	}
-	return archCharVal
 }
 
 // A builder holds global state about a build.
@@ -1208,7 +1195,7 @@ func (b *builder) build(a *action) (err error) {
 		fmt.Fprintf(os.Stderr, "%s\n", a.p.ImportPath)
 	}
 
-	if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" && archChar() != "" &&
+	if a.p.Standard && a.p.ImportPath == "runtime" && buildContext.Compiler == "gc" &&
 		(!hasString(a.p.GoFiles, "zgoos_"+buildContext.GOOS+".go") ||
 			!hasString(a.p.GoFiles, "zgoarch_"+buildContext.GOARCH+".go")) {
 		return fmt.Errorf("%s/%s must be bootstrapped using make%v", buildContext.GOOS, buildContext.GOARCH, defaultSuffix())
@@ -1371,15 +1358,8 @@ func (b *builder) build(a *action) (err error) {
 		}
 	}
 
-	var objExt string
-	if _, ok := buildToolchain.(gccgoToolchain); ok {
-		objExt = "o"
-	} else {
-		objExt = archChar()
-	}
-
 	for _, file := range cfiles {
-		out := file[:len(file)-len(".c")] + "." + objExt
+		out := file[:len(file)-len(".c")] + ".o"
 		if err := buildToolchain.cc(b, a.p, obj, obj+out, file); err != nil {
 			return err
 		}
@@ -1388,7 +1368,7 @@ func (b *builder) build(a *action) (err error) {
 
 	// Assemble .s files.
 	for _, file := range sfiles {
-		out := file[:len(file)-len(".s")] + "." + objExt
+		out := file[:len(file)-len(".s")] + ".o"
 		if err := buildToolchain.asm(b, a.p, obj, obj+out, file); err != nil {
 			return err
 		}
@@ -2120,7 +2100,7 @@ func (gcToolchain) gc(b *builder, p *Package, archive, obj string, asmhdr bool,
 	if archive != "" {
 		ofile = archive
 	} else {
-		out := "_go_." + archChar()
+		out := "_go_.o"
 		ofile = obj + out
 	}
 
@@ -2182,9 +2162,22 @@ func (gcToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
 	}
 	// Disable checks when additional flags are passed, as the old assemblers
 	// don't implement some of them (e.g., -shared).
-	if verifyAsm && goarch != "arm64" && len(buildAsmflags) == 0 {
-		if err := toolVerify(b, p, "old"+archChar()+"a", ofile, args); err != nil {
-			return err
+	if verifyAsm && len(buildAsmflags) == 0 {
+		old := ""
+		switch goarch {
+		case "arm":
+			old = "old5a"
+		case "amd64", "amd64p32":
+			old = "old6a"
+		case "386":
+			old = "old8a"
+		case "ppc64", "ppc64le":
+			old = "old9a"
+		}
+		if old != "" {
+			if err := toolVerify(b, p, old, ofile, args); err != nil {
+				return err
+			}
 		}
 	}
 	return nil
@@ -2785,13 +2778,6 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
 	cgoflags := []string{}
 	// TODO: make cgo not depend on $GOARCH?
 
-	var objExt string
-	if _, ok := buildToolchain.(gccgoToolchain); ok {
-		objExt = "o"
-	} else {
-		objExt = archChar()
-	}
-
 	if p.Standard && p.ImportPath == "runtime/cgo" {
 		cgoflags = append(cgoflags, "-import_runtime_cgo=false")
 	}
@@ -2836,7 +2822,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, pcCFLAGS, pcLDFLAGS, cgofi
 	// cc _cgo_defun.c
 	_, gccgo := buildToolchain.(gccgoToolchain)
 	if gccgo {
-		defunObj := obj + "_cgo_defun." + objExt
+		defunObj := obj + "_cgo_defun.o"
 		if err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {
 			return nil, nil, err
 		}
diff --git a/src/cmd/go/env.go b/src/cmd/go/env.go
index 863eb4d26e..8d427b37c2 100644
--- a/src/cmd/go/env.go
+++ b/src/cmd/go/env.go
@@ -49,10 +49,6 @@ func mkEnv() []envVar {
 		{"TERM", "dumb"},
 	}
 
-	if archCharErr == nil {
-		env = append(env, envVar{"GOCHAR", archChar()})
-	}
-
 	if goos != "plan9" {
 		cmd := b.gccCmd(".")
 		env = append(env, envVar{"CC", cmd[0]})
diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index a36cd0f8f4..6cf0b525e5 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1627,7 +1627,7 @@ func Cput(c uint8) {
 }
 
 func usage() {
-	fmt.Fprintf(os.Stderr, "usage: %cl [options] obj.%c\n", Thearch.Thechar, Thearch.Thechar)
+	fmt.Fprintf(os.Stderr, "usage: link [options] main.o\n")
 	obj.Flagprint(2)
 	Exit(2)
 }
diff --git a/src/cmd/link/internal/ld/pobj.go b/src/cmd/link/internal/ld/pobj.go
index 8568744c3d..5b24428059 100644
--- a/src/cmd/link/internal/ld/pobj.go
+++ b/src/cmd/link/internal/ld/pobj.go
@@ -160,10 +160,9 @@ func Ldmain() {
 	}
 
 	if outfile == "" {
+		outfile = "a.out"
 		if HEADTYPE == obj.Hwindows {
-			outfile = fmt.Sprintf("%c.out.exe", Thearch.Thechar)
-		} else {
-			outfile = fmt.Sprintf("%c.out", Thearch.Thechar)
+			outfile += ".exe"
 		}
 	}
 
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go
index 97992059b9..cd32020501 100644
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -13,7 +13,6 @@ import (
 	"os"
 	"os/exec"
 	"path/filepath"
-	"regexp"
 	"runtime"
 	"testing"
 	"time"
@@ -223,15 +222,13 @@ func TestHello(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	char := findChar(t, dir)
-
 	run := func(args ...string) string {
 		return doRun(t, dir, args...)
 	}
 
 	run("go", "build", "cmd/pack") // writes pack binary to dir
 	run("go", "tool", "compile", "hello.go")
-	run("./pack", "grc", "hello.a", "hello."+char)
+	run("./pack", "grc", "hello.a", "hello.o")
 	run("go", "tool", "link", "-o", "a.out", "hello.a")
 	out := run("./a.out")
 	if out != "hello world\n" {
@@ -297,17 +294,15 @@ func TestLargeDefs(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	char := findChar(t, dir)
-
 	run := func(args ...string) string {
 		return doRun(t, dir, args...)
 	}
 
 	run("go", "build", "cmd/pack") // writes pack binary to dir
 	run("go", "tool", "compile", "large.go")
-	run("./pack", "grc", "large.a", "large."+char)
+	run("./pack", "grc", "large.a", "large.o")
 	run("go", "tool", "compile", "-I", ".", "main.go")
-	run("go", "tool", "link", "-L", ".", "-o", "a.out", "main."+char)
+	run("go", "tool", "link", "-L", ".", "-o", "a.out", "main.o")
 	out := run("./a.out")
 	if out != "ok\n" {
 		t.Fatalf("incorrect output: %q, want %q", out, "ok\n")
@@ -325,20 +320,6 @@ func doRun(t *testing.T, dir string, args ...string) string {
 	return string(out)
 }
 
-// findChar returns the architecture character for the go command.
-func findChar(t *testing.T, dir string) string {
-	out := doRun(t, dir, "go", "env")
-	re, err := regexp.Compile(`\s*GOCHAR=['"]?(\w)['"]?`)
-	if err != nil {
-		t.Fatal(err)
-	}
-	fields := re.FindStringSubmatch(out)
-	if fields == nil {
-		t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
-	}
-	return fields[1]
-}
-
 // Fake implementation of files.
 
 var helloFile = &FakeFile{
diff --git a/src/go/build/build.go b/src/go/build/build.go
index 820434bc4a..1fd06b5d92 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -1391,20 +1391,11 @@ func IsLocalImport(path string) bool {
 		strings.HasPrefix(path, "./") || strings.HasPrefix(path, "../")
 }
 
-// ArchChar returns the architecture character for the given goarch.
-// For example, ArchChar("amd64") returns "6".
+// ArchChar returns "?" and an error.
+// In earlier versions of Go, the returned string was used to derive
+// the compiler and linker tool names, the default object file suffix,
+// and the default linker output name. As of Go 1.5, those strings
+// no longer vary by architecture; they are compile, link, .o, and a.out, respectively.
 func ArchChar(goarch string) (string, error) {
-	switch goarch {
-	case "386":
-		return "8", nil
-	case "amd64", "amd64p32":
-		return "6", nil
-	case "arm":
-		return "5", nil
-	case "arm64":
-		return "7", nil
-	case "ppc64", "ppc64le":
-		return "9", nil
-	}
-	return "", errors.New("unsupported GOARCH " + goarch)
+	return "", errors.New("architecture letter no longer used")
 }
diff --git a/src/go/internal/gcimporter/gcimporter.go b/src/go/internal/gcimporter/gcimporter.go
index ec71d793bd..7278c0c0a0 100644
--- a/src/go/internal/gcimporter/gcimporter.go
+++ b/src/go/internal/gcimporter/gcimporter.go
@@ -25,7 +25,7 @@ import (
 // debugging/development support
 const debug = false
 
-var pkgExts = [...]string{".a", ".5", ".6", ".7", ".8", ".9"}
+var pkgExts = [...]string{".a", ".o"}
 
 // FindPkg returns the filename and unique package id for an import
 // path based on package information provided by build.Import (using
diff --git a/src/go/internal/gcimporter/gcimporter_test.go b/src/go/internal/gcimporter/gcimporter_test.go
index 5d4de39712..fe4a758cd4 100644
--- a/src/go/internal/gcimporter/gcimporter_test.go
+++ b/src/go/internal/gcimporter/gcimporter_test.go
@@ -50,9 +50,8 @@ func compile(t *testing.T, dirname, filename string) string {
 		t.Logf("%s", out)
 		t.Fatalf("%s %s failed: %s", gcPath, filename, err)
 	}
-	archCh, _ := build.ArchChar(runtime.GOARCH)
 	// filename should end with ".go"
-	return filepath.Join(dirname, filename[:len(filename)-2]+archCh)
+	return filepath.Join(dirname, filename[:len(filename)-2]+"o")
 }
 
 // Use the same global imports map for all tests. The effect is
diff --git a/test/bench/shootout/timing.sh b/test/bench/shootout/timing.sh
index b15825cc68..d8b1486480 100755
--- a/test/bench/shootout/timing.sh
+++ b/test/bench/shootout/timing.sh
@@ -6,7 +6,6 @@
 set -e
 
 eval $(go tool dist env)
-O=$GOCHAR
 GC="go tool compile"
 LD="go tool link"
 
@@ -61,11 +60,11 @@ X-test)
 esac
 
 gc() {
-	$GC $1.go; $LD -o $O.$EXE $1.$O
+	$GC $1.go; $LD -o a.$EXE $1.o
 }
 
 gc_B() {
-	$GC -B $1.go; $LD -o $O.$EXE $1.$O
+	$GC -B $1.go; $LD -o a.$EXE $1.o
 }
 
 runonly() {
@@ -115,8 +114,8 @@ fasta() {
 	runonly echo 'fasta -n 25000000'
 	run "gcc $gccm -O2 fasta.c" a.$EXE 25000000
 	run 'gccgo -O2 fasta.go' a.$EXE -n 25000000	#commented out until WriteString is in bufio
-	run 'gc fasta' $O.$EXE -n 25000000
-	run 'gc_B fasta' $O.$EXE -n 25000000
+	run 'gc fasta' a.$EXE -n 25000000
+	run 'gc_B fasta' a.$EXE -n 25000000
 }
 
 revcomp() {
@@ -125,8 +124,8 @@ revcomp() {
 	runonly echo 'reverse-complement < output-of-fasta-25000000'
 	run "gcc $gccm -O2 reverse-complement.c" a.$EXE < x
 	run 'gccgo -O2 reverse-complement.go' a.$EXE < x
-	run 'gc reverse-complement' $O.$EXE < x
-	run 'gc_B reverse-complement' $O.$EXE < x
+	run 'gc reverse-complement' a.$EXE < x
+	run 'gc_B reverse-complement' a.$EXE < x
 	rm x
 }
 
@@ -134,8 +133,8 @@ nbody() {
 	runonly echo 'nbody -n 50000000'
 	run "gcc $gccm -O2 nbody.c -lm" a.$EXE 50000000
 	run 'gccgo -O2 nbody.go' a.$EXE -n 50000000
-	run 'gc nbody' $O.$EXE -n 50000000
-	run 'gc_B nbody' $O.$EXE -n 50000000
+	run 'gc nbody' a.$EXE -n 50000000
+	run 'gc_B nbody' a.$EXE -n 50000000
 }
 
 binarytree() {
@@ -143,8 +142,8 @@ binarytree() {
 	run "gcc $gccm -O2 binary-tree.c -lm" a.$EXE 15
 	run 'gccgo -O2 binary-tree.go' a.$EXE -n 15
 	run 'gccgo -O2 binary-tree-freelist.go' a.$EXE -n 15
-	run 'gc binary-tree' $O.$EXE -n 15
-	run 'gc binary-tree-freelist' $O.$EXE -n 15
+	run 'gc binary-tree' a.$EXE -n 15
+	run 'gc binary-tree-freelist' a.$EXE -n 15
 }
 
 fannkuch() {
@@ -152,9 +151,9 @@ fannkuch() {
 	run "gcc $gccm -O2 fannkuch.c" a.$EXE 12
 	run 'gccgo -O2 fannkuch.go' a.$EXE -n 12
 	run 'gccgo -O2 fannkuch-parallel.go' a.$EXE -n 12
-	run 'gc fannkuch' $O.$EXE -n 12
-	run 'gc fannkuch-parallel' $O.$EXE -n 12
-	run 'gc_B fannkuch' $O.$EXE -n 12
+	run 'gc fannkuch' a.$EXE -n 12
+	run 'gc fannkuch-parallel' a.$EXE -n 12
+	run 'gc_B fannkuch' a.$EXE -n 12
 }
 
 regexdna() {
@@ -166,9 +165,9 @@ regexdna() {
 	fi
 	run 'gccgo -O2 regex-dna.go' a.$EXE  0 {
@@ -199,7 +192,7 @@ func compileInDir(runcmd runCmd, dir string, names ...string) (out []byte, err e
 }
 
 func linkFile(runcmd runCmd, goname string) (err error) {
-	pfile := strings.Replace(goname, ".go", "."+letter, -1)
+	pfile := strings.Replace(goname, ".go", ".o", -1)
 	_, err = runcmd("go", "tool", "link", "-w", "-o", "a.exe", "-L", ".", pfile)
 	return
 }
@@ -501,7 +494,7 @@ func (t *test) run() {
 		t.err = fmt.Errorf("unimplemented action %q", action)
 
 	case "errorcheck":
-		cmdline := []string{"go", "tool", "compile", "-e", "-o", "a." + letter}
+		cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"}
 		cmdline = append(cmdline, flags...)
 		cmdline = append(cmdline, long)
 		out, err := runcmd(cmdline...)
@@ -664,7 +657,7 @@ func (t *test) run() {
 			t.err = fmt.Errorf("write tempfile:%s", err)
 			return
 		}
-		cmdline := []string{"go", "tool", "compile", "-e", "-o", "a." + letter}
+		cmdline := []string{"go", "tool", "compile", "-e", "-o", "a.o"}
 		cmdline = append(cmdline, flags...)
 		cmdline = append(cmdline, tfile)
 		out, err = runcmd(cmdline...)
diff --git a/test/sinit_run.go b/test/sinit_run.go
index cc437bfacf..c9afd3b777 100644
--- a/test/sinit_run.go
+++ b/test/sinit_run.go
@@ -12,18 +12,11 @@ package main
 import (
 	"bytes"
 	"fmt"
-	"go/build"
 	"os"
 	"os/exec"
 )
 
 func main() {
-	letter, err := build.ArchChar(build.Default.GOARCH)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-
 	cmd := exec.Command("go", "tool", "compile", "-S", "sinit.go")
 	out, err := cmd.CombinedOutput()
 	if err != nil {
@@ -31,7 +24,7 @@ func main() {
 		fmt.Println(err)
 		os.Exit(1)
 	}
-	os.Remove("sinit." + letter)
+	os.Remove("sinit.o")
 
 	if bytes.Contains(out, []byte("initdone")) {
 		fmt.Println("sinit generated an init function")

From 216e5c747d28676beea2287f48a0771fa56bbaf8 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 21 May 2015 13:40:17 -0400
Subject: [PATCH 176/232] cmd/go: set correct install location for cmd/compile
 and cmd/link

Without this, they install to $GOROOT/bin.

Change-Id: Iae4b8f59c8392f6abd841490e56922738089f8d4
Reviewed-on: https://go-review.googlesource.com/10297
Reviewed-by: Russ Cox 
---
 src/cmd/go/pkg.go | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 41e66ef9c9..7a71471340 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -394,24 +394,16 @@ const (
 
 // goTools is a map of Go program import path to install target directory.
 var goTools = map[string]targetDir{
-	"cmd/5g":                               toTool,
-	"cmd/5l":                               toTool,
-	"cmd/6g":                               toTool,
-	"cmd/6l":                               toTool,
-	"cmd/7g":                               toTool,
-	"cmd/7l":                               toTool,
-	"cmd/8g":                               toTool,
-	"cmd/8l":                               toTool,
-	"cmd/9g":                               toTool,
-	"cmd/9l":                               toTool,
 	"cmd/addr2line":                        toTool,
 	"cmd/api":                              toTool,
 	"cmd/asm":                              toTool,
+	"cmd/compile":                          toTool,
 	"cmd/cgo":                              toTool,
 	"cmd/cover":                            toTool,
 	"cmd/dist":                             toTool,
 	"cmd/doc":                              toTool,
 	"cmd/fix":                              toTool,
+	"cmd/link":                             toTool,
 	"cmd/newlink":                          toTool,
 	"cmd/nm":                               toTool,
 	"cmd/objdump":                          toTool,

From 137817378548b263f0322d8240a0c503b456f696 Mon Sep 17 00:00:00 2001
From: Josh Bleecher Snyder 
Date: Wed, 20 May 2015 17:35:43 -0700
Subject: [PATCH 177/232] cmd/internal/obj: remove F3t field from Prog

F3t was effectively a local variable.
Remove it.

This shrinks obj.Prog from 456 to 448 bytes,
which places it in a smaller malloc class.

This reduces the memory usage of the compiler
while compiling the rotate tests by ~2.75%.

Change-Id: I31cc9dd67269851a430b56bcc7d255c9349eb522
Reviewed-on: https://go-review.googlesource.com/10255
Reviewed-by: Brad Fitzpatrick 
---
 src/cmd/internal/obj/link.go     | 1 -
 src/cmd/internal/obj/x86/asm6.go | 5 +----
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go
index 9f5e87b4c3..b0c7a55ca1 100644
--- a/src/cmd/internal/obj/link.go
+++ b/src/cmd/internal/obj/link.go
@@ -222,7 +222,6 @@ type Prog struct {
 	Scond    uint8
 	Back     uint8
 	Ft       uint8
-	F3t      uint8
 	Tt       uint8
 	Isize    uint8
 	Printed  uint8
diff --git a/src/cmd/internal/obj/x86/asm6.go b/src/cmd/internal/obj/x86/asm6.go
index 0c0cc04548..2b9c2670df 100644
--- a/src/cmd/internal/obj/x86/asm6.go
+++ b/src/cmd/internal/obj/x86/asm6.go
@@ -2974,15 +2974,12 @@ func doasm(ctxt *obj.Link, p *obj.Prog) {
 	if p.Ft == 0 {
 		p.Ft = uint8(oclass(ctxt, p, &p.From))
 	}
-	if p.F3t == 0 {
-		p.F3t = uint8(oclass(ctxt, p, &p.From3))
-	}
 	if p.Tt == 0 {
 		p.Tt = uint8(oclass(ctxt, p, &p.To))
 	}
 
 	ft := int(p.Ft) * Ymax
-	f3t := int(p.F3t) * Ymax
+	f3t := oclass(ctxt, p, &p.From3) * Ymax
 	tt := int(p.Tt) * Ymax
 
 	xo := obj.Bool2int(o.op[0] == 0x0f)

From a5c3bbe0b49af8c3d31e511c6ee4c9380696e40d Mon Sep 17 00:00:00 2001
From: Austin Clements 
Date: Wed, 20 May 2015 11:57:02 -0400
Subject: [PATCH 178/232] runtime: eliminate write barrier from adjustpointers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Currently adjustpointers invokes a write barrier for every stack slot
it updates. This is safe---the write barrier always does nothing
because the new value is never a heap pointer---but it's unnecessary
overhead in performance and complexity.

Fix this by rewriting adjustpointers to work with *uintptrs instead of
*unsafe.Pointers. As an added bonus, this makes the code cleaner.

name                   old mean              new mean              delta
BinaryTree17            3.35s × (0.98,1.01)   3.33s × (0.99,1.02)    ~    (p=0.095 n=20+19)
Fannkuch11              2.49s × (1.00,1.01)   2.52s × (0.99,1.01)  +1.23% (p=0.000 n=19+20)
FmtFprintfEmpty        52.2ns × (0.99,1.02)  52.2ns × (0.99,1.02)    ~    (p=0.766 n=19+19)
FmtFprintfString        181ns × (0.99,1.02)   179ns × (0.99,1.01)  -1.06% (p=0.000 n=20+19)
FmtFprintfInt           177ns × (0.99,1.01)   173ns × (0.99,1.02)  -2.26% (p=0.000 n=17+20)
FmtFprintfIntInt        300ns × (0.99,1.01)   302ns × (0.99,1.01)  +0.76% (p=0.000 n=19+20)
FmtFprintfPrefixedInt   253ns × (0.99,1.02)   256ns × (0.99,1.01)  +0.96% (p=0.000 n=20+19)
FmtFprintfFloat         334ns × (0.99,1.02)   334ns × (1.00,1.01)    ~    (p=0.243 n=20+19)
FmtManyArgs            1.16µs × (0.99,1.01)  1.17µs × (0.99,1.02)  +0.88% (p=0.000 n=20+20)
GobDecode              9.16ms × (0.99,1.02)  9.18ms × (1.00,1.00)  +0.21% (p=0.048 n=20+17)
GobEncode              7.03ms × (0.99,1.01)  7.05ms × (0.99,1.01)    ~    (p=0.091 n=19+19)
Gzip                    374ms × (0.99,1.01)   372ms × (0.99,1.02)  -0.50% (p=0.008 n=18+20)
Gunzip                 92.9ms × (0.99,1.01)  92.5ms × (1.00,1.01)  -0.47% (p=0.002 n=19+19)
HTTPClientServer       53.1µs × (0.98,1.01)  52.5µs × (0.99,1.01)  -0.98% (p=0.000 n=20+19)
JSONEncode             17.4ms × (0.99,1.02)  17.5ms × (0.99,1.01)    ~    (p=0.061 n=19+20)
JSONDecode             66.0ms × (0.99,1.02)  64.7ms × (0.99,1.01)  -1.87% (p=0.000 n=20+20)
Mandelbrot200          3.94ms × (1.00,1.01)  3.95ms × (1.00,1.01)    ~    (p=0.799 n=18+19)
GoParse                3.89ms × (0.99,1.02)  3.86ms × (0.99,1.01)  -0.70% (p=0.016 n=20+19)
RegexpMatchEasy0_32     102ns × (0.99,1.02)   102ns × (1.00,1.01)    ~    (p=0.557 n=20+18)
RegexpMatchEasy0_1K     353ns × (0.99,1.02)   341ns × (0.99,1.01)  -3.38% (p=0.000 n=20+20)
RegexpMatchEasy1_32    85.0ns × (0.99,1.02)  85.0ns × (0.99,1.01)    ~    (p=0.851 n=19+20)
RegexpMatchEasy1_1K     521ns × (0.99,1.02)   506ns × (1.00,1.01)  -2.85% (p=0.000 n=20+18)
RegexpMatchMedium_32    142ns × (0.99,1.02)   141ns × (1.00,1.01)  -1.17% (p=0.000 n=20+19)
RegexpMatchMedium_1K   42.8µs × (0.99,1.01)  42.3µs × (0.99,1.01)  -1.07% (p=0.000 n=20+19)
RegexpMatchHard_32     2.17µs × (0.99,1.01)  2.16µs × (1.00,1.01)  -0.51% (p=0.042 n=20+18)
RegexpMatchHard_1K     65.6µs × (0.99,1.01)  64.8µs × (1.00,1.00)  -1.21% (p=0.000 n=20+17)
Revcomp                 581ms × (0.99,1.04)   536ms × (1.00,1.01)  -7.71% (p=0.000 n=20+18)
Template               77.2ms × (0.99,1.01)  76.8ms × (0.99,1.01)    ~    (p=0.426 n=20+18)
TimeParse               369ns × (0.99,1.02)   371ns × (1.00,1.01)    ~    (p=0.117 n=20+19)
TimeFormat              371ns × (0.99,1.02)   391ns × (0.99,1.01)  +5.33% (p=0.000 n=20+19)

Change-Id: I5b952ba577ac4365c8c87db837c5804a1e30b7be
Reviewed-on: https://go-review.googlesource.com/10293
Reviewed-by: Russ Cox 
---
 src/runtime/stack1.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/runtime/stack1.go b/src/runtime/stack1.go
index d6ddf86dba..27427af955 100644
--- a/src/runtime/stack1.go
+++ b/src/runtime/stack1.go
@@ -387,20 +387,20 @@ func adjustpointers(scanp unsafe.Pointer, cbv *bitvector, adjinfo *adjustinfo, f
 			print("        ", add(scanp, i*ptrSize), ":", ptrnames[ptrbit(&bv, i)], ":", hex(*(*uintptr)(add(scanp, i*ptrSize))), " # ", i, " ", bv.bytedata[i/4], "\n")
 		}
 		if ptrbit(&bv, i) == 1 {
-			p := *(*unsafe.Pointer)(add(scanp, i*ptrSize))
-			up := uintptr(p)
-			if f != nil && 0 < up && up < _PageSize && debug.invalidptr != 0 || up == poisonStack {
+			pp := (*uintptr)(add(scanp, i*ptrSize))
+			p := *pp
+			if f != nil && 0 < p && p < _PageSize && debug.invalidptr != 0 || p == poisonStack {
 				// Looks like a junk value in a pointer slot.
 				// Live analysis wrong?
 				getg().m.traceback = 2
-				print("runtime: bad pointer in frame ", funcname(f), " at ", add(scanp, i*ptrSize), ": ", p, "\n")
+				print("runtime: bad pointer in frame ", funcname(f), " at ", pp, ": ", hex(p), "\n")
 				throw("invalid stack pointer")
 			}
-			if minp <= up && up < maxp {
+			if minp <= p && p < maxp {
 				if stackDebug >= 3 {
 					print("adjust ptr ", p, " ", funcname(f), "\n")
 				}
-				*(*unsafe.Pointer)(add(scanp, i*ptrSize)) = unsafe.Pointer(up + delta)
+				*pp = p + delta
 			}
 		}
 	}

From 001438bdfec2b97e04a053abfbe42efe499f78e5 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Tue, 19 May 2015 22:58:10 -0400
Subject: [PATCH 179/232] runtime: fix callwritebarrier

Given a call frame F of size N where the return values start at offset R,
callwritebarrier was instructing heapBitsBulkBarrier to scan the block
of memory [F+R, F+R+N). It should only scan [F+R, F+N). The extra N-R
bytes scanned might lead into the next allocated block in memory.
Because the scan was consulting the heap bitmap for type information,
scanning into the next block normally "just worked" in the sense of
not crashing.

Scanning the extra N-R bytes of memory is a problem mainly because
it causes the GC to consider pointers that might otherwise not be
considered, leading it to retain objects that should actually be freed.
This is very difficult to detect.

Luckily, juju turned up a case where the heap bitmap and the memory
were out of sync for the block immediately after the call frame, so that
heapBitsBulkBarrier saw an obvious non-pointer where it expected a
pointer, causing a loud crash.

Why is there a non-pointer in memory that the heap bitmap records as
a pointer? That is more difficult to answer. At least one way that it
could happen is that allocations containing no pointers at all do not
update the heap bitmap. So if heapBitsBulkBarrier walked out of the
current object and into a no-pointer object and consulted those bitmap
bits, it would be misled. This doesn't happen in general because all
the paths to heapBitsBulkBarrier first check for the no-pointer case.
This may or may not be what happened, but it's the only scenario
I've been able to construct.

I tried for quite a while to write a simple test for this and could not.
It does fix the juju crash, and it is clearly an improvement over the
old code.

Fixes #10844.

Change-Id: I53982c93ef23ef93155c4086bbd95a4c4fdaac9a
Reviewed-on: https://go-review.googlesource.com/10317
Reviewed-by: Austin Clements 
---
 src/runtime/mbarrier.go | 2 +-
 src/runtime/mbitmap.go  | 6 ++++++
 src/runtime/mheap.go    | 2 ++
 3 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/runtime/mbarrier.go b/src/runtime/mbarrier.go
index 77b50095a0..53a0a00ae7 100644
--- a/src/runtime/mbarrier.go
+++ b/src/runtime/mbarrier.go
@@ -195,7 +195,7 @@ func callwritebarrier(typ *_type, frame unsafe.Pointer, framesize, retoffset uin
 	if !writeBarrierEnabled || typ == nil || typ.kind&kindNoPointers != 0 || framesize-retoffset < ptrSize || !inheap(uintptr(frame)) {
 		return
 	}
-	heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize)
+	heapBitsBulkBarrier(uintptr(add(frame, retoffset)), framesize-retoffset)
 }
 
 //go:nosplit
diff --git a/src/runtime/mbitmap.go b/src/runtime/mbitmap.go
index 39bb4217b3..b20908fb49 100644
--- a/src/runtime/mbitmap.go
+++ b/src/runtime/mbitmap.go
@@ -356,6 +356,12 @@ func (h heapBits) setCheckmarked(size uintptr) {
 // calling memmove(p, src, size). This function is marked nosplit
 // to avoid being preempted; the GC must not stop the goroutine
 // betwen the memmove and the execution of the barriers.
+//
+// The heap bitmap is not maintained for allocations containing
+// no pointers at all; any caller of heapBitsBulkBarrier must first
+// make sure the underlying allocation contains pointers, usually
+// by checking typ.kind&kindNoPointers.
+//
 //go:nosplit
 func heapBitsBulkBarrier(p, size uintptr) {
 	if (p|size)&(ptrSize-1) != 0 {
diff --git a/src/runtime/mheap.go b/src/runtime/mheap.go
index a610da2e47..04fa050bc5 100644
--- a/src/runtime/mheap.go
+++ b/src/runtime/mheap.go
@@ -168,7 +168,9 @@ func recordspan(vh unsafe.Pointer, p unsafe.Pointer) {
 
 // inheap reports whether b is a pointer into a (potentially dead) heap object.
 // It returns false for pointers into stack spans.
+// Non-preemptible because it is used by write barriers.
 //go:nowritebarrier
+//go:nosplit
 func inheap(b uintptr) bool {
 	if b == 0 || b < mheap_.arena_start || b >= mheap_.arena_used {
 		return false

From 9279684908673448aacb4eaea71990ef96fce645 Mon Sep 17 00:00:00 2001
From: Vlad Krasnov 
Date: Wed, 22 Apr 2015 15:03:59 -0700
Subject: [PATCH 180/232] math/big: Simple Montgomery Multiplication to
 accelerate Mod-Exp

On Haswell I measure anywhere between 2X to 3.5X speedup for RSA.
I believe other architectures will also greatly improve.
In the future may be upgraded by dedicated assembly routine.

Built-in benchmarks i5-4278U turbo off:

benchmark                         old ns/op     new ns/op     delta
BenchmarkRSA2048Decrypt           6696649       3073769       -54.10%
Benchmark3PrimeRSA2048Decrypt     4472340       1669080       -62.68%

Change-Id: I17df84f85e34208f990665f9f90ea671695b2add
Reviewed-on: https://go-review.googlesource.com/9253
Reviewed-by: Brad Fitzpatrick 
Reviewed-by: Adam Langley 
Reviewed-by: Vlad Krasnov 
Run-TryBot: Adam Langley 
---
 src/math/big/arith_amd64.s |  28 +++++++++
 src/math/big/nat.go        | 114 ++++++++++++++++++++++++++++++++++++-
 src/math/big/nat_test.go   |  61 ++++++++++++++++++++
 3 files changed, 202 insertions(+), 1 deletion(-)

diff --git a/src/math/big/arith_amd64.s b/src/math/big/arith_amd64.s
index d2d5187a48..b69a2c616a 100644
--- a/src/math/big/arith_amd64.s
+++ b/src/math/big/arith_amd64.s
@@ -351,6 +351,34 @@ TEXT ·addMulVVW(SB),NOSPLIT,$0
 	MOVQ z_len+8(FP), R11
 	MOVQ $0, BX		// i = 0
 	MOVQ $0, CX		// c = 0
+	MOVQ R11, R12
+	ANDQ $-2, R12
+	CMPQ R11, $2
+	JAE A6
+	JMP E6
+
+A6:
+	MOVQ (R8)(BX*8), AX
+	MULQ R9
+	ADDQ (R10)(BX*8), AX
+	ADCQ $0, DX
+	ADDQ CX, AX
+	ADCQ $0, DX
+	MOVQ DX, CX
+	MOVQ AX, (R10)(BX*8)
+
+	MOVQ (8)(R8)(BX*8), AX
+	MULQ R9
+	ADDQ (8)(R10)(BX*8), AX
+	ADCQ $0, DX
+	ADDQ CX, AX
+	ADCQ $0, DX
+	MOVQ DX, CX
+	MOVQ AX, (8)(R10)(BX*8)
+
+	ADDQ $2, BX
+	CMPQ BX, R12
+	JL A6
 	JMP E6
 
 L6:	MOVQ (R8)(BX*8), AX
diff --git a/src/math/big/nat.go b/src/math/big/nat.go
index 7157a5487b..c3eef76fa1 100644
--- a/src/math/big/nat.go
+++ b/src/math/big/nat.go
@@ -216,6 +216,34 @@ func basicMul(z, x, y nat) {
 	}
 }
 
+// montgomery computes x*y*2^(-n*_W) mod m,
+// assuming k = -1/m mod 2^_W.
+// z is used for storing the result which is returned;
+// z must not alias x, y or m.
+func (z nat) montgomery(x, y, m nat, k Word, n int) nat {
+	var c1, c2 Word
+	z = z.make(n)
+	z.clear()
+	for i := 0; i < n; i++ {
+		d := y[i]
+		c1 += addMulVVW(z, x, d)
+		t := z[0] * k
+		c2 = addMulVVW(z, m, t)
+
+		copy(z, z[1:])
+		z[n-1] = c1 + c2
+		if z[n-1] < c1 {
+			c1 = 1
+		} else {
+			c1 = 0
+		}
+	}
+	if c1 != 0 {
+		subVV(z, z, m)
+	}
+	return z
+}
+
 // Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks.
 // Factored out for readability - do not use outside karatsuba.
 func karatsubaAdd(z, x nat, n int) {
@@ -905,8 +933,11 @@ func (z nat) expNN(x, y, m nat) nat {
 	// 4-bit, windowed exponentiation. This involves precomputing 14 values
 	// (x^2...x^15) but then reduces the number of multiply-reduces by a
 	// third. Even for a 32-bit exponent, this reduces the number of
-	// operations.
+	// operations. Uses Montgomery method for odd moduli.
 	if len(x) > 1 && len(y) > 1 && len(m) > 0 {
+		if m[0]&1 == 1 {
+			return z.expNNMontgomery(x, y, m)
+		}
 		return z.expNNWindowed(x, y, m)
 	}
 
@@ -1029,6 +1060,87 @@ func (z nat) expNNWindowed(x, y, m nat) nat {
 	return z.norm()
 }
 
+// expNNMontgomery calculates x**y mod m using a fixed, 4-bit window.
+// Uses Montgomery representation.
+func (z nat) expNNMontgomery(x, y, m nat) nat {
+	var zz, one, rr, RR nat
+
+	numWords := len(m)
+
+	// We want the lengths of x and m to be equal.
+	if len(x) > numWords {
+		_, rr = rr.div(rr, x, m)
+	} else if len(x) < numWords {
+		rr = rr.make(numWords)
+		rr.clear()
+		for i := range x {
+			rr[i] = x[i]
+		}
+	} else {
+		rr = x
+	}
+	x = rr
+
+	// Ideally the precomputations would be performed outside, and reused
+	// k0 = -mˆ-1 mod 2ˆ_W. Algorithm from: Dumas, J.G. "On Newton–Raphson
+	// Iteration for Multiplicative Inverses Modulo Prime Powers".
+	k0 := 2 - m[0]
+	t := m[0] - 1
+	for i := 1; i < _W; i <<= 1 {
+		t *= t
+		k0 *= (t + 1)
+	}
+	k0 = -k0
+
+	// RR = 2ˆ(2*_W*len(m)) mod m
+	RR = RR.setWord(1)
+	zz = zz.shl(RR, uint(2*numWords*_W))
+	_, RR = RR.div(RR, zz, m)
+	if len(RR) < numWords {
+		zz = zz.make(numWords)
+		copy(zz, RR)
+		RR = zz
+	}
+	// one = 1, with equal length to that of m
+	one = one.make(numWords)
+	one.clear()
+	one[0] = 1
+
+	const n = 4
+	// powers[i] contains x^i
+	var powers [1 << n]nat
+	powers[0] = powers[0].montgomery(one, RR, m, k0, numWords)
+	powers[1] = powers[1].montgomery(x, RR, m, k0, numWords)
+	for i := 2; i < 1<= 0; i-- {
+		yi := y[i]
+		for j := 0; j < _W; j += n {
+			if i != len(y)-1 || j != 0 {
+				zz = zz.montgomery(z, z, m, k0, numWords)
+				z = z.montgomery(zz, zz, m, k0, numWords)
+				zz = zz.montgomery(z, z, m, k0, numWords)
+				z = z.montgomery(zz, zz, m, k0, numWords)
+			}
+			zz = zz.montgomery(z, powers[yi>>(_W-n)], m, k0, numWords)
+			z, zz = zz, z
+			yi <<= n
+		}
+	}
+	// convert to regular number
+	zz = zz.montgomery(z, one, m, k0, numWords)
+	return zz.norm()
+}
+
 // probablyPrime performs reps Miller-Rabin tests to check whether n is prime.
 // If it returns true, n is prime with probability 1 - 1/4^reps.
 // If it returns false, n is not prime.
diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go
index b25a89f731..69b9c30a71 100644
--- a/src/math/big/nat_test.go
+++ b/src/math/big/nat_test.go
@@ -332,6 +332,67 @@ func TestTrailingZeroBits(t *testing.T) {
 	}
 }
 
+var montgomeryTests = []struct {
+	x, y, m string
+	k0      uint64
+	out32, out64     string
+}{
+	{
+		"0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+		"0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+		"0xfffffffffffffffffffffffffffffffffffffffffffffffff",
+		0x0000000000000000,
+		"0xffffffffffffffffffffffffffffffffffffffffff",
+		"0xffffffffffffffffffffffffffffffffff",
+	},
+	{
+		"0x0000000080000000",
+		"0x00000000ffffffff",
+		"0x0000000010000001",
+		0xff0000000fffffff,
+		"0x0000000088000000",
+		"0x0000000007800001",
+	},
+	{
+		"0xffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+		"0xffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+		"0x33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+		0xdecc8f1249812adf,
+		"0x22bb05b6d95eaaeca2bb7c05e51f807bce9064b5fbad177161695e4558f9474e91cd79",
+		"0x14beb58d230f85b6d95eaaeca2bb7c05e51f807bce9064b5fb45669afa695f228e48cd",
+	},
+	{
+		"0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+		"0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+		"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+		0xdecc8f1249812adf,
+		"0x5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d7a11c7772cba02c22f9711078d51a3797eb18e691295293284d988e349fa6deba46b25a4ecd9f715",
+		"0x92fcad4b5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d799c32fe2f3cc5422f9711078d51a3797eb18e691295293284d8f5e69caf6decddfe1df6",
+	},
+}
+
+func TestMontgomery(t *testing.T) {
+	for i, test := range montgomeryTests {
+		x := natFromString(test.x)
+		y := natFromString(test.y)
+		m := natFromString(test.m)
+
+		var out nat
+		if _W == 32 {
+			out = natFromString(test.out32)
+		} else {
+			out = natFromString(test.out64)
+		}
+
+		k0 := Word(test.k0 & _M)  // mask k0 to ensure that it fits for 32-bit systems.
+		z := nat(nil).montgomery(x, y, m, k0, len(m))
+		z = z.norm()
+		if z.cmp(out) != 0 {
+			t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString())
+		}
+	}
+}
+
 var expNNTests = []struct {
 	x, y, m string
 	out     string

From c344f751fced679add6eec87c4dc92020484e476 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 21 May 2015 18:06:34 -0700
Subject: [PATCH 181/232] math/big: gofmt nat_test.go

Was apparently checked in by https://go-review.googlesource.com/#/c/9253/
without being gofmt-ed.

TBR: agl

Change-Id: I4d011dbaa15b7c5e73ca71f724f32951a0302dae
Reviewed-on: https://go-review.googlesource.com/10353
Reviewed-by: Robert Griesemer 
---
 src/math/big/nat_test.go | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go
index 69b9c30a71..a15a2bcac0 100644
--- a/src/math/big/nat_test.go
+++ b/src/math/big/nat_test.go
@@ -333,9 +333,9 @@ func TestTrailingZeroBits(t *testing.T) {
 }
 
 var montgomeryTests = []struct {
-	x, y, m string
-	k0      uint64
-	out32, out64     string
+	x, y, m      string
+	k0           uint64
+	out32, out64 string
 }{
 	{
 		"0xffffffffffffffffffffffffffffffffffffffffffffffffe",
@@ -384,7 +384,7 @@ func TestMontgomery(t *testing.T) {
 			out = natFromString(test.out64)
 		}
 
-		k0 := Word(test.k0 & _M)  // mask k0 to ensure that it fits for 32-bit systems.
+		k0 := Word(test.k0 & _M) // mask k0 to ensure that it fits for 32-bit systems.
 		z := nat(nil).montgomery(x, y, m, k0, len(m))
 		z = z.norm()
 		if z.cmp(out) != 0 {

From b19ec6842d3d3bdc6d7b67fa065121a9d317cff7 Mon Sep 17 00:00:00 2001
From: David Chase 
Date: Thu, 21 May 2015 12:40:25 -0400
Subject: [PATCH 182/232] cmd/internal/gc: make indirect calls properly
 escape-y

Indirect function and method calls should leak everything,
but they didn't.

This fix had no particular effect on the cost of running the
compiler on html/template/*.go and added a single new "escape"
to the standard library:

    syscall/syscall_unix.go:85: &b[0] escapes to heap
in
	if errno := m.munmap(uintptr(unsafe.Pointer(&b[0])),
	                     uintptr(len(b))); errno != nil {

Added specific escape testing to escape_calls.go
(and verified that it fails without this patch)

I also did a little code cleanup around the changes in esc.c.

Fixes #10925

Change-Id: I9984b701621ad4c49caed35b01e359295c210033
Reviewed-on: https://go-review.googlesource.com/10295
Reviewed-by: Russ Cox 
---
 src/cmd/compile/internal/gc/esc.go | 67 ++++++++++++++++++++----------
 test/escape_calls.go               | 10 +++++
 test/fixedbugs/issue10925.go       | 23 ++++++++++
 3 files changed, 79 insertions(+), 21 deletions(-)
 create mode 100644 test/fixedbugs/issue10925.go

diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index a9a1748b9a..4c1f52521d 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -1269,6 +1269,24 @@ func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 {
 	return (e &^ (bitsMaskForTag << shift)) | encodedFlow
 }
 
+func initEscretval(e *EscState, n *Node, fntype *Type) {
+	i := 0
+	n.Escretval = nil // Suspect this is not nil for indirect calls.
+	for t := getoutargx(fntype).Type; t != nil; t = t.Down {
+		src := Nod(ONAME, nil, nil)
+		buf := fmt.Sprintf(".out%d", i)
+		i++
+		src.Sym = Lookup(buf)
+		src.Type = t.Type
+		src.Class = PAUTO
+		src.Curfn = Curfn
+		src.Escloopdepth = e.loopdepth
+		src.Used = true
+		src.Lineno = n.Lineno
+		n.Escretval = list(n.Escretval, src)
+	}
+}
+
 // This is a bit messier than fortunate, pulled out of esc's big
 // switch for clarity.	We either have the paramnodes, which may be
 // connected to other things through flows or we have the parameter type
@@ -1277,7 +1295,7 @@ func escNoteOutputParamFlow(e uint16, vargen int32, level Level) uint16 {
 // this-package
 func esccall(e *EscState, n *Node, up *Node) {
 	var fntype *Type
-
+	var indirect bool
 	var fn *Node
 	switch n.Op {
 	default:
@@ -1286,6 +1304,7 @@ func esccall(e *EscState, n *Node, up *Node) {
 	case OCALLFUNC:
 		fn = n.Left
 		fntype = fn.Type
+		indirect = fn.Op != ONAME || fn.Class != PFUNC
 
 	case OCALLMETH:
 		fn = n.Left.Right.Sym.Def
@@ -1297,6 +1316,7 @@ func esccall(e *EscState, n *Node, up *Node) {
 
 	case OCALLINTER:
 		fntype = n.Left.Type
+		indirect = true
 	}
 
 	ll := n.List
@@ -1307,6 +1327,28 @@ func esccall(e *EscState, n *Node, up *Node) {
 		}
 	}
 
+	if indirect {
+		// We know nothing!
+		// Leak all the parameters
+		for ; ll != nil; ll = ll.Next {
+			escassign(e, &e.theSink, ll.N)
+			if Debug['m'] > 2 {
+				fmt.Printf("%v::esccall:: indirect call <- %v, untracked\n", Ctxt.Line(int(lineno)), Nconv(ll.N, obj.FmtShort))
+			}
+		}
+		// Set up bogus outputs
+		initEscretval(e, n, fntype)
+		// If there is a receiver, it also leaks to heap.
+		if n.Op != OCALLFUNC {
+			t := getthisx(fntype).Type
+			src := n.Left.Left
+			if haspointers(t.Type) {
+				escassign(e, &e.theSink, src)
+			}
+		}
+		return
+	}
+
 	if fn != nil && fn.Op == ONAME && fn.Class == PFUNC &&
 		fn.Defn != nil && fn.Defn.Nbody != nil && fn.Ntype != nil && fn.Defn.Esc < EscFuncTagged {
 		if Debug['m'] > 2 {
@@ -1376,23 +1418,7 @@ func esccall(e *EscState, n *Node, up *Node) {
 	}
 
 	// set up out list on this call node with dummy auto ONAMES in the current (calling) function.
-	i := 0
-
-	var src *Node
-	var buf string
-	for t := getoutargx(fntype).Type; t != nil; t = t.Down {
-		src = Nod(ONAME, nil, nil)
-		buf = fmt.Sprintf(".out%d", i)
-		i++
-		src.Sym = Lookup(buf)
-		src.Type = t.Type
-		src.Class = PAUTO
-		src.Curfn = Curfn
-		src.Escloopdepth = e.loopdepth
-		src.Used = true
-		src.Lineno = n.Lineno
-		n.Escretval = list(n.Escretval, src)
-	}
+	initEscretval(e, n, fntype)
 
 	//	print("esc analyzed fn: %#N (%+T) returning (%+H)\n", fn, fntype, n->escretval);
 
@@ -1405,9 +1431,8 @@ func esccall(e *EscState, n *Node, up *Node) {
 		}
 	}
 
-	var a *Node
 	for t := getinargx(fntype).Type; ll != nil; ll = ll.Next {
-		src = ll.N
+		src := ll.N
 		if t.Isddd && !n.Isddd {
 			// Introduce ODDDARG node to represent ... allocation.
 			src = Nod(ODDDARG, nil, nil)
@@ -1425,7 +1450,7 @@ func esccall(e *EscState, n *Node, up *Node) {
 
 		if haspointers(t.Type) {
 			if escassignfromtag(e, t.Note, n.Escretval, src) == EscNone && up.Op != ODEFER && up.Op != OPROC {
-				a = src
+				a := src
 				for a.Op == OCONVNOP {
 					a = a.Left
 				}
diff --git a/test/escape_calls.go b/test/escape_calls.go
index f289670091..8c9a6dadda 100644
--- a/test/escape_calls.go
+++ b/test/escape_calls.go
@@ -42,3 +42,13 @@ func walk(np **Node) int { // ERROR "leaking param content: np"
 	*np = n
 	return w + wl + wr
 }
+
+// Test for bug where func var f used prototype's escape analysis results.
+func prototype(xyz []string) {} // ERROR "prototype xyz does not escape"
+func bar() {
+	var got [][]string
+	f := prototype
+	f = func(ss []string) { got = append(got, ss) } // ERROR "leaking param: ss" "func literal does not escape"
+	s := "string"
+	f([]string{s}) // ERROR "\[\]string literal escapes to heap"
+}
diff --git a/test/fixedbugs/issue10925.go b/test/fixedbugs/issue10925.go
new file mode 100644
index 0000000000..30add82c78
--- /dev/null
+++ b/test/fixedbugs/issue10925.go
@@ -0,0 +1,23 @@
+// run
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import "fmt"
+
+func prototype(xyz []string) {}
+func main() {
+	var got [][]string
+	f := prototype
+	f = func(ss []string) { got = append(got, ss) }
+	for _, s := range []string{"one", "two", "three"} {
+		f([]string{s})
+	}
+	if got[0][0] != "one" || got[1][0] != "two" || got[2][0] != "three" {
+		// Bug's wrong output was [[three] [three] [three]]
+		fmt.Println("Expected [[one] [two] [three]], got", got)
+	}
+}

From e5060c7f7524bf32e07f62d2593aae8d280725e5 Mon Sep 17 00:00:00 2001
From: David Chase 
Date: Wed, 20 May 2015 15:16:34 -0400
Subject: [PATCH 183/232] cmd/internal/gc: move check for
 large-hence-heap-allocated types into escape analysis

Before this change, the check for too-large arrays (and other large
types) occurred after escape analysis.  If the data moved off stack
and onto the heap contained any pointers, it would therefore escape,
but because the too-large check occurred after escape analysis this
would not be recorded and a stack pointer would leak to the heap
(see the modified escape_array.go for an example).

Some of these appear to remain, in calls to typecheck from within walk.

Also corrected a few comments in escape_array.go about "BAD"
analysis that is now done correctly.

Enhanced to move aditional EscNone-but-large-so-heap checks into esc.c.

Change-Id: I770c111baff28a9ed5f8beb601cf09dacc561b83
Reviewed-on: https://go-review.googlesource.com/10268
Reviewed-by: Russ Cox 
---
 src/cmd/compile/internal/gc/esc.go  | 13 ++++++
 src/cmd/compile/internal/gc/lex.go  |  6 +--
 src/cmd/compile/internal/gc/pgen.go | 19 ---------
 src/cmd/compile/internal/gc/walk.go | 24 ++++++++++-
 test/escape5.go                     |  7 ++--
 test/escape_array.go                | 62 ++++++++++++++++++++++++++---
 6 files changed, 96 insertions(+), 35 deletions(-)

diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go
index 4c1f52521d..578ce33a81 100644
--- a/src/cmd/compile/internal/gc/esc.go
+++ b/src/cmd/compile/internal/gc/esc.go
@@ -586,6 +586,19 @@ func esc(e *EscState, n *Node, up *Node) {
 		}
 	}
 
+	// Big stuff escapes unconditionally
+	// "Big" conditions that were scattered around in walk have been gathered here
+	if n.Esc != EscHeap && n.Type != nil && (n.Type.Width > MaxStackVarSize ||
+		n.Op == ONEW && n.Type.Type.Width >= 1<<16 ||
+		n.Op == OMAKESLICE && !isSmallMakeSlice(n)) {
+		if Debug['m'] > 1 {
+			Warnl(int(n.Lineno), "%v is too large for stack", n)
+		}
+		n.Esc = EscHeap
+		addrescapes(n)
+		escassign(e, &e.theSink, n)
+	}
+
 	esc(e, n.Left, n)
 	esc(e, n.Right, n)
 	esc(e, n.Ntest, n)
diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go
index 3b93207ef1..974ca9282e 100644
--- a/src/cmd/compile/internal/gc/lex.go
+++ b/src/cmd/compile/internal/gc/lex.go
@@ -446,12 +446,10 @@ func Main() {
 	// which stores the addresses of stack variables into the closure.
 	// If the closure does not escape, it needs to be on the stack
 	// or else the stack copier will not update it.
+	// Large values are also moved off stack in escape analysis;
+	// because large values may contain pointers, it must happen early.
 	escapes(xtop)
 
-	// Escape analysis moved escaped values off stack.
-	// Move large values off stack too.
-	movelarge(xtop)
-
 	// Phase 7: Transform closure bodies to properly reference captured variables.
 	// This needs to happen before walk, because closures must be transformed
 	// before walk reaches a call of a closure.
diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go
index 1b67cf2c3e..5fb0776f3c 100644
--- a/src/cmd/compile/internal/gc/pgen.go
+++ b/src/cmd/compile/internal/gc/pgen.go
@@ -301,25 +301,6 @@ func allocauto(ptxt *obj.Prog) {
 	}
 }
 
-func movelarge(l *NodeList) {
-	for ; l != nil; l = l.Next {
-		if l.N.Op == ODCLFUNC {
-			movelargefn(l.N)
-		}
-	}
-}
-
-func movelargefn(fn *Node) {
-	var n *Node
-
-	for l := fn.Func.Dcl; l != nil; l = l.Next {
-		n = l.N
-		if n.Class == PAUTO && n.Type != nil && n.Type.Width > MaxStackVarSize {
-			addrescapes(n)
-		}
-	}
-}
-
 func Cgen_checknil(n *Node) {
 	if Disable_checknil != 0 {
 		return
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index b5b8611e5b..11117666c7 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -352,6 +352,20 @@ func walkstmt(np **Node) {
 	*np = n
 }
 
+func isSmallMakeSlice(n *Node) bool {
+	if n.Op != OMAKESLICE {
+		return false
+	}
+	l := n.Left
+	r := n.Right
+	if r == nil {
+		r = l
+	}
+	t := n.Type
+
+	return Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.(*Mpint)) < (1<<16)/t.Type.Width)
+}
+
 /*
  * walk the whole tree of the body of an
  * expression or simple statement.
@@ -1320,7 +1334,10 @@ func walkexpr(np **Node, init **NodeList) {
 		goto ret
 
 	case ONEW:
-		if n.Esc == EscNone && n.Type.Type.Width < 1<<16 {
+		if n.Esc == EscNone {
+			if n.Type.Type.Width >= 1<<16 {
+				Fatal("Large ONEW with EscNone, %v", n)
+			}
 			r := temp(n.Type.Type)
 			r = Nod(OAS, r, nil) // zero temp
 			typecheck(&r, Etop)
@@ -1458,7 +1475,10 @@ func walkexpr(np **Node, init **NodeList) {
 			l = r
 		}
 		t := n.Type
-		if n.Esc == EscNone && Smallintconst(l) && Smallintconst(r) && (t.Type.Width == 0 || Mpgetfix(r.Val.U.(*Mpint)) < (1<<16)/t.Type.Width) {
+		if n.Esc == EscNone {
+			if !isSmallMakeSlice(n) {
+				Fatal("Non-small OMAKESLICE with EscNone, %v", n)
+			}
 			// var arr [r]T
 			// n = arr[:l]
 			t = aindex(r, t.Type) // [r]T
diff --git a/test/escape5.go b/test/escape5.go
index 1d411b32d4..6a138ea090 100644
--- a/test/escape5.go
+++ b/test/escape5.go
@@ -117,7 +117,6 @@ func leakrecursive2(p, q *int) (*int, *int) { // ERROR "leaking param: p" "leaki
 	return p, q
 }
 
-
 var global interface{}
 
 type T1 struct {
@@ -141,12 +140,12 @@ func f8(p *T1) (k T2) { // ERROR "leaking param: p to result k" "leaking param:
 
 func f9() {
 	var j T1 // ERROR "moved to heap: j"
-	f8(&j) // ERROR "&j escapes to heap"
+	f8(&j)   // ERROR "&j escapes to heap"
 }
 
 func f10() {
 	// These don't escape but are too big for the stack
-	var x [1<<30]byte // ERROR "moved to heap: x"
-	var y = make([]byte, 1<<30) // ERROR "does not escape"
+	var x [1 << 30]byte         // ERROR "moved to heap: x"
+	var y = make([]byte, 1<<30) // ERROR "make\(\[\]byte, 1 << 30\) escapes to heap"
 	_ = x[0] + y[0]
 }
diff --git a/test/escape_array.go b/test/escape_array.go
index ac51fe7ca6..5da77713d2 100644
--- a/test/escape_array.go
+++ b/test/escape_array.go
@@ -4,10 +4,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Test escape analysis for function parameters.
-
-// In this test almost everything is BAD except the simplest cases
-// where input directly flows to output.
+// Test escape analysis for arrays and some large things
 
 package foo
 
@@ -59,14 +56,67 @@ func fup(x *U, y *string) *string { // ERROR "leaking param: x to result ~r2 lev
 	return x[1]
 }
 
-// BAD: would be nice to record that *y (content) is what leaks, not y itself
 func fum(x *U, y **string) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$"
 	x[0] = *y
 	return x[1]
 }
 
-// BAD: would be nice to record that y[0] (content) is what leaks, not y itself
 func fuo(x *U, y *U) *string { // ERROR "leaking param: x to result ~r2 level=1$" "leaking param content: y$"
 	x[0] = y[0]
 	return x[1]
 }
+
+// These two tests verify that:
+// small array literals are stack allocated;
+// pointers stored in small array literals do not escape;
+// large array literals are heap allocated;
+// pointers stored in large array literals escape.
+func hugeLeaks1(x **string, y **string) { // ERROR "leaking param content: x" "hugeLeaks1 y does not escape" "mark escaped content: x"
+	a := [10]*string{*y}
+	_ = a
+	// 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
+	b := [4000000]*string{*x} // ERROR "moved to heap: b"
+	_ = b
+}
+
+func hugeLeaks2(x *string, y *string) { // ERROR "leaking param: x" "hugeLeaks2 y does not escape"
+	a := [10]*string{y}
+	_ = a
+	// 4 x 4,000,000 exceeds MaxStackVarSize, therefore it must be heap allocated if pointers are 4 bytes or larger.
+	b := [4000000]*string{x} // ERROR "moved to heap: b"
+	_ = b
+}
+
+// BAD: x need not leak.
+func doesNew1(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+	a := new([10]*string) // ERROR "new\(\[10\]\*string\) does not escape"
+	a[0] = x
+	b := new([65537]*string) // ERROR "new\(\[65537\]\*string\) escapes to heap"
+	b[0] = y
+}
+
+type a10 struct {
+	s *string
+	i [10]int32
+}
+
+type a65537 struct {
+	s *string
+	i [65537]int32
+}
+
+// BAD: x need not leak.
+func doesNew2(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+	a := new(a10) // ERROR "new\(a10\) does not escape"
+	a.s = x
+	b := new(a65537) // ERROR "new\(a65537\) escapes to heap"
+	b.s = y
+}
+
+// BAD: x need not leak.
+func doesMakeSlice(x *string, y *string) { // ERROR "leaking param: x" "leaking param: y"
+	a := make([]*string, 10) // ERROR "make\(\[\]\*string, 10\) does not escape"
+	a[0] = x
+	b := make([]*string, 65537) // ERROR "make\(\[\]\*string, 65537\) escapes to heap"
+	b[0] = y
+}

From 994b2d46455509f95d60f0abd0d37d3d789f89f2 Mon Sep 17 00:00:00 2001
From: Dave Cheney 
Date: Fri, 22 May 2015 12:55:47 +1000
Subject: [PATCH 184/232] net: fix panic in TestDialerDualStack

This change ensures that the test does not try to close dual stack
listeners which have not yet been opened.

Spotted in crash here
http://build.golang.org/log/e5843777df400868ce708b7f00c50dc32c2ec478

Change-Id: I79d513e166effb3e018e2b9dfc23751d92fcbe4b
Reviewed-on: https://go-review.googlesource.com/10371
Reviewed-by: Mikio Hara 
Run-TryBot: Dave Cheney 
TryBot-Result: Gobot Gobot 
---
 src/net/mockserver_test.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/net/mockserver_test.go b/src/net/mockserver_test.go
index 62bcfa4022..dd6f4df3b9 100644
--- a/src/net/mockserver_test.go
+++ b/src/net/mockserver_test.go
@@ -186,7 +186,7 @@ func newDualStackServer(lns []streamListener) (*dualStackServer, error) {
 	for i := range dss.lns {
 		ln, err := Listen(dss.lns[i].network, JoinHostPort(dss.lns[i].address, dss.port))
 		if err != nil {
-			for _, ln := range dss.lns {
+			for _, ln := range dss.lns[:i] {
 				ln.Listener.Close()
 			}
 			return nil, err

From 84cfba17c2451f1a94ea7d812c1aba91e3606890 Mon Sep 17 00:00:00 2001
From: Elias Naur 
Date: Mon, 18 May 2015 11:00:24 +0200
Subject: [PATCH 185/232] runtime: don't always unblock all signals

Ian proposed an improved way of handling signals masks in Go, motivated
by a problem where the Android java runtime expects certain signals to
be blocked for all JVM threads. Discussion here

https://groups.google.com/forum/#!topic/golang-dev/_TSCkQHJt6g

Ian's text is used in the following:

A Go program always needs to have the synchronous signals enabled.
These are the signals for which _SigPanic is set in sigtable, namely
SIGSEGV, SIGBUS, SIGFPE.

A Go program that uses the os/signal package, and calls signal.Notify,
needs to have at least one thread which is not blocking that signal,
but it doesn't matter much which one.

Unix programs do not change signal mask across execve.  They inherit
signal masks across fork.  The shell uses this fact to some extent;
for example, the job control signals (SIGTTIN, SIGTTOU, SIGTSTP) are
blocked for commands run due to backquote quoting or $().

Our current position on signal masks was not thought out.  We wandered
into step by step, e.g., http://golang.org/cl/7323067 .

This CL does the following:

Introduce a new platform hook, msigsave, that saves the signal mask of
the current thread to m.sigsave.

Call msigsave from needm and newm.

In minit grab set up the signal mask from m.sigsave and unblock the
essential synchronous signals, and SIGILL, SIGTRAP, SIGPROF, SIGSTKFLT
(for systems that have it).

In unminit, restore the signal mask from m.sigsave.

The first time that os/signal.Notify is called, start a new thread whose
only purpose is to update its signal mask to make sure signals for
signal.Notify are unblocked on at least one thread.

The effect on Go programs will be that if they are invoked with some
non-synchronous signals blocked, those signals will normally be
ignored.  Previously, those signals would mostly be ignored.  A change
in behaviour will occur for programs started with any of these signals
blocked, if they receive the signal: SIGHUP, SIGINT, SIGQUIT, SIGABRT,
SIGTERM.  Previously those signals would always cause a crash (unless
using the os/signal package); with this change, they will be ignored
if the program is started with the signal blocked (and does not use
the os/signal package).

./all.bash completes successfully on linux/amd64.

OpenBSD is missing the implementation.

Change-Id: I188098ba7eb85eae4c14861269cc466f2aa40e8c
Reviewed-on: https://go-review.googlesource.com/10173
Reviewed-by: Ian Lance Taylor 
---
 misc/cgo/test/cgo_linux_test.go    |  9 +--
 misc/cgo/test/sigprocmask_linux.c  | 35 ++++++++++++
 misc/cgo/test/sigprocmask_linux.go | 38 ++++++++++++
 src/runtime/os1_darwin.go          | 26 +++++++--
 src/runtime/os1_dragonfly.go       | 28 +++++++--
 src/runtime/os1_freebsd.go         | 28 +++++++--
 src/runtime/os1_linux.go           | 28 +++++++--
 src/runtime/os1_nacl.go            |  3 +
 src/runtime/os1_netbsd.go          | 29 ++++++++--
 src/runtime/os1_openbsd.go         |  7 ++-
 src/runtime/os1_plan9.go           |  3 +
 src/runtime/os1_windows.go         |  3 +
 src/runtime/os3_solaris.go         | 29 ++++++++--
 src/runtime/proc1.go               |  2 +
 src/runtime/runtime2.go            | 20 ++++---
 src/runtime/signal1_unix.go        | 92 ++++++++++++++++++++++++++----
 src/runtime/signal_darwin.go       | 14 ++---
 src/runtime/signal_linux.go        | 16 +++---
 src/runtime/signal_netbsd.go       | 14 ++---
 src/runtime/signal_solaris.go      | 14 ++---
 20 files changed, 358 insertions(+), 80 deletions(-)
 create mode 100644 misc/cgo/test/sigprocmask_linux.c
 create mode 100644 misc/cgo/test/sigprocmask_linux.go

diff --git a/misc/cgo/test/cgo_linux_test.go b/misc/cgo/test/cgo_linux_test.go
index 6e1d1065f6..3cc2af5919 100644
--- a/misc/cgo/test/cgo_linux_test.go
+++ b/misc/cgo/test/cgo_linux_test.go
@@ -6,7 +6,8 @@ package cgotest
 
 import "testing"
 
-func TestSetgid(t *testing.T)  { testSetgid(t) }
-func Test6997(t *testing.T)    { test6997(t) }
-func TestBuildID(t *testing.T) { testBuildID(t) }
-func Test9400(t *testing.T)    { test9400(t) }
+func TestSetgid(t *testing.T)      { testSetgid(t) }
+func Test6997(t *testing.T)        { test6997(t) }
+func TestBuildID(t *testing.T)     { testBuildID(t) }
+func Test9400(t *testing.T)        { test9400(t) }
+func TestSigProcMask(t *testing.T) { testSigProcMask(t) }
diff --git a/misc/cgo/test/sigprocmask_linux.c b/misc/cgo/test/sigprocmask_linux.c
new file mode 100644
index 0000000000..6597e985ac
--- /dev/null
+++ b/misc/cgo/test/sigprocmask_linux.c
@@ -0,0 +1,35 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+extern void IntoGoAndBack();
+
+int CheckBlocked() {
+	sigset_t mask;
+	sigprocmask(SIG_BLOCK, NULL, &mask);
+	return sigismember(&mask, SIGIO);
+}
+
+static void* sigthreadfunc(void* unused) {
+	sigset_t mask;
+	sigemptyset(&mask);
+	sigaddset(&mask, SIGIO);
+	sigprocmask(SIG_BLOCK, &mask, NULL);
+	IntoGoAndBack();
+}
+
+int RunSigThread() {
+	pthread_t thread;
+	int r;
+
+	r = pthread_create(&thread, NULL, &sigthreadfunc, NULL);
+	if (r != 0)
+		return r;
+	return pthread_join(thread, NULL);
+}
diff --git a/misc/cgo/test/sigprocmask_linux.go b/misc/cgo/test/sigprocmask_linux.go
new file mode 100644
index 0000000000..7d343e92c4
--- /dev/null
+++ b/misc/cgo/test/sigprocmask_linux.go
@@ -0,0 +1,38 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+#cgo CFLAGS: -pthread
+#cgo LDFLAGS: -pthread
+extern int RunSigThread();
+extern int CheckBlocked();
+*/
+import "C"
+import (
+	"os"
+	"os/signal"
+	"syscall"
+	"testing"
+)
+
+var blocked bool
+
+//export IntoGoAndBack
+func IntoGoAndBack() {
+	// Verify that SIGIO stays blocked on the C thread
+	// even when unblocked for signal.Notify().
+	signal.Notify(make(chan os.Signal), syscall.SIGIO)
+	blocked = C.CheckBlocked() != 0
+}
+
+func testSigProcMask(t *testing.T) {
+	if r := C.RunSigThread(); r != 0 {
+		t.Error("pthread_create/pthread_join failed")
+	}
+	if !blocked {
+		t.Error("Go runtime unblocked SIGIO")
+	}
+}
diff --git a/src/runtime/os1_darwin.go b/src/runtime/os1_darwin.go
index 10cf460f7f..1b74e3e653 100644
--- a/src/runtime/os1_darwin.go
+++ b/src/runtime/os1_darwin.go
@@ -8,7 +8,6 @@ import "unsafe"
 
 //extern SigTabTT runtime·sigtab[];
 
-var sigset_none = uint32(0)
 var sigset_all = ^uint32(0)
 
 func unimplemented(name string) {
@@ -126,17 +125,36 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
+func msigsave(mp *m) {
+	smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
+	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+		throw("insufficient storage for signal mask")
+	}
+	sigprocmask(_SIG_SETMASK, nil, smask)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
 	// Initialize signal handling.
 	_g_ := getg()
 	signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
-	sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask &^= 1 << (uint32(i) - 1)
+		}
+	}
+	sigprocmask(_SIG_SETMASK, &nmask, nil)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
+	_g_ := getg()
+	smask := (*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
 	signalstack(nil, 0)
 }
 
@@ -447,6 +465,6 @@ func signalstack(p *byte, n int32) {
 	sigaltstack(&st, nil)
 }
 
-func unblocksignals() {
-	sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+func updatesigmask(m sigmask) {
+	sigprocmask(_SIG_SETMASK, &m[0], nil)
 }
diff --git a/src/runtime/os1_dragonfly.go b/src/runtime/os1_dragonfly.go
index a590aea39b..eb42b54e2b 100644
--- a/src/runtime/os1_dragonfly.go
+++ b/src/runtime/os1_dragonfly.go
@@ -12,7 +12,6 @@ const (
 	_HW_NCPU = 3
 )
 
-var sigset_none = sigset{}
 var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
 
 func getncpu() int32 {
@@ -120,6 +119,14 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
+func msigsave(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+		throw("insufficient storage for signal mask")
+	}
+	sigprocmask(nil, smask)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -130,11 +137,22 @@ func minit() {
 
 	// Initialize signal handling
 	signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
-	sigprocmask(&sigset_none, nil)
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+		}
+	}
+	sigprocmask(&nmask, nil)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
+	_g_ := getg()
+	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	sigprocmask(smask, nil)
 	signalstack(nil, 0)
 }
 
@@ -215,6 +233,8 @@ func signalstack(p *byte, n int32) {
 	sigaltstack(&st, nil)
 }
 
-func unblocksignals() {
-	sigprocmask(&sigset_none, nil)
+func updatesigmask(m sigmask) {
+	var mask sigset
+	copy(mask.__bits[:], m[:])
+	sigprocmask(&mask, nil)
 }
diff --git a/src/runtime/os1_freebsd.go b/src/runtime/os1_freebsd.go
index 8719a49286..f7f34bd386 100644
--- a/src/runtime/os1_freebsd.go
+++ b/src/runtime/os1_freebsd.go
@@ -12,7 +12,6 @@ const (
 	_HW_NCPU = 3
 )
 
-var sigset_none = sigset{}
 var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
 
 func getncpu() int32 {
@@ -119,6 +118,14 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
+func msigsave(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+		throw("insufficient storage for signal mask")
+	}
+	sigprocmask(nil, smask)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -132,11 +139,22 @@ func minit() {
 
 	// Initialize signal handling.
 	signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
-	sigprocmask(&sigset_none, nil)
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+		}
+	}
+	sigprocmask(&nmask, nil)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
+	_g_ := getg()
+	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	sigprocmask(smask, nil)
 	signalstack(nil, 0)
 }
 
@@ -217,6 +235,8 @@ func signalstack(p *byte, n int32) {
 	sigaltstack(&st, nil)
 }
 
-func unblocksignals() {
-	sigprocmask(&sigset_none, nil)
+func updatesigmask(m [(_NSIG + 31) / 32]uint32) {
+	var mask sigset
+	copy(mask.__bits[:], m[:])
+	sigprocmask(&mask, nil)
 }
diff --git a/src/runtime/os1_linux.go b/src/runtime/os1_linux.go
index e4b18c79b3..02f98d7c5f 100644
--- a/src/runtime/os1_linux.go
+++ b/src/runtime/os1_linux.go
@@ -6,7 +6,6 @@ package runtime
 
 import "unsafe"
 
-var sigset_none sigset
 var sigset_all sigset = sigset{^uint32(0), ^uint32(0)}
 
 // Linux futex.
@@ -190,17 +189,36 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
+func msigsave(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+		throw("insufficient storage for signal mask")
+	}
+	rtsigprocmask(_SIG_SETMASK, nil, smask, int32(unsafe.Sizeof(*smask)))
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
 	// Initialize signal handling.
 	_g_ := getg()
 	signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
-	rtsigprocmask(_SIG_SETMASK, &sigset_none, nil, int32(unsafe.Sizeof(sigset_none)))
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+		}
+	}
+	rtsigprocmask(_SIG_SETMASK, &nmask, nil, int32(unsafe.Sizeof(nmask)))
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
+	_g_ := getg()
+	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	rtsigprocmask(_SIG_SETMASK, smask, nil, int32(unsafe.Sizeof(*smask)))
 	signalstack(nil, 0)
 }
 
@@ -304,6 +322,8 @@ func signalstack(p *byte, n int32) {
 	sigaltstack(&st, nil)
 }
 
-func unblocksignals() {
-	rtsigprocmask(_SIG_SETMASK, &sigset_none, nil, int32(unsafe.Sizeof(sigset_none)))
+func updatesigmask(m sigmask) {
+	var mask sigset
+	copy(mask[:], m[:])
+	rtsigprocmask(_SIG_SETMASK, &mask, nil, int32(unsafe.Sizeof(mask)))
 }
diff --git a/src/runtime/os1_nacl.go b/src/runtime/os1_nacl.go
index dbb5dec2fd..66e60f8b12 100644
--- a/src/runtime/os1_nacl.go
+++ b/src/runtime/os1_nacl.go
@@ -15,6 +15,9 @@ func mpreinit(mp *m) {
 
 func sigtramp()
 
+func msigsave(mp *m) {
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
diff --git a/src/runtime/os1_netbsd.go b/src/runtime/os1_netbsd.go
index 8df74b5593..3fb05989e7 100644
--- a/src/runtime/os1_netbsd.go
+++ b/src/runtime/os1_netbsd.go
@@ -17,7 +17,6 @@ const (
 	_CLOCK_MONOTONIC = 3
 )
 
-var sigset_none = sigset{}
 var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
 
 // From NetBSD's 
@@ -139,6 +138,14 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
+func msigsave(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+		throw("insufficient storage for signal mask")
+	}
+	sigprocmask(_SIG_SETMASK, nil, smask)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -147,11 +154,23 @@ func minit() {
 
 	// Initialize signal handling
 	signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
-	sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask.__bits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+		}
+	}
+	sigprocmask(_SIG_SETMASK, &nmask, nil)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
+	_g_ := getg()
+	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
+
 	signalstack(nil, 0)
 }
 
@@ -206,6 +225,8 @@ func signalstack(p *byte, n int32) {
 	sigaltstack(&st, nil)
 }
 
-func unblocksignals() {
-	sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+func updatesigmask(m sigmask) {
+	var mask sigset
+	copy(mask.__bits[:], m[:])
+	sigprocmask(_SIG_SETMASK, &mask, nil)
 }
diff --git a/src/runtime/os1_openbsd.go b/src/runtime/os1_openbsd.go
index 95729a56df..98af545f7f 100644
--- a/src/runtime/os1_openbsd.go
+++ b/src/runtime/os1_openbsd.go
@@ -148,6 +148,9 @@ func mpreinit(mp *m) {
 	mp.gsignal.m = mp
 }
 
+func msigsave(mp *m) {
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -217,6 +220,6 @@ func signalstack(p *byte, n int32) {
 	sigaltstack(&st, nil)
 }
 
-func unblocksignals() {
-	sigprocmask(_SIG_SETMASK, sigset_none)
+func updatesigmask(m sigmask) {
+	sigprocmask(_SIG_SETMASK, m[0])
 }
diff --git a/src/runtime/os1_plan9.go b/src/runtime/os1_plan9.go
index 1aae96a999..bda7057f44 100644
--- a/src/runtime/os1_plan9.go
+++ b/src/runtime/os1_plan9.go
@@ -18,6 +18,9 @@ func mpreinit(mp *m) {
 	mp.errstr = (*byte)(mallocgc(_ERRMAX, nil, _FlagNoScan))
 }
 
+func msigsave(mp *m) {
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
diff --git a/src/runtime/os1_windows.go b/src/runtime/os1_windows.go
index 5719b320f5..bc472d0de9 100644
--- a/src/runtime/os1_windows.go
+++ b/src/runtime/os1_windows.go
@@ -292,6 +292,9 @@ func newosproc(mp *m, stk unsafe.Pointer) {
 func mpreinit(mp *m) {
 }
 
+func msigsave(mp *m) {
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
diff --git a/src/runtime/os3_solaris.go b/src/runtime/os3_solaris.go
index 69ac5b4970..e4fe92de41 100644
--- a/src/runtime/os3_solaris.go
+++ b/src/runtime/os3_solaris.go
@@ -114,7 +114,6 @@ var (
 	libc_write libcFunc
 )
 
-var sigset_none = sigset{}
 var sigset_all = sigset{[4]uint32{^uint32(0), ^uint32(0), ^uint32(0), ^uint32(0)}}
 
 func getncpu() int32 {
@@ -190,6 +189,14 @@ func mpreinit(mp *m) {
 
 func miniterrno()
 
+func msigsave(mp *m) {
+	smask := (*sigset)(unsafe.Pointer(&mp.sigmask))
+	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+		throw("insufficient storage for signal mask")
+	}
+	sigprocmask(_SIG_SETMASK, nil, smask)
+}
+
 // Called to initialize a new m (including the bootstrap m).
 // Called on the new thread, can not allocate memory.
 func minit() {
@@ -197,11 +204,23 @@ func minit() {
 	asmcgocall(unsafe.Pointer(funcPC(miniterrno)), unsafe.Pointer(&libc____errno))
 	// Initialize signal handling
 	signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
-	sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := *(*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask.__sigbits[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+		}
+	}
+	sigprocmask(_SIG_SETMASK, &nmask, nil)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
+	_g_ := getg()
+	smask := (*sigset)(unsafe.Pointer(&_g_.m.sigmask))
+	sigprocmask(_SIG_SETMASK, smask, nil)
+
 	signalstack(nil, 0)
 }
 
@@ -278,8 +297,10 @@ func signalstack(p *byte, n int32) {
 	sigaltstack(&st, nil)
 }
 
-func unblocksignals() {
-	sigprocmask(_SIG_SETMASK, &sigset_none, nil)
+func updatesigmask(m sigmask) {
+	var mask sigset
+	copy(mask.__sigbits[:], m[:])
+	sigprocmask(_SIG_SETMASK, &mask, nil)
 }
 
 //go:nosplit
diff --git a/src/runtime/proc1.go b/src/runtime/proc1.go
index 27281406b8..c070f7d773 100644
--- a/src/runtime/proc1.go
+++ b/src/runtime/proc1.go
@@ -944,6 +944,7 @@ func needm(x byte) {
 	_g_.stack.lo = uintptr(noescape(unsafe.Pointer(&x))) - 32*1024
 	_g_.stackguard0 = _g_.stack.lo + _StackGuard
 
+	msigsave(mp)
 	// Initialize this thread to use the m.
 	asminit()
 	minit()
@@ -1071,6 +1072,7 @@ func unlockextra(mp *m) {
 func newm(fn func(), _p_ *p) {
 	mp := allocm(_p_, fn)
 	mp.nextp.set(_p_)
+	msigsave(mp)
 	if iscgo {
 		var ts cgothreadstart
 		if _cgo_thread_start == nil {
diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go
index 83d8062baf..3ee5d5d29d 100644
--- a/src/runtime/runtime2.go
+++ b/src/runtime/runtime2.go
@@ -266,6 +266,7 @@ type m struct {
 	// Fields not known to debuggers.
 	procid        uint64     // for debuggers, but offset not hard-coded
 	gsignal       *g         // signal-handling g
+	sigmask       [4]uintptr // storage for saved signal mask
 	tls           [4]uintptr // thread-local storage (for x86 extern register)
 	mstartfn      func()
 	curg          *g       // current running goroutine
@@ -469,15 +470,16 @@ type sigtabtt struct {
 }
 
 const (
-	_SigNotify   = 1 << 0 // let signal.Notify have signal, even if from kernel
-	_SigKill     = 1 << 1 // if signal.Notify doesn't take it, exit quietly
-	_SigThrow    = 1 << 2 // if signal.Notify doesn't take it, exit loudly
-	_SigPanic    = 1 << 3 // if the signal is from the kernel, panic
-	_SigDefault  = 1 << 4 // if the signal isn't explicitly requested, don't monitor it
-	_SigHandling = 1 << 5 // our signal handler is registered
-	_SigIgnored  = 1 << 6 // the signal was ignored before we registered for it
-	_SigGoExit   = 1 << 7 // cause all runtime procs to exit (only used on Plan 9).
-	_SigSetStack = 1 << 8 // add SA_ONSTACK to libc handler
+	_SigNotify   = 1 << iota // let signal.Notify have signal, even if from kernel
+	_SigKill                 // if signal.Notify doesn't take it, exit quietly
+	_SigThrow                // if signal.Notify doesn't take it, exit loudly
+	_SigPanic                // if the signal is from the kernel, panic
+	_SigDefault              // if the signal isn't explicitly requested, don't monitor it
+	_SigHandling             // our signal handler is registered
+	_SigIgnored              // the signal was ignored before we registered for it
+	_SigGoExit               // cause all runtime procs to exit (only used on Plan 9).
+	_SigSetStack             // add SA_ONSTACK to libc handler
+	_SigUnblock              // unblocked in minit
 )
 
 // Layout of in-memory per-function information prepared by linker
diff --git a/src/runtime/signal1_unix.go b/src/runtime/signal1_unix.go
index 7577d43a64..d3e9dac097 100644
--- a/src/runtime/signal1_unix.go
+++ b/src/runtime/signal1_unix.go
@@ -19,6 +19,19 @@ const (
 // Signal forwarding is currently available only on Linux.
 var fwdSig [_NSIG]uintptr
 
+// sigmask represents a general signal mask compatible with the GOOS
+// specific sigset types: the signal numbered x is represented by bit x-1
+// to match the representation expected by sigprocmask.
+type sigmask [(_NSIG + 31) / 32]uint32
+
+// channels for synchronizing signal mask updates with the signal mask
+// thread
+var (
+	disableSigChan  chan uint32
+	enableSigChan   chan uint32
+	maskUpdatedChan chan struct{}
+)
+
 func initsig() {
 	// _NSIG is the number of signals on this operating system.
 	// sigtable should describe what to do for all the possible signals.
@@ -61,12 +74,17 @@ func sigenable(sig uint32) {
 	}
 
 	t := &sigtable[sig]
-	if t.flags&_SigNotify != 0 && t.flags&_SigHandling == 0 {
-		t.flags |= _SigHandling
-		if getsig(int32(sig)) == _SIG_IGN {
-			t.flags |= _SigIgnored
+	if t.flags&_SigNotify != 0 {
+		ensureSigM()
+		enableSigChan <- sig
+		<-maskUpdatedChan
+		if t.flags&_SigHandling == 0 {
+			t.flags |= _SigHandling
+			if getsig(int32(sig)) == _SIG_IGN {
+				t.flags |= _SigIgnored
+			}
+			setsig(int32(sig), funcPC(sighandler), true)
 		}
-		setsig(int32(sig), funcPC(sighandler), true)
 	}
 }
 
@@ -76,12 +94,17 @@ func sigdisable(sig uint32) {
 	}
 
 	t := &sigtable[sig]
-	if t.flags&_SigNotify != 0 && t.flags&_SigHandling != 0 {
-		t.flags &^= _SigHandling
-		if t.flags&_SigIgnored != 0 {
-			setsig(int32(sig), _SIG_IGN, true)
-		} else {
-			setsig(int32(sig), _SIG_DFL, true)
+	if t.flags&_SigNotify != 0 {
+		ensureSigM()
+		disableSigChan <- sig
+		<-maskUpdatedChan
+		if t.flags&_SigHandling != 0 {
+			t.flags &^= _SigHandling
+			if t.flags&_SigIgnored != 0 {
+				setsig(int32(sig), _SIG_IGN, true)
+			} else {
+				setsig(int32(sig), _SIG_DFL, true)
+			}
 		}
 	}
 }
@@ -130,7 +153,52 @@ func crash() {
 		}
 	}
 
-	unblocksignals()
+	updatesigmask(sigmask{})
 	setsig(_SIGABRT, _SIG_DFL, false)
 	raise(_SIGABRT)
 }
+
+// createSigM starts one global, sleeping thread to make sure at least one thread
+// is available to catch signals enabled for os/signal.
+func ensureSigM() {
+	if maskUpdatedChan != nil {
+		return
+	}
+	maskUpdatedChan = make(chan struct{})
+	disableSigChan = make(chan uint32)
+	enableSigChan = make(chan uint32)
+	go func() {
+		// Signal masks are per-thread, so make sure this goroutine stays on one
+		// thread.
+		LockOSThread()
+		defer UnlockOSThread()
+		// The sigBlocked mask contains the signals not active for os/signal,
+		// initially all signals except the essential. When signal.Notify()/Stop is called,
+		// sigenable/sigdisable in turn notify this thread to update its signal
+		// mask accordingly.
+		var sigBlocked sigmask
+		for i := range sigBlocked {
+			sigBlocked[i] = ^uint32(0)
+		}
+		for i := range sigtable {
+			if sigtable[i].flags&_SigUnblock != 0 {
+				sigBlocked[(i-1)/32] &^= 1 << ((uint32(i) - 1) & 31)
+			}
+		}
+		updatesigmask(sigBlocked)
+		for {
+			select {
+			case sig := <-enableSigChan:
+				if b := sig - 1; b >= 0 {
+					sigBlocked[b/32] &^= (1 << (b & 31))
+				}
+			case sig := <-disableSigChan:
+				if b := sig - 1; b >= 0 {
+					sigBlocked[b/32] |= (1 << (b & 31))
+				}
+			}
+			updatesigmask(sigBlocked)
+			maskUpdatedChan <- struct{}{}
+		}
+	}()
+}
diff --git a/src/runtime/signal_darwin.go b/src/runtime/signal_darwin.go
index 32ecce0d7d..6cd18653d5 100644
--- a/src/runtime/signal_darwin.go
+++ b/src/runtime/signal_darwin.go
@@ -16,14 +16,14 @@ var sigtable = [...]sigTabT{
 	/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
 	/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
 	/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
-	/* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
-	/* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+	/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+	/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
 	/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
 	/* 7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
-	/* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+	/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
 	/* 9 */ {0, "SIGKILL: kill"},
-	/* 10 */ {_SigPanic, "SIGBUS: bus error"},
-	/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+	/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+	/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
 	/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
 	/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
 	/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
@@ -32,14 +32,14 @@ var sigtable = [...]sigTabT{
 	/* 17 */ {0, "SIGSTOP: stop"},
 	/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
 	/* 19 */ {0, "SIGCONT: continue after stop"},
-	/* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
+	/* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
 	/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
 	/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
 	/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
 	/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
 	/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
 	/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
-	/* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+	/* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
 	/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
 	/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
 	/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
diff --git a/src/runtime/signal_linux.go b/src/runtime/signal_linux.go
index f8250b9fa1..2f25b59663 100644
--- a/src/runtime/signal_linux.go
+++ b/src/runtime/signal_linux.go
@@ -16,20 +16,20 @@ var sigtable = [...]sigTabT{
 	/* 1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
 	/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
 	/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
-	/* 4 */ {_SigThrow, "SIGILL: illegal instruction"},
-	/* 5 */ {_SigThrow, "SIGTRAP: trace trap"},
+	/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+	/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
 	/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
-	/* 7 */ {_SigPanic, "SIGBUS: bus error"},
-	/* 8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+	/* 7 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+	/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
 	/* 9 */ {0, "SIGKILL: kill"},
 	/* 10 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
-	/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+	/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
 	/* 12 */ {_SigNotify, "SIGUSR2: user-defined signal 2"},
 	/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
 	/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
 	/* 15 */ {_SigNotify + _SigKill, "SIGTERM: termination"},
-	/* 16 */ {_SigThrow, "SIGSTKFLT: stack fault"},
-	/* 17 */ {_SigNotify, "SIGCHLD: child status has changed"},
+	/* 16 */ {_SigThrow + _SigUnblock, "SIGSTKFLT: stack fault"},
+	/* 17 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
 	/* 18 */ {0, "SIGCONT: continue"},
 	/* 19 */ {0, "SIGSTOP: stop, unblockable"},
 	/* 20 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
@@ -39,7 +39,7 @@ var sigtable = [...]sigTabT{
 	/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
 	/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
 	/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
-	/* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+	/* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
 	/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
 	/* 29 */ {_SigNotify, "SIGIO: i/o now possible"},
 	/* 30 */ {_SigNotify, "SIGPWR: power failure restart"},
diff --git a/src/runtime/signal_netbsd.go b/src/runtime/signal_netbsd.go
index 78afc59efa..d93a450d98 100644
--- a/src/runtime/signal_netbsd.go
+++ b/src/runtime/signal_netbsd.go
@@ -14,14 +14,14 @@ var sigtable = [...]sigTabT{
 	/*  1 */ {_SigNotify + _SigKill, "SIGHUP: terminal line hangup"},
 	/*  2 */ {_SigNotify + _SigKill, "SIGINT: interrupt"},
 	/*  3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit"},
-	/*  4 */ {_SigThrow, "SIGILL: illegal instruction"},
-	/*  5 */ {_SigThrow, "SIGTRAP: trace trap"},
+	/*  4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction"},
+	/*  5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap"},
 	/*  6 */ {_SigNotify + _SigThrow, "SIGABRT: abort"},
 	/*  7 */ {_SigThrow, "SIGEMT: emulate instruction executed"},
-	/*  8 */ {_SigPanic, "SIGFPE: floating-point exception"},
+	/*  8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating-point exception"},
 	/*  9 */ {0, "SIGKILL: kill"},
-	/* 10 */ {_SigPanic, "SIGBUS: bus error"},
-	/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+	/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+	/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
 	/* 12 */ {_SigThrow, "SIGSYS: bad system call"},
 	/* 13 */ {_SigNotify, "SIGPIPE: write to broken pipe"},
 	/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
@@ -30,14 +30,14 @@ var sigtable = [...]sigTabT{
 	/* 17 */ {0, "SIGSTOP: stop"},
 	/* 18 */ {_SigNotify + _SigDefault, "SIGTSTP: keyboard stop"},
 	/* 19 */ {0, "SIGCONT: continue after stop"},
-	/* 20 */ {_SigNotify, "SIGCHLD: child status has changed"},
+	/* 20 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status has changed"},
 	/* 21 */ {_SigNotify + _SigDefault, "SIGTTIN: background read from tty"},
 	/* 22 */ {_SigNotify + _SigDefault, "SIGTTOU: background write to tty"},
 	/* 23 */ {_SigNotify, "SIGIO: i/o now possible"},
 	/* 24 */ {_SigNotify, "SIGXCPU: cpu limit exceeded"},
 	/* 25 */ {_SigNotify, "SIGXFSZ: file size limit exceeded"},
 	/* 26 */ {_SigNotify, "SIGVTALRM: virtual alarm clock"},
-	/* 27 */ {_SigNotify, "SIGPROF: profiling alarm clock"},
+	/* 27 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling alarm clock"},
 	/* 28 */ {_SigNotify, "SIGWINCH: window size change"},
 	/* 29 */ {_SigNotify, "SIGINFO: status request from keyboard"},
 	/* 30 */ {_SigNotify, "SIGUSR1: user-defined signal 1"},
diff --git a/src/runtime/signal_solaris.go b/src/runtime/signal_solaris.go
index 2986c5aabc..d8ac676846 100644
--- a/src/runtime/signal_solaris.go
+++ b/src/runtime/signal_solaris.go
@@ -14,21 +14,21 @@ var sigtable = [...]sigTabT{
 	/* 1 */ {_SigNotify + _SigKill, "SIGHUP: hangup"},
 	/* 2 */ {_SigNotify + _SigKill, "SIGINT: interrupt (rubout)"},
 	/* 3 */ {_SigNotify + _SigThrow, "SIGQUIT: quit (ASCII FS)"},
-	/* 4 */ {_SigThrow, "SIGILL: illegal instruction (not reset when caught)"},
-	/* 5 */ {_SigThrow, "SIGTRAP: trace trap (not reset when caught)"},
+	/* 4 */ {_SigThrow + _SigUnblock, "SIGILL: illegal instruction (not reset when caught)"},
+	/* 5 */ {_SigThrow + _SigUnblock, "SIGTRAP: trace trap (not reset when caught)"},
 	/* 6 */ {_SigNotify + _SigThrow, "SIGABRT: used by abort, replace SIGIOT in the future"},
 	/* 7 */ {_SigThrow, "SIGEMT: EMT instruction"},
-	/* 8 */ {_SigPanic, "SIGFPE: floating point exception"},
+	/* 8 */ {_SigPanic + _SigUnblock, "SIGFPE: floating point exception"},
 	/* 9 */ {0, "SIGKILL: kill (cannot be caught or ignored)"},
-	/* 10 */ {_SigPanic, "SIGBUS: bus error"},
-	/* 11 */ {_SigPanic, "SIGSEGV: segmentation violation"},
+	/* 10 */ {_SigPanic + _SigUnblock, "SIGBUS: bus error"},
+	/* 11 */ {_SigPanic + _SigUnblock, "SIGSEGV: segmentation violation"},
 	/* 12 */ {_SigThrow, "SIGSYS: bad argument to system call"},
 	/* 13 */ {_SigNotify, "SIGPIPE: write on a pipe with no one to read it"},
 	/* 14 */ {_SigNotify, "SIGALRM: alarm clock"},
 	/* 15 */ {_SigNotify + _SigKill, "SIGTERM: software termination signal from kill"},
 	/* 16 */ {_SigNotify, "SIGUSR1: user defined signal 1"},
 	/* 17 */ {_SigNotify, "SIGUSR2: user defined signal 2"},
-	/* 18 */ {_SigNotify, "SIGCHLD: child status change alias (POSIX)"},
+	/* 18 */ {_SigNotify + _SigUnblock, "SIGCHLD: child status change alias (POSIX)"},
 	/* 19 */ {_SigNotify, "SIGPWR: power-fail restart"},
 	/* 20 */ {_SigNotify, "SIGWINCH: window size change"},
 	/* 21 */ {_SigNotify, "SIGURG: urgent socket condition"},
@@ -39,7 +39,7 @@ var sigtable = [...]sigTabT{
 	/* 26 */ {_SigNotify + _SigDefault, "SIGTTIN: background tty read attempted"},
 	/* 27 */ {_SigNotify + _SigDefault, "SIGTTOU: background tty write attempted"},
 	/* 28 */ {_SigNotify, "SIGVTALRM: virtual timer expired"},
-	/* 29 */ {_SigNotify, "SIGPROF: profiling timer expired"},
+	/* 29 */ {_SigNotify + _SigUnblock, "SIGPROF: profiling timer expired"},
 	/* 30 */ {_SigNotify, "SIGXCPU: exceeded cpu limit"},
 	/* 31 */ {_SigNotify, "SIGXFSZ: exceeded file size limit"},
 	/* 32 */ {_SigNotify, "SIGWAITING: reserved signal no longer used by"},

From 79afb43a0a42abb435c9a7596d66759eb7af18f2 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 21 May 2015 17:00:37 -0700
Subject: [PATCH 186/232] math/big: fix Float.Float32 conversion for denormal
 corner cases

The existing code was incorrect for numbers that after rounding would
become the smallest denormal float32 (instead the result was 0). This
caused all.bash to fail if Float32() were used in the compiler for
constant arithmetic (there's currently a work-around - see also issue
10321.

This change fixes the implementation of Float.Float32 and adds
corresponding test cases. Float32 and Float64 diverge at this point.
For ease of review, this change only fixes Float32. Float64 will be
made to match in a subsequent change.

Fixes #10321.

Change-Id: Iccafe37c1593a4946bc552e4ad2045f69be62d80
Reviewed-on: https://go-review.googlesource.com/10350
Reviewed-by: Alan Donovan 
---
 src/math/big/float.go      | 77 +++++++++++++++++--------------
 src/math/big/float_test.go | 93 ++++++++++++++++++++++++--------------
 2 files changed, 100 insertions(+), 70 deletions(-)

diff --git a/src/math/big/float.go b/src/math/big/float.go
index d46c046e67..b666b9dd38 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -853,9 +853,6 @@ func (x *Float) Int64() (int64, Accuracy) {
 	panic("unreachable")
 }
 
-// TODO(gri) Float32 and Float64 are very similar internally but for the
-// floatxx parameters and some conversions. Should factor out shared code.
-
 // Float32 returns the float32 value nearest to x. If x is too small to be
 // represented by a float32 (|x| < math.SmallestNonzeroFloat32), the result
 // is (0, Below) or (-0, Above), respectively, depending on the sign of x.
@@ -880,64 +877,70 @@ func (x *Float) Float32() (float32, Accuracy) {
 			emax  = bias              //   127  largest unbiased exponent (normal)
 		)
 
-		// Float mantissae m have an explicit msb and are in the range 0.5 <= m < 1.0.
-		// floatxx mantissae have an implicit msb and are in the range 1.0 <= m < 2.0.
-		// For a given mantissa m, we need to add 1 to a floatxx exponent to get the
-		// corresponding Float exponent.
-		// (see also implementation of math.Ldexp for similar code)
+		// Float mantissa m is 0.5 <= m < 1.0; compute exponent for floatxx mantissa.
+		e := x.exp - 1 // exponent for mantissa m with 1.0 <= m < 2.0
+		p := mbits + 1 // precision of normal float
 
-		if x.exp < dmin+1 {
-			// underflow
-			if x.neg {
-				var z float32
-				return -z, Above
+		// If the exponent is too small, we may have a denormal number
+		// in which case we have fewer mantissa bits available: reduce
+		// precision accordingly.
+		if e < emin {
+			p -= emin - int(e)
+			// Make sure we have at least 1 bit so that we don't
+			// lose numbers rounded up to the smallest denormal.
+			if p < 1 {
+				p = 1
 			}
-			return 0.0, Below
 		}
-		// x.exp >= dmin+1
 
+		// round
 		var r Float
-		r.prec = mbits + 1 // +1 for implicit msb
-		if x.exp < emin+1 {
-			// denormal number - round to fewer bits
-			r.prec = uint32(x.exp - dmin)
-		}
+		r.prec = uint32(p)
 		r.Set(x)
+		e = r.exp - 1
 
 		// Rounding may have caused r to overflow to ±Inf
 		// (rounding never causes underflows to 0).
 		if r.form == inf {
-			r.exp = emax + 2 // cause overflow below
+			e = emax + 1 // cause overflow below
 		}
 
-		if r.exp > emax+1 {
+		// If the exponent is too large, overflow to ±Inf.
+		if e > emax {
 			// overflow
 			if x.neg {
 				return float32(math.Inf(-1)), Below
 			}
 			return float32(math.Inf(+1)), Above
 		}
-		// dmin+1 <= r.exp <= emax+1
 
-		var s uint32
-		if r.neg {
-			s = 1 << (fbits - 1)
+		// Determine sign, biased exponent, and mantissa.
+		var sign, bexp, mant uint32
+		if x.neg {
+			sign = 1 << (fbits - 1)
 		}
 
-		m := high32(r.mant) >> ebits & (1<> (fbits - r.prec)
+		} else {
+			// normal number: emin <= e <= emax
+			bexp = uint32(e+bias) << mbits
+			mant = high32(r.mant) >> ebits & (1<
Date: Thu, 21 May 2015 17:52:49 -0700
Subject: [PATCH 187/232] math/big: add more Float.Float64 conversion tests

- structure the Float64 conversion tests the same way as for Float32
- add additional test cases, including one that exposes a current issue
  (currently disabled, same issue as was fixed for Float32)

The Float64 fix will be in a subsequent change for easier reviewing.

Change-Id: I95dc9e8d1f6b6073a98c7bc2289e6d3248fc3420
Reviewed-on: https://go-review.googlesource.com/10351
Reviewed-by: Alan Donovan 
---
 src/math/big/float_test.go | 88 +++++++++++++++++++++++---------------
 1 file changed, 53 insertions(+), 35 deletions(-)

diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index 4da145b652..92eec71c5f 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -209,6 +209,12 @@ func alike32(x, y float32) bool {
 
 }
 
+func alike64(x, y float64) bool {
+	// we can ignore NaNs
+	return x == y && math.Signbit(x) == math.Signbit(y)
+
+}
+
 func TestFloatMantExp(t *testing.T) {
 	for _, test := range []struct {
 		x    string
@@ -908,35 +914,37 @@ func TestFloatFloat64(t *testing.T) {
 		out float64
 		acc Accuracy
 	}{
-		{"-Inf", math.Inf(-1), Exact},
-		{"-0x1.fffffffffffff8p2147483646", -math.Inf(+1), Below}, // overflow in rounding
-		{"-1e10000", math.Inf(-1), Below},                        // overflow
-		{"-0x1p1024", math.Inf(-1), Below},                       // overflow
-		{"-0x1.fffffffffffff8p1023", -math.Inf(+1), Below},       // overflow
-		{"-0x1.fffffffffffff4p1023", -math.MaxFloat64, Above},
-		{"-0x1.fffffffffffff0p1023", -math.MaxFloat64, Exact},
-		{"-12345.000000000000000000001", -12345, Above},
-		{"-12345.0", -12345, Exact},
-		{"-1.000000000000000000001", -1, Above},
-		{"-1", -1, Exact},
-		{"-0x0.0000000000001p-1022", -math.SmallestNonzeroFloat64, Exact},
-		{"-0x0.0000000000001p-1023", -0, Above}, // underflow
-		{"-1e-1000", -0, Above},                 // underflow
 		{"0", 0, Exact},
-		{"1e-1000", 0, Below},                 // underflow
-		{"0x0.0000000000001p-1023", 0, Below}, // underflow
-		{"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64, Exact},
+
+		// underflow
+		{"1e-1000", 0, Below},
+		{"0x0.0000000000001p-1023", 0, Below},
+		{"0x0.00000000000008p-1022", 0, Below},
+
+		// denormals
+		// TODO(gri) enable once Float64 is fixed
+		// {"0x0.0000000000000cp-1022", math.SmallestNonzeroFloat64, Above}, // rounded up to smallest denormal
+		{"0x0.0000000000001p-1022", math.SmallestNonzeroFloat64, Exact}, // smallest denormal
+		{"0x.8p-1073", math.SmallestNonzeroFloat64, Exact},
+		{"1p-1074", math.SmallestNonzeroFloat64, Exact},
+		{"0x.fffffffffffffp-1022", math.Float64frombits(0x000fffffffffffff), Exact}, // largest denormal
+
+		// normals
+		{"0x.fffffffffffff8p-1022", math.Float64frombits(0x0010000000000000), Above}, // rounded up to smallest normal
+		{"1p-1022", math.Float64frombits(0x0010000000000000), Exact},                 // smallest normal
 		{"1", 1, Exact},
 		{"1.000000000000000000001", 1, Below},
 		{"12345.0", 12345, Exact},
 		{"12345.000000000000000000001", 12345, Below},
 		{"0x1.fffffffffffff0p1023", math.MaxFloat64, Exact},
 		{"0x1.fffffffffffff4p1023", math.MaxFloat64, Below},
-		{"0x1.fffffffffffff8p1023", math.Inf(+1), Above},       // overflow
-		{"0x1p1024", math.Inf(+1), Above},                      // overflow
-		{"1e10000", math.Inf(+1), Above},                       // overflow
+
+		// overflow
+		{"0x1.fffffffffffff8p1023", math.Inf(+1), Above},
+		{"0x1p1024", math.Inf(+1), Above},
+		{"1e10000", math.Inf(+1), Above},
 		{"0x1.fffffffffffff8p2147483646", math.Inf(+1), Above}, // overflow in rounding
-		{"+Inf", math.Inf(+1), Exact},
+		{"Inf", math.Inf(+1), Exact},
 
 		// selected denormalized values that were handled incorrectly in the past
 		{"0x.fffffffffffffp-1022", smallestNormalFloat64 - math.SmallestNonzeroFloat64, Exact},
@@ -947,22 +955,32 @@ func TestFloatFloat64(t *testing.T) {
 		// http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/
 		{"2.2250738585072012e-308", 2.2250738585072014e-308, Above},
 	} {
-		// conversion should match strconv where syntax is agreeable
-		if f, err := strconv.ParseFloat(test.x, 64); err == nil && f != test.out {
-			t.Errorf("%s: got %g; want %g (incorrect test data)", test.x, f, test.out)
-		}
+		for i := 0; i < 2; i++ {
+			// test both signs
+			tx, tout, tacc := test.x, test.out, test.acc
+			if i != 0 {
+				tx = "-" + tx
+				tout = -tout
+				tacc = -tacc
+			}
 
-		x := makeFloat(test.x)
-		out, acc := x.Float64()
-		if out != test.out || acc != test.acc {
-			t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", test.x, out, math.Float64bits(out), acc, test.out, math.Float64bits(test.out), test.acc)
-		}
+			// conversion should match strconv where syntax is agreeable
+			if f, err := strconv.ParseFloat(tx, 64); err == nil && !alike64(f, tout) {
+				t.Errorf("%s: got %g; want %g (incorrect test data)", tx, f, tout)
+			}
 
-		// test that x.SetFloat64(f).Float64() == f
-		var x2 Float
-		out2, acc2 := x2.SetFloat64(out).Float64()
-		if out2 != out || acc2 != Exact {
-			t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+			x := makeFloat(tx)
+			out, acc := x.Float64()
+			if !alike64(out, tout) || acc != tacc {
+				t.Errorf("%s: got %g (%#x, %s); want %g (%#x, %s)", tx, out, math.Float64bits(out), acc, test.out, math.Float64bits(test.out), tacc)
+			}
+
+			// test that x.SetFloat64(f).Float64() == f
+			var x2 Float
+			out2, acc2 := x2.SetFloat64(out).Float64()
+			if !alike64(out2, out) || acc2 != Exact {
+				t.Errorf("idempotency test: got %g (%s); want %g (Exact)", out2, acc2, out)
+			}
 		}
 	}
 }

From 5ffbca4823455d42ef6314c67c1cc346599d65bf Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 21 May 2015 18:00:54 -0700
Subject: [PATCH 188/232] math/big: fix Float.Float64 conversion for denormal
 corner cases

- This change uses the same code as for Float32 and fixes the case
  of a number that gets rounded up to the smallest denormal.

- Enabled correspoding test case.

Change-Id: I8aac874a566cd727863a82717854f603fbdc26c6
Reviewed-on: https://go-review.googlesource.com/10352
Reviewed-by: Alan Donovan 
---
 src/math/big/float.go      | 74 +++++++++++++++++++-------------------
 src/math/big/float_test.go |  5 ++-
 2 files changed, 40 insertions(+), 39 deletions(-)

diff --git a/src/math/big/float.go b/src/math/big/float.go
index b666b9dd38..dcb72c5754 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -959,10 +959,6 @@ func (x *Float) Float32() (float32, Accuracy) {
 	panic("unreachable")
 }
 
-// TODO(gri) Use same algorithm for Float64 as for Float32. The Float64 code
-// is incorrect for some corner cases (numbers that get rounded up to smallest
-// denormal - test cases are missing).
-
 // Float64 returns the float64 value nearest to x. If x is too small to be
 // represented by a float64 (|x| < math.SmallestNonzeroFloat64), the result
 // is (0, Below) or (-0, Above), respectively, depending on the sign of x.
@@ -987,64 +983,70 @@ func (x *Float) Float64() (float64, Accuracy) {
 			emax  = bias              //  1023  largest unbiased exponent (normal)
 		)
 
-		// Float mantissae m have an explicit msb and are in the range 0.5 <= m < 1.0.
-		// floatxx mantissae have an implicit msb and are in the range 1.0 <= m < 2.0.
-		// For a given mantissa m, we need to add 1 to a floatxx exponent to get the
-		// corresponding Float exponent.
-		// (see also implementation of math.Ldexp for similar code)
+		// Float mantissa m is 0.5 <= m < 1.0; compute exponent for floatxx mantissa.
+		e := x.exp - 1 // exponent for mantissa m with 1.0 <= m < 2.0
+		p := mbits + 1 // precision of normal float
 
-		if x.exp < dmin+1 {
-			// underflow
-			if x.neg {
-				var z float64
-				return -z, Above
+		// If the exponent is too small, we may have a denormal number
+		// in which case we have fewer mantissa bits available: reduce
+		// precision accordingly.
+		if e < emin {
+			p -= emin - int(e)
+			// Make sure we have at least 1 bit so that we don't
+			// lose numbers rounded up to the smallest denormal.
+			if p < 1 {
+				p = 1
 			}
-			return 0.0, Below
 		}
-		// x.exp >= dmin+1
 
+		// round
 		var r Float
-		r.prec = mbits + 1 // +1 for implicit msb
-		if x.exp < emin+1 {
-			// denormal number - round to fewer bits
-			r.prec = uint32(x.exp - dmin)
-		}
+		r.prec = uint32(p)
 		r.Set(x)
+		e = r.exp - 1
 
 		// Rounding may have caused r to overflow to ±Inf
 		// (rounding never causes underflows to 0).
 		if r.form == inf {
-			r.exp = emax + 2 // cause overflow below
+			e = emax + 1 // cause overflow below
 		}
 
-		if r.exp > emax+1 {
+		// If the exponent is too large, overflow to ±Inf.
+		if e > emax {
 			// overflow
 			if x.neg {
 				return math.Inf(-1), Below
 			}
 			return math.Inf(+1), Above
 		}
-		// dmin+1 <= r.exp <= emax+1
 
-		var s uint64
-		if r.neg {
-			s = 1 << (fbits - 1)
+		// Determine sign, biased exponent, and mantissa.
+		var sign, bexp, mant uint64
+		if x.neg {
+			sign = 1 << (fbits - 1)
 		}
 
-		m := high64(r.mant) >> ebits & (1<> (fbits - r.prec)
+		} else {
+			// normal number: emin <= e <= emax
+			bexp = uint64(e+bias) << mbits
+			mant = high64(r.mant) >> ebits & (1<
Date: Thu, 21 May 2015 18:13:44 -0700
Subject: [PATCH 189/232] cmd/compile/internal/big: update to latest version
 (run sh vendor.bash)

No manual code changes.

This will permit addressing the compiler aspect of issue #10321 in a
subsequent change.

Change-Id: I3376dc38cafa0ec98bf54de33293015d0183cc82
Reviewed-on: https://go-review.googlesource.com/10354
Reviewed-by: Josh Bleecher Snyder 
---
 src/cmd/compile/internal/big/arith.go      |   1 -
 src/cmd/compile/internal/big/arith_test.go |   1 +
 src/cmd/compile/internal/big/float.go      | 151 +++++++++--------
 src/cmd/compile/internal/big/float_test.go | 183 +++++++++++++--------
 src/cmd/compile/internal/big/int.go        | 118 +++++++++++++
 src/cmd/compile/internal/big/int_test.go   | 138 ++++++++++++++++
 src/cmd/compile/internal/big/nat.go        | 121 +++++++++++++-
 src/cmd/compile/internal/big/nat_test.go   |  61 +++++++
 8 files changed, 633 insertions(+), 141 deletions(-)

diff --git a/src/cmd/compile/internal/big/arith.go b/src/cmd/compile/internal/big/arith.go
index 328c85c4f7..1ff6349d9d 100644
--- a/src/cmd/compile/internal/big/arith.go
+++ b/src/cmd/compile/internal/big/arith.go
@@ -196,7 +196,6 @@ func subVV_g(z, x, y []Word) (c Word) {
 	return
 }
 
-// Argument y must be either 0 or 1.
 // The resulting carry c is either 0 or 1.
 func addVW_g(z, x []Word, y Word) (c Word) {
 	if use_addWW_g {
diff --git a/src/cmd/compile/internal/big/arith_test.go b/src/cmd/compile/internal/big/arith_test.go
index cd92dd7173..f46a494f17 100644
--- a/src/cmd/compile/internal/big/arith_test.go
+++ b/src/cmd/compile/internal/big/arith_test.go
@@ -155,6 +155,7 @@ var sumVW = []argVW{
 	{nat{1}, nat{1}, 0, 0},
 	{nat{0}, nat{_M}, 1, 1},
 	{nat{0, 0, 0, 0}, nat{_M, _M, _M, _M}, 1, 1},
+	{nat{585}, nat{314}, 271, 0},
 }
 
 var prodVW = []argVW{
diff --git a/src/cmd/compile/internal/big/float.go b/src/cmd/compile/internal/big/float.go
index ed55e8e513..dcb72c5754 100644
--- a/src/cmd/compile/internal/big/float.go
+++ b/src/cmd/compile/internal/big/float.go
@@ -65,12 +65,16 @@ type Float struct {
 	exp  int32
 }
 
-// Float operations that would lead to a NaN under IEEE-754 rules cause
-// a run-time panic of ErrNaN type.
+// An ErrNaN panic is raised by a Float operation that would lead to
+// a NaN under IEEE-754 rules. An ErrNaN implements the error interface.
 type ErrNaN struct {
 	msg string
 }
 
+func (err ErrNaN) Error() string {
+	return err.msg
+}
+
 // NewFloat allocates and returns a new Float set to x,
 // with precision 53 and rounding mode ToNearestEven.
 // NewFloat panics with ErrNaN if x is a NaN.
@@ -849,9 +853,6 @@ func (x *Float) Int64() (int64, Accuracy) {
 	panic("unreachable")
 }
 
-// TODO(gri) Float32 and Float64 are very similar internally but for the
-// floatxx parameters and some conversions. Should factor out shared code.
-
 // Float32 returns the float32 value nearest to x. If x is too small to be
 // represented by a float32 (|x| < math.SmallestNonzeroFloat32), the result
 // is (0, Below) or (-0, Above), respectively, depending on the sign of x.
@@ -876,64 +877,70 @@ func (x *Float) Float32() (float32, Accuracy) {
 			emax  = bias              //   127  largest unbiased exponent (normal)
 		)
 
-		// Float mantissae m have an explicit msb and are in the range 0.5 <= m < 1.0.
-		// floatxx mantissae have an implicit msb and are in the range 1.0 <= m < 2.0.
-		// For a given mantissa m, we need to add 1 to a floatxx exponent to get the
-		// corresponding Float exponent.
-		// (see also implementation of math.Ldexp for similar code)
+		// Float mantissa m is 0.5 <= m < 1.0; compute exponent for floatxx mantissa.
+		e := x.exp - 1 // exponent for mantissa m with 1.0 <= m < 2.0
+		p := mbits + 1 // precision of normal float
 
-		if x.exp < dmin+1 {
-			// underflow
-			if x.neg {
-				var z float32
-				return -z, Above
+		// If the exponent is too small, we may have a denormal number
+		// in which case we have fewer mantissa bits available: reduce
+		// precision accordingly.
+		if e < emin {
+			p -= emin - int(e)
+			// Make sure we have at least 1 bit so that we don't
+			// lose numbers rounded up to the smallest denormal.
+			if p < 1 {
+				p = 1
 			}
-			return 0.0, Below
 		}
-		// x.exp >= dmin+1
 
+		// round
 		var r Float
-		r.prec = mbits + 1 // +1 for implicit msb
-		if x.exp < emin+1 {
-			// denormal number - round to fewer bits
-			r.prec = uint32(x.exp - dmin)
-		}
+		r.prec = uint32(p)
 		r.Set(x)
+		e = r.exp - 1
 
 		// Rounding may have caused r to overflow to ±Inf
 		// (rounding never causes underflows to 0).
 		if r.form == inf {
-			r.exp = emax + 2 // cause overflow below
+			e = emax + 1 // cause overflow below
 		}
 
-		if r.exp > emax+1 {
+		// If the exponent is too large, overflow to ±Inf.
+		if e > emax {
 			// overflow
 			if x.neg {
 				return float32(math.Inf(-1)), Below
 			}
 			return float32(math.Inf(+1)), Above
 		}
-		// dmin+1 <= r.exp <= emax+1
 
-		var s uint32
-		if r.neg {
-			s = 1 << (fbits - 1)
+		// Determine sign, biased exponent, and mantissa.
+		var sign, bexp, mant uint32
+		if x.neg {
+			sign = 1 << (fbits - 1)
 		}
 
-		m := high32(r.mant) >> ebits & (1<> (fbits - r.prec)
+		} else {
+			// normal number: emin <= e <= emax
+			bexp = uint32(e+bias) << mbits
+			mant = high32(r.mant) >> ebits & (1<= dmin+1
 
+		// round
 		var r Float
-		r.prec = mbits + 1 // +1 for implicit msb
-		if x.exp < emin+1 {
-			// denormal number - round to fewer bits
-			r.prec = uint32(x.exp - dmin)
-		}
+		r.prec = uint32(p)
 		r.Set(x)
+		e = r.exp - 1
 
 		// Rounding may have caused r to overflow to ±Inf
 		// (rounding never causes underflows to 0).
 		if r.form == inf {
-			r.exp = emax + 2 // cause overflow below
+			e = emax + 1 // cause overflow below
 		}
 
-		if r.exp > emax+1 {
+		// If the exponent is too large, overflow to ±Inf.
+		if e > emax {
 			// overflow
 			if x.neg {
 				return math.Inf(-1), Below
 			}
 			return math.Inf(+1), Above
 		}
-		// dmin+1 <= r.exp <= emax+1
 
-		var s uint64
-		if r.neg {
-			s = 1 << (fbits - 1)
+		// Determine sign, biased exponent, and mantissa.
+		var sign, bexp, mant uint64
+		if x.neg {
+			sign = 1 << (fbits - 1)
 		}
 
-		m := high64(r.mant) >> ebits & (1<> (fbits - r.prec)
+		} else {
+			// normal number: emin <= e <= emax
+			bexp = uint64(e+bias) << mbits
+			mant = high64(r.mant) >> ebits & (1< 0
+
+		// handle factors of 2 in 'a'
+		s := a.abs.trailingZeroBits()
+		if s&1 != 0 {
+			bmod8 := b.abs[0] & 7
+			if bmod8 == 3 || bmod8 == 5 {
+				j = -j
+			}
+		}
+		c.Rsh(&a, s) // a = 2^s*c
+
+		// swap numerator and denominator
+		if b.abs[0]&3 == 3 && c.abs[0]&3 == 3 {
+			j = -j
+		}
+		a.Set(&b)
+		b.Set(&c)
+	}
+}
+
+// ModSqrt sets z to a square root of x mod p if such a square root exists, and
+// returns z. The modulus p must be an odd prime. If x is not a square mod p,
+// ModSqrt leaves z unchanged and returns nil. This function panics if p is
+// not an odd integer.
+func (z *Int) ModSqrt(x, p *Int) *Int {
+	switch Jacobi(x, p) {
+	case -1:
+		return nil // x is not a square mod p
+	case 0:
+		return z.SetInt64(0) // sqrt(0) mod p = 0
+	case 1:
+		break
+	}
+	if x.neg || x.Cmp(p) >= 0 { // ensure 0 <= x < p
+		x = new(Int).Mod(x, p)
+	}
+
+	// Break p-1 into s*2^e such that s is odd.
+	var s Int
+	s.Sub(p, intOne)
+	e := s.abs.trailingZeroBits()
+	s.Rsh(&s, e)
+
+	// find some non-square n
+	var n Int
+	n.SetInt64(2)
+	for Jacobi(&n, p) != -1 {
+		n.Add(&n, intOne)
+	}
+
+	// Core of the Tonelli-Shanks algorithm. Follows the description in
+	// section 6 of "Square roots from 1; 24, 51, 10 to Dan Shanks" by Ezra
+	// Brown:
+	// https://www.maa.org/sites/default/files/pdf/upload_library/22/Polya/07468342.di020786.02p0470a.pdf
+	var y, b, g, t Int
+	y.Add(&s, intOne)
+	y.Rsh(&y, 1)
+	y.Exp(x, &y, p)  // y = x^((s+1)/2)
+	b.Exp(x, &s, p)  // b = x^s
+	g.Exp(&n, &s, p) // g = n^s
+	r := e
+	for {
+		// find the least m such that ord_p(b) = 2^m
+		var m uint
+		t.Set(&b)
+		for t.Cmp(intOne) != 0 {
+			t.Mul(&t, &t).Mod(&t, p)
+			m++
+		}
+
+		if m == 0 {
+			return z.Set(&y)
+		}
+
+		t.SetInt64(0).SetBit(&t, int(r-m-1), 1).Exp(&g, &t, p)
+		// t = g^(2^(r-m-1)) mod p
+		g.Mul(&t, &t).Mod(&g, p) // g = g^(2^(r-m)) mod p
+		y.Mul(&y, &t).Mod(&y, p)
+		b.Mul(&b, &g).Mod(&b, p)
+		r = m
+	}
+}
+
 // Lsh sets z = x << n and returns z.
 func (z *Int) Lsh(x *Int, n uint) *Int {
 	z.abs = z.abs.shl(x.abs, n)
diff --git a/src/cmd/compile/internal/big/int_test.go b/src/cmd/compile/internal/big/int_test.go
index a972a7249b..c19e88addb 100644
--- a/src/cmd/compile/internal/big/int_test.go
+++ b/src/cmd/compile/internal/big/int_test.go
@@ -525,6 +525,7 @@ var expTests = []struct {
 	{"1234", "-1", "1", "0"},
 
 	// misc
+	{"5", "1", "3", "2"},
 	{"5", "-7", "", "1"},
 	{"-5", "-7", "", "1"},
 	{"5", "0", "", "1"},
@@ -703,6 +704,13 @@ var primes = []string{
 	"230975859993204150666423538988557839555560243929065415434980904258310530753006723857139742334640122533598517597674807096648905501653461687601339782814316124971547968912893214002992086353183070342498989426570593",
 	"5521712099665906221540423207019333379125265462121169655563495403888449493493629943498064604536961775110765377745550377067893607246020694972959780839151452457728855382113555867743022746090187341871655890805971735385789993",
 	"203956878356401977405765866929034577280193993314348263094772646453283062722701277632936616063144088173312372882677123879538709400158306567338328279154499698366071906766440037074217117805690872792848149112022286332144876183376326512083574821647933992961249917319836219304274280243803104015000563790123",
+
+	// ECC primes: http://tools.ietf.org/html/draft-ladd-safecurves-02
+	"3618502788666131106986593281521497120414687020801267626233049500247285301239",                                                                                  // Curve1174: 2^251-9
+	"57896044618658097711785492504343953926634992332820282019728792003956564819949",                                                                                 // Curve25519: 2^255-19
+	"9850501549098619803069760025035903451269934817616361666987073351061430442874302652853566563721228910201656997576599",                                           // E-382: 2^382-105
+	"42307582002575910332922579714097346549017899709713998034217522897561970639123926132812109468141778230245837569601494931472367",                                 // Curve41417: 2^414-17
+	"6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151", // E-521: 2^521-1
 }
 
 var composites = []string{
@@ -1248,6 +1256,136 @@ func TestModInverse(t *testing.T) {
 	}
 }
 
+// testModSqrt is a helper for TestModSqrt,
+// which checks that ModSqrt can compute a square-root of elt^2.
+func testModSqrt(t *testing.T, elt, mod, sq, sqrt *Int) bool {
+	var sqChk, sqrtChk, sqrtsq Int
+	sq.Mul(elt, elt)
+	sq.Mod(sq, mod)
+	z := sqrt.ModSqrt(sq, mod)
+	if z != sqrt {
+		t.Errorf("ModSqrt returned wrong value %s", z)
+	}
+
+	// test ModSqrt arguments outside the range [0,mod)
+	sqChk.Add(sq, mod)
+	z = sqrtChk.ModSqrt(&sqChk, mod)
+	if z != &sqrtChk || z.Cmp(sqrt) != 0 {
+		t.Errorf("ModSqrt returned inconsistent value %s", z)
+	}
+	sqChk.Sub(sq, mod)
+	z = sqrtChk.ModSqrt(&sqChk, mod)
+	if z != &sqrtChk || z.Cmp(sqrt) != 0 {
+		t.Errorf("ModSqrt returned inconsistent value %s", z)
+	}
+
+	// make sure we actually got a square root
+	if sqrt.Cmp(elt) == 0 {
+		return true // we found the "desired" square root
+	}
+	sqrtsq.Mul(sqrt, sqrt) // make sure we found the "other" one
+	sqrtsq.Mod(&sqrtsq, mod)
+	return sq.Cmp(&sqrtsq) == 0
+}
+
+func TestModSqrt(t *testing.T) {
+	var elt, mod, modx4, sq, sqrt Int
+	r := rand.New(rand.NewSource(9))
+	for i, s := range primes[1:] { // skip 2, use only odd primes
+		mod.SetString(s, 10)
+		modx4.Lsh(&mod, 2)
+
+		// test a few random elements per prime
+		for x := 1; x < 5; x++ {
+			elt.Rand(r, &modx4)
+			elt.Sub(&elt, &mod) // test range [-mod, 3*mod)
+			if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
+				t.Errorf("#%d: failed (sqrt(e) = %s)", i, &sqrt)
+			}
+		}
+	}
+
+	// exhaustive test for small values
+	for n := 3; n < 100; n++ {
+		mod.SetInt64(int64(n))
+		if !mod.ProbablyPrime(10) {
+			continue
+		}
+		isSquare := make([]bool, n)
+
+		// test all the squares
+		for x := 1; x < n; x++ {
+			elt.SetInt64(int64(x))
+			if !testModSqrt(t, &elt, &mod, &sq, &sqrt) {
+				t.Errorf("#%d: failed (sqrt(%d,%d) = %s)", x, &elt, &mod, &sqrt)
+			}
+			isSquare[sq.Uint64()] = true
+		}
+
+		// test all non-squares
+		for x := 1; x < n; x++ {
+			sq.SetInt64(int64(x))
+			z := sqrt.ModSqrt(&sq, &mod)
+			if !isSquare[x] && z != nil {
+				t.Errorf("#%d: failed (sqrt(%d,%d) = nil)", x, &sqrt, &mod)
+			}
+		}
+	}
+}
+
+func TestJacobi(t *testing.T) {
+	testCases := []struct {
+		x, y   int64
+		result int
+	}{
+		{0, 1, 1},
+		{0, -1, 1},
+		{1, 1, 1},
+		{1, -1, 1},
+		{0, 5, 0},
+		{1, 5, 1},
+		{2, 5, -1},
+		{-2, 5, -1},
+		{2, -5, -1},
+		{-2, -5, 1},
+		{3, 5, -1},
+		{5, 5, 0},
+		{-5, 5, 0},
+		{6, 5, 1},
+		{6, -5, 1},
+		{-6, 5, 1},
+		{-6, -5, -1},
+	}
+
+	var x, y Int
+
+	for i, test := range testCases {
+		x.SetInt64(test.x)
+		y.SetInt64(test.y)
+		expected := test.result
+		actual := Jacobi(&x, &y)
+		if actual != expected {
+			t.Errorf("#%d: Jacobi(%d, %d) = %d, but expected %d", i, test.x, test.y, actual, expected)
+		}
+	}
+}
+
+func TestJacobiPanic(t *testing.T) {
+	const failureMsg = "test failure"
+	defer func() {
+		msg := recover()
+		if msg == nil || msg == failureMsg {
+			panic(msg)
+		}
+		t.Log(msg)
+	}()
+	x := NewInt(1)
+	y := NewInt(2)
+	// Jacobi should panic when the second argument is even.
+	Jacobi(x, y)
+	panic(failureMsg)
+}
+
 var encodingTests = []string{
 	"-539345864568634858364538753846587364875430589374589",
 	"-678645873",
diff --git a/src/cmd/compile/internal/big/nat.go b/src/cmd/compile/internal/big/nat.go
index 2a279d186c..c3eef76fa1 100644
--- a/src/cmd/compile/internal/big/nat.go
+++ b/src/cmd/compile/internal/big/nat.go
@@ -216,6 +216,34 @@ func basicMul(z, x, y nat) {
 	}
 }
 
+// montgomery computes x*y*2^(-n*_W) mod m,
+// assuming k = -1/m mod 2^_W.
+// z is used for storing the result which is returned;
+// z must not alias x, y or m.
+func (z nat) montgomery(x, y, m nat, k Word, n int) nat {
+	var c1, c2 Word
+	z = z.make(n)
+	z.clear()
+	for i := 0; i < n; i++ {
+		d := y[i]
+		c1 += addMulVVW(z, x, d)
+		t := z[0] * k
+		c2 = addMulVVW(z, m, t)
+
+		copy(z, z[1:])
+		z[n-1] = c1 + c2
+		if z[n-1] < c1 {
+			c1 = 1
+		} else {
+			c1 = 0
+		}
+	}
+	if c1 != 0 {
+		subVV(z, z, m)
+	}
+	return z
+}
+
 // Fast version of z[0:n+n>>1].add(z[0:n+n>>1], x[0:n]) w/o bounds checks.
 // Factored out for readability - do not use outside karatsuba.
 func karatsubaAdd(z, x nat, n int) {
@@ -888,6 +916,13 @@ func (z nat) expNN(x, y, m nat) nat {
 	}
 	// y > 0
 
+	// x**1 mod m == x mod m
+	if len(y) == 1 && y[0] == 1 && len(m) != 0 {
+		_, z = z.div(z, x, m)
+		return z
+	}
+	// y > 1
+
 	if len(m) != 0 {
 		// We likely end up being as long as the modulus.
 		z = z.make(len(m))
@@ -898,8 +933,11 @@ func (z nat) expNN(x, y, m nat) nat {
 	// 4-bit, windowed exponentiation. This involves precomputing 14 values
 	// (x^2...x^15) but then reduces the number of multiply-reduces by a
 	// third. Even for a 32-bit exponent, this reduces the number of
-	// operations.
+	// operations. Uses Montgomery method for odd moduli.
 	if len(x) > 1 && len(y) > 1 && len(m) > 0 {
+		if m[0]&1 == 1 {
+			return z.expNNMontgomery(x, y, m)
+		}
 		return z.expNNWindowed(x, y, m)
 	}
 
@@ -1022,6 +1060,87 @@ func (z nat) expNNWindowed(x, y, m nat) nat {
 	return z.norm()
 }
 
+// expNNMontgomery calculates x**y mod m using a fixed, 4-bit window.
+// Uses Montgomery representation.
+func (z nat) expNNMontgomery(x, y, m nat) nat {
+	var zz, one, rr, RR nat
+
+	numWords := len(m)
+
+	// We want the lengths of x and m to be equal.
+	if len(x) > numWords {
+		_, rr = rr.div(rr, x, m)
+	} else if len(x) < numWords {
+		rr = rr.make(numWords)
+		rr.clear()
+		for i := range x {
+			rr[i] = x[i]
+		}
+	} else {
+		rr = x
+	}
+	x = rr
+
+	// Ideally the precomputations would be performed outside, and reused
+	// k0 = -mˆ-1 mod 2ˆ_W. Algorithm from: Dumas, J.G. "On Newton–Raphson
+	// Iteration for Multiplicative Inverses Modulo Prime Powers".
+	k0 := 2 - m[0]
+	t := m[0] - 1
+	for i := 1; i < _W; i <<= 1 {
+		t *= t
+		k0 *= (t + 1)
+	}
+	k0 = -k0
+
+	// RR = 2ˆ(2*_W*len(m)) mod m
+	RR = RR.setWord(1)
+	zz = zz.shl(RR, uint(2*numWords*_W))
+	_, RR = RR.div(RR, zz, m)
+	if len(RR) < numWords {
+		zz = zz.make(numWords)
+		copy(zz, RR)
+		RR = zz
+	}
+	// one = 1, with equal length to that of m
+	one = one.make(numWords)
+	one.clear()
+	one[0] = 1
+
+	const n = 4
+	// powers[i] contains x^i
+	var powers [1 << n]nat
+	powers[0] = powers[0].montgomery(one, RR, m, k0, numWords)
+	powers[1] = powers[1].montgomery(x, RR, m, k0, numWords)
+	for i := 2; i < 1<= 0; i-- {
+		yi := y[i]
+		for j := 0; j < _W; j += n {
+			if i != len(y)-1 || j != 0 {
+				zz = zz.montgomery(z, z, m, k0, numWords)
+				z = z.montgomery(zz, zz, m, k0, numWords)
+				zz = zz.montgomery(z, z, m, k0, numWords)
+				z = z.montgomery(zz, zz, m, k0, numWords)
+			}
+			zz = zz.montgomery(z, powers[yi>>(_W-n)], m, k0, numWords)
+			z, zz = zz, z
+			yi <<= n
+		}
+	}
+	// convert to regular number
+	zz = zz.montgomery(z, one, m, k0, numWords)
+	return zz.norm()
+}
+
 // probablyPrime performs reps Miller-Rabin tests to check whether n is prime.
 // If it returns true, n is prime with probability 1 - 1/4^reps.
 // If it returns false, n is not prime.
diff --git a/src/cmd/compile/internal/big/nat_test.go b/src/cmd/compile/internal/big/nat_test.go
index b25a89f731..a15a2bcac0 100644
--- a/src/cmd/compile/internal/big/nat_test.go
+++ b/src/cmd/compile/internal/big/nat_test.go
@@ -332,6 +332,67 @@ func TestTrailingZeroBits(t *testing.T) {
 	}
 }
 
+var montgomeryTests = []struct {
+	x, y, m      string
+	k0           uint64
+	out32, out64 string
+}{
+	{
+		"0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+		"0xffffffffffffffffffffffffffffffffffffffffffffffffe",
+		"0xfffffffffffffffffffffffffffffffffffffffffffffffff",
+		0x0000000000000000,
+		"0xffffffffffffffffffffffffffffffffffffffffff",
+		"0xffffffffffffffffffffffffffffffffff",
+	},
+	{
+		"0x0000000080000000",
+		"0x00000000ffffffff",
+		"0x0000000010000001",
+		0xff0000000fffffff,
+		"0x0000000088000000",
+		"0x0000000007800001",
+	},
+	{
+		"0xffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+		"0xffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+		"0x33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+		0xdecc8f1249812adf,
+		"0x22bb05b6d95eaaeca2bb7c05e51f807bce9064b5fbad177161695e4558f9474e91cd79",
+		"0x14beb58d230f85b6d95eaaeca2bb7c05e51f807bce9064b5fb45669afa695f228e48cd",
+	},
+	{
+		"0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000022222223333333333444444444",
+		"0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc",
+		"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1",
+		0xdecc8f1249812adf,
+		"0x5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d7a11c7772cba02c22f9711078d51a3797eb18e691295293284d988e349fa6deba46b25a4ecd9f715",
+		"0x92fcad4b5c0d52f451aec609b15da8e5e5626c4eaa88723bdeac9d25ca9b961269400410ca208a16af9c2fb07d799c32fe2f3cc5422f9711078d51a3797eb18e691295293284d8f5e69caf6decddfe1df6",
+	},
+}
+
+func TestMontgomery(t *testing.T) {
+	for i, test := range montgomeryTests {
+		x := natFromString(test.x)
+		y := natFromString(test.y)
+		m := natFromString(test.m)
+
+		var out nat
+		if _W == 32 {
+			out = natFromString(test.out32)
+		} else {
+			out = natFromString(test.out64)
+		}
+
+		k0 := Word(test.k0 & _M) // mask k0 to ensure that it fits for 32-bit systems.
+		z := nat(nil).montgomery(x, y, m, k0, len(m))
+		z = z.norm()
+		if z.cmp(out) != 0 {
+			t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString())
+		}
+	}
+}
+
 var expNNTests = []struct {
 	x, y, m string
 	out     string

From 1c0498fc05156d99a53d4a5ed728a3dc329c34a0 Mon Sep 17 00:00:00 2001
From: Shenghou Ma 
Date: Fri, 22 May 2015 17:09:36 -0400
Subject: [PATCH 190/232] go/build: make ArchChar always return "?" to match
 docs

Change-Id: I56f825f81aead9ded7af07a02188a52d3650ccf8
Reviewed-on: https://go-review.googlesource.com/10333
Reviewed-by: Brad Fitzpatrick 
---
 src/go/build/build.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/go/build/build.go b/src/go/build/build.go
index 1fd06b5d92..db6bdcf923 100644
--- a/src/go/build/build.go
+++ b/src/go/build/build.go
@@ -1397,5 +1397,5 @@ func IsLocalImport(path string) bool {
 // and the default linker output name. As of Go 1.5, those strings
 // no longer vary by architecture; they are compile, link, .o, and a.out, respectively.
 func ArchChar(goarch string) (string, error) {
-	return "", errors.New("architecture letter no longer used")
+	return "?", errors.New("architecture letter no longer used")
 }

From bd7c1f1fb3176a8f926ca03f9342d83353b05f83 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Thu, 21 May 2015 18:23:51 -0700
Subject: [PATCH 191/232] cmd/compile/internal/gc: correctly use Float32 in
 mpgetfltN

This resolves the compiler part of issue #10321.

Change-Id: I44b9909f992b37dd34b1c5292decd12de3d3a65e
Reviewed-on: https://go-review.googlesource.com/10355
Reviewed-by: Alan Donovan 
---
 src/cmd/compile/internal/gc/mparith3.go | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/src/cmd/compile/internal/gc/mparith3.go b/src/cmd/compile/internal/gc/mparith3.go
index 181e91c87d..58c129fcca 100644
--- a/src/cmd/compile/internal/gc/mparith3.go
+++ b/src/cmd/compile/internal/gc/mparith3.go
@@ -111,15 +111,8 @@ func mpgetfltN(a *Mpflt, prec int, bias int) float64 {
 	case 53:
 		x, _ = a.Val.Float64()
 	case 24:
-		// We should be using a.Val.Float32() here but that seems incorrect
-		// for certain denormal values (all.bash fails). The current code
-		// appears to work for all existing test cases, though there ought
-		// to be issues with denormal numbers that are incorrectly rounded.
-		// TODO(gri) replace with a.Val.Float32() once correctly working
-		// See also: https://github.com/golang/go/issues/10321
-		var t Mpflt
-		t.Val.SetPrec(24).Set(&a.Val)
-		x, _ = t.Val.Float64()
+		x32, _ := a.Val.Float32()
+		x = float64(x32)
 	default:
 		panic("unreachable")
 	}

From 75250a9f791e650bcf0fe16e9d3436314a94dde6 Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Fri, 22 May 2015 11:31:47 -0700
Subject: [PATCH 192/232] cmd/compile/internal/gc: simplify mpgetflt (remove
 switch and indirection)

Change-Id: I6ae3534defdae9367e1b856dbb8e846c3263a758
Reviewed-on: https://go-review.googlesource.com/10358
Reviewed-by: Alan Donovan 
---
 src/cmd/compile/internal/gc/mparith3.go | 27 ++++++++++---------------
 1 file changed, 11 insertions(+), 16 deletions(-)

diff --git a/src/cmd/compile/internal/gc/mparith3.go b/src/cmd/compile/internal/gc/mparith3.go
index 58c129fcca..0e0b626475 100644
--- a/src/cmd/compile/internal/gc/mparith3.go
+++ b/src/cmd/compile/internal/gc/mparith3.go
@@ -105,17 +105,8 @@ func mpcmpfltc(b *Mpflt, c float64) int {
 	return mpcmpfltflt(b, &a)
 }
 
-func mpgetfltN(a *Mpflt, prec int, bias int) float64 {
-	var x float64
-	switch prec {
-	case 53:
-		x, _ = a.Val.Float64()
-	case 24:
-		x32, _ := a.Val.Float32()
-		x = float64(x32)
-	default:
-		panic("unreachable")
-	}
+func mpgetflt(a *Mpflt) float64 {
+	x, _ := a.Val.Float64()
 
 	// check for overflow
 	if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
@@ -125,12 +116,16 @@ func mpgetfltN(a *Mpflt, prec int, bias int) float64 {
 	return x
 }
 
-func mpgetflt(a *Mpflt) float64 {
-	return mpgetfltN(a, 53, -1023)
-}
-
 func mpgetflt32(a *Mpflt) float64 {
-	return mpgetfltN(a, 24, -127)
+	x32, _ := a.Val.Float32()
+	x := float64(x32)
+
+	// check for overflow
+	if math.IsInf(x, 0) && nsavederrors+nerrors == 0 {
+		Yyerror("mpgetflt32 ovf")
+	}
+
+	return x
 }
 
 func Mpmovecflt(a *Mpflt, c float64) {

From 1893d3b320887daeb0a088dc3067c45dd87d9d5b Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Fri, 22 May 2015 15:21:56 -0700
Subject: [PATCH 193/232] cmd/compile/internal/big: update and apply
 vendor.bash

Package-external tests must use the vendored math/big package, not
the original one, otherwise tests may fail if there are discrepancies
in the implementation.

Change-Id: Ic5f0489aa6420ffea1f488633453f871ce1f0f66
Reviewed-on: https://go-review.googlesource.com/10380
Reviewed-by: Ian Lance Taylor 
---
 src/cmd/compile/internal/big/example_test.go      | 2 +-
 src/cmd/compile/internal/big/floatexample_test.go | 2 +-
 src/cmd/compile/internal/big/vendor.bash          | 8 +++++++-
 3 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/src/cmd/compile/internal/big/example_test.go b/src/cmd/compile/internal/big/example_test.go
index 078be47f95..cb91bc23bd 100644
--- a/src/cmd/compile/internal/big/example_test.go
+++ b/src/cmd/compile/internal/big/example_test.go
@@ -5,9 +5,9 @@
 package big_test
 
 import (
+	"cmd/compile/internal/big"
 	"fmt"
 	"log"
-	"math/big"
 )
 
 func ExampleRat_SetString() {
diff --git a/src/cmd/compile/internal/big/floatexample_test.go b/src/cmd/compile/internal/big/floatexample_test.go
index 7db10238bc..0ac9617c06 100644
--- a/src/cmd/compile/internal/big/floatexample_test.go
+++ b/src/cmd/compile/internal/big/floatexample_test.go
@@ -5,9 +5,9 @@
 package big_test
 
 import (
+	"cmd/compile/internal/big"
 	"fmt"
 	"math"
-	"math/big"
 )
 
 func ExampleFloat_Add() {
diff --git a/src/cmd/compile/internal/big/vendor.bash b/src/cmd/compile/internal/big/vendor.bash
index 84aa750462..1b191ccb8f 100755
--- a/src/cmd/compile/internal/big/vendor.bash
+++ b/src/cmd/compile/internal/big/vendor.bash
@@ -15,9 +15,15 @@ rm *.go
 cp $BIGDIR/*.go .
 
 # Use pure Go arith ops w/o build tag.
-sed 's/^\/\/ \+build math_big_pure_go$//' arith_decl_pure.go > arith_decl.go
+sed 's|^// \+build math_big_pure_go$||' arith_decl_pure.go > arith_decl.go
 rm arith_decl_pure.go
 
+# Import vendored math/big in external tests (e.g., floatexample_test.go).
+for f in *_test.go; do
+	sed 's|"math/big"|"cmd/compile/internal/big"|' $f > foo.go
+	mv foo.go $f
+done
+
 # gofmt to clean up after sed
 gofmt -w .
 

From 22e4b8167f14bdd33738cfdc21c3396b2341f8fd Mon Sep 17 00:00:00 2001
From: Elias Naur 
Date: Sat, 23 May 2015 00:46:10 +0200
Subject: [PATCH 194/232] misc/cgo/test: fix build for CC=clang

Fix build error when CL=clang introduced by CL 10173.

Change-Id: I8edf210787a9803280c0779ff710c7e634a820d6
Reviewed-on: https://go-review.googlesource.com/10341
Reviewed-by: Ian Lance Taylor 
---
 misc/cgo/test/sigprocmask_linux.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/misc/cgo/test/sigprocmask_linux.c b/misc/cgo/test/sigprocmask_linux.c
index 6597e985ac..518c533fa4 100644
--- a/misc/cgo/test/sigprocmask_linux.c
+++ b/misc/cgo/test/sigprocmask_linux.c
@@ -22,6 +22,7 @@ static void* sigthreadfunc(void* unused) {
 	sigaddset(&mask, SIGIO);
 	sigprocmask(SIG_BLOCK, &mask, NULL);
 	IntoGoAndBack();
+	return NULL;
 }
 
 int RunSigThread() {

From 2df1ccdbc6aac9e570e985437d741d723cb3497c Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Fri, 22 May 2015 13:58:03 -0700
Subject: [PATCH 195/232] math/big: Always print exponent sign when using 'p'
 exponent for Floats.

Float.Format supports the 'b' and 'p' format, both of which print
a binary ('p') exponent. The 'b' format always printed a sign ('+'
or '-') for the exponent; the 'p' format only printed a negative
sign for the exponent. This change makes the two consistent. It
also makes the 'p' format easier to read if the exponent is >= 0.

Also:
- Comments added elsewhere.

Change-Id: Ifd2e01bdafb3043345972ca22a90248d055bd29b
Reviewed-on: https://go-review.googlesource.com/10359
Reviewed-by: Alan Donovan 
---
 src/math/big/bits_test.go         | 20 ++++++++++----------
 src/math/big/float.go             |  2 ++
 src/math/big/float_test.go        | 30 +++++++++++++++---------------
 src/math/big/floatconv.go         |  6 ++++++
 src/math/big/floatconv_test.go    | 24 ++++++++++++------------
 src/math/big/floatexample_test.go |  6 +++---
 6 files changed, 48 insertions(+), 40 deletions(-)

diff --git a/src/math/big/bits_test.go b/src/math/big/bits_test.go
index 3ce24222d7..14ecab5909 100644
--- a/src/math/big/bits_test.go
+++ b/src/math/big/bits_test.go
@@ -203,18 +203,18 @@ func TestFromBits(t *testing.T) {
 	}{
 		// all different bit numbers
 		{nil, "0"},
-		{Bits{0}, "0x.8p1"},
-		{Bits{1}, "0x.8p2"},
-		{Bits{-1}, "0x.8p0"},
-		{Bits{63}, "0x.8p64"},
-		{Bits{33, -30}, "0x.8000000000000001p34"},
-		{Bits{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p256"},
+		{Bits{0}, "0x.8p+1"},
+		{Bits{1}, "0x.8p+2"},
+		{Bits{-1}, "0x.8p+0"},
+		{Bits{63}, "0x.8p+64"},
+		{Bits{33, -30}, "0x.8000000000000001p+34"},
+		{Bits{255, 0}, "0x.8000000000000000000000000000000000000000000000000000000000000001p+256"},
 
 		// multiple equal bit numbers
-		{Bits{0, 0}, "0x.8p2"},
-		{Bits{0, 0, 0, 0}, "0x.8p3"},
-		{Bits{0, 1, 0}, "0x.8p3"},
-		{append(Bits{2, 1, 0} /* 7 */, Bits{3, 1} /* 10 */ ...), "0x.88p5" /* 17 */},
+		{Bits{0, 0}, "0x.8p+2"},
+		{Bits{0, 0, 0, 0}, "0x.8p+3"},
+		{Bits{0, 1, 0}, "0x.8p+3"},
+		{append(Bits{2, 1, 0} /* 7 */, Bits{3, 1} /* 10 */ ...), "0x.88p+5" /* 17 */},
 	} {
 		f := test.bits.Float()
 		if got := f.Format('p', 0); got != test.want {
diff --git a/src/math/big/float.go b/src/math/big/float.go
index dcb72c5754..e663c1c6ac 100644
--- a/src/math/big/float.go
+++ b/src/math/big/float.go
@@ -913,6 +913,7 @@ func (x *Float) Float32() (float32, Accuracy) {
 			}
 			return float32(math.Inf(+1)), Above
 		}
+		// e <= emax
 
 		// Determine sign, biased exponent, and mantissa.
 		var sign, bexp, mant uint32
@@ -1019,6 +1020,7 @@ func (x *Float) Float64() (float64, Accuracy) {
 			}
 			return math.Inf(+1), Above
 		}
+		// e <= emax
 
 		// Determine sign, biased exponent, and mantissa.
 		var sign, bexp, mant uint64
diff --git a/src/math/big/float_test.go b/src/math/big/float_test.go
index 8bd3a9c8c9..7df9fc74bc 100644
--- a/src/math/big/float_test.go
+++ b/src/math/big/float_test.go
@@ -1563,32 +1563,32 @@ func TestFloatArithmeticOverflow(t *testing.T) {
 		x, y, want string
 		acc        Accuracy
 	}{
-		{4, ToNearestEven, '+', "0", "0", "0", Exact},                // smoke test
-		{4, ToNearestEven, '+', "0x.8p0", "0x.8p0", "0x.8p1", Exact}, // smoke test
+		{4, ToNearestEven, '+', "0", "0", "0", Exact},                   // smoke test
+		{4, ToNearestEven, '+', "0x.8p+0", "0x.8p+0", "0x.8p+1", Exact}, // smoke test
 
-		{4, ToNearestEven, '+', "0", "0x.8p2147483647", "0x.8p2147483647", Exact},
-		{4, ToNearestEven, '+', "0x.8p2147483500", "0x.8p2147483647", "0x.8p2147483647", Below}, // rounded to zero
-		{4, ToNearestEven, '+', "0x.8p2147483647", "0x.8p2147483647", "+Inf", Above},            // exponent overflow in +
-		{4, ToNearestEven, '+', "-0x.8p2147483647", "-0x.8p2147483647", "-Inf", Below},          // exponent overflow in +
-		{4, ToNearestEven, '-', "-0x.8p2147483647", "0x.8p2147483647", "-Inf", Below},           // exponent overflow in -
+		{4, ToNearestEven, '+', "0", "0x.8p2147483647", "0x.8p+2147483647", Exact},
+		{4, ToNearestEven, '+', "0x.8p2147483500", "0x.8p2147483647", "0x.8p+2147483647", Below}, // rounded to zero
+		{4, ToNearestEven, '+', "0x.8p2147483647", "0x.8p2147483647", "+Inf", Above},             // exponent overflow in +
+		{4, ToNearestEven, '+', "-0x.8p2147483647", "-0x.8p2147483647", "-Inf", Below},           // exponent overflow in +
+		{4, ToNearestEven, '-', "-0x.8p2147483647", "0x.8p2147483647", "-Inf", Below},            // exponent overflow in -
 
-		{4, ToZero, '+', "0x.fp2147483647", "0x.8p2147483643", "0x.fp2147483647", Below}, // rounded to zero
-		{4, ToNearestEven, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above},     // exponent overflow in rounding
-		{4, AwayFromZero, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above},      // exponent overflow in rounding
+		{4, ToZero, '+', "0x.fp2147483647", "0x.8p2147483643", "0x.fp+2147483647", Below}, // rounded to zero
+		{4, ToNearestEven, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above},      // exponent overflow in rounding
+		{4, AwayFromZero, '+', "0x.fp2147483647", "0x.8p2147483643", "+Inf", Above},       // exponent overflow in rounding
 
-		{4, AwayFromZero, '-', "-0x.fp2147483647", "0x.8p2147483644", "-Inf", Below},       // exponent overflow in rounding
-		{4, ToNearestEven, '-', "-0x.fp2147483647", "0x.8p2147483643", "-Inf", Below},      // exponent overflow in rounding
-		{4, ToZero, '-', "-0x.fp2147483647", "0x.8p2147483643", "-0x.fp2147483647", Above}, // rounded to zero
+		{4, AwayFromZero, '-', "-0x.fp2147483647", "0x.8p2147483644", "-Inf", Below},        // exponent overflow in rounding
+		{4, ToNearestEven, '-', "-0x.fp2147483647", "0x.8p2147483643", "-Inf", Below},       // exponent overflow in rounding
+		{4, ToZero, '-', "-0x.fp2147483647", "0x.8p2147483643", "-0x.fp+2147483647", Above}, // rounded to zero
 
 		{4, ToNearestEven, '+', "0", "0x.8p-2147483648", "0x.8p-2147483648", Exact},
 		{4, ToNearestEven, '+', "0x.8p-2147483648", "0x.8p-2147483648", "0x.8p-2147483647", Exact},
 
-		{4, ToNearestEven, '*', "1", "0x.8p2147483647", "0x.8p2147483647", Exact},
+		{4, ToNearestEven, '*', "1", "0x.8p2147483647", "0x.8p+2147483647", Exact},
 		{4, ToNearestEven, '*', "2", "0x.8p2147483647", "+Inf", Above},  // exponent overflow in *
 		{4, ToNearestEven, '*', "-2", "0x.8p2147483647", "-Inf", Below}, // exponent overflow in *
 
 		{4, ToNearestEven, '/', "0.5", "0x.8p2147483647", "0x.8p-2147483646", Exact},
-		{4, ToNearestEven, '/', "0x.8p0", "0x.8p2147483647", "0x.8p-2147483646", Exact},
+		{4, ToNearestEven, '/', "0x.8p+0", "0x.8p2147483647", "0x.8p-2147483646", Exact},
 		{4, ToNearestEven, '/', "0x.8p-1", "0x.8p2147483647", "0x.8p-2147483647", Exact},
 		{4, ToNearestEven, '/', "0x.8p-2", "0x.8p2147483647", "0x.8p-2147483648", Exact},
 		{4, ToNearestEven, '/', "0x.8p-3", "0x.8p2147483647", "0", Below}, // exponent underflow in /
diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go
index b929d1202c..5ab75e9031 100644
--- a/src/math/big/floatconv.go
+++ b/src/math/big/floatconv.go
@@ -67,6 +67,7 @@ func (z *Float) SetString(s string) (*Float, bool) {
 // defined if an error is reported.
 //
 // BUG(gri) The Float.Scan signature conflicts with Scan(s fmt.ScanState, ch rune) error.
+//          (https://github.com/golang/go/issues/10938)
 func (z *Float) Scan(r io.ByteScanner, base int) (f *Float, b int, err error) {
 	prec := z.prec
 	if prec == 0 {
@@ -268,6 +269,8 @@ func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b i
 // The prec value is ignored for the 'b' or 'p' format.
 //
 // BUG(gri) Float.Format does not accept negative precisions.
+// BUG(gri) The Float.Format signature conflicts with Format(f fmt.State, c rune).
+//          (https://github.com/golang/go/issues/10938)
 func (x *Float) Format(format byte, prec int) string {
 	const extra = 10 // TODO(gri) determine a good/better value here
 	return string(x.Append(make([]byte, 0, prec+extra), format, prec))
@@ -369,5 +372,8 @@ func (x *Float) pstring(buf []byte) []byte {
 	buf = append(buf, "0x."...)
 	buf = append(buf, strings.TrimRight(x.mant.hexString(), "0")...)
 	buf = append(buf, 'p')
+	if x.exp >= 0 {
+		buf = append(buf, '+')
+	}
 	return strconv.AppendInt(buf, int64(x.exp), 10)
 }
diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go
index 96c01eed81..6ba15754e5 100644
--- a/src/math/big/floatconv_test.go
+++ b/src/math/big/floatconv_test.go
@@ -145,8 +145,8 @@ func TestFloat64Format(t *testing.T) {
 
 		{0, 'p', 0, "0"},
 		{math.Copysign(0, -1), 'p', 0, "-0"},
-		{1024.0, 'p', 0, "0x.8p11"},
-		{-1024.0, 'p', 0, "-0x.8p11"},
+		{1024.0, 'p', 0, "0x.8p+11"},
+		{-1024.0, 'p', 0, "-0x.8p+11"},
 
 		// all test cases below from strconv/ftoa_test.go
 		{1, 'e', 5, "1.00000e+00"},
@@ -331,8 +331,8 @@ func TestFloatFormat(t *testing.T) {
 		{"3e40", 100, 'g', 40, "3e+40"},
 
 		// make sure "stupid" exponents don't stall the machine
-		{"1e1000000", 64, 'p', 0, "0x.88b3a28a05eade3ap3321929"},
-		{"1e1000000000", 64, 'p', 0, "0x.ecc5f45aa573d3p1538481529"},
+		{"1e1000000", 64, 'p', 0, "0x.88b3a28a05eade3ap+3321929"},
+		{"1e1000000000", 64, 'p', 0, "0x.ecc5f45aa573d3p+1538481529"},
 		{"1e-1000000", 64, 'p', 0, "0x.efb4542cc8ca418ap-3321928"},
 		{"1e-1000000000", 64, 'p', 0, "0x.8a64dd983a4c7dabp-1538481528"},
 
@@ -352,17 +352,17 @@ func TestFloatFormat(t *testing.T) {
 		{"3.00", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
 		{"3.000", 350, 'b', 0, "1720123961992553633708115671476565205597423741876210842803191629540192157066363606052513914832594264915968p-348"},
 
-		{"3", 350, 'p', 0, "0x.cp2"},
-		{"03", 350, 'p', 0, "0x.cp2"},
-		{"3.", 350, 'p', 0, "0x.cp2"},
-		{"3.0", 350, 'p', 0, "0x.cp2"},
-		{"3.00", 350, 'p', 0, "0x.cp2"},
-		{"3.000", 350, 'p', 0, "0x.cp2"},
+		{"3", 350, 'p', 0, "0x.cp+2"},
+		{"03", 350, 'p', 0, "0x.cp+2"},
+		{"3.", 350, 'p', 0, "0x.cp+2"},
+		{"3.0", 350, 'p', 0, "0x.cp+2"},
+		{"3.00", 350, 'p', 0, "0x.cp+2"},
+		{"3.000", 350, 'p', 0, "0x.cp+2"},
 
 		{"0", 64, 'p', 0, "0"},
 		{"-0", 64, 'p', 0, "-0"},
-		{"1024.0", 64, 'p', 0, "0x.8p11"},
-		{"-1024.0", 64, 'p', 0, "-0x.8p11"},
+		{"1024.0", 64, 'p', 0, "0x.8p+11"},
+		{"-1024.0", 64, 'p', 0, "-0x.8p+11"},
 
 		// unsupported format
 		{"3.14", 64, 'x', 0, "%x"},
diff --git a/src/math/big/floatexample_test.go b/src/math/big/floatexample_test.go
index 7db10238bc..d9d39ed365 100644
--- a/src/math/big/floatexample_test.go
+++ b/src/math/big/floatexample_test.go
@@ -21,9 +21,9 @@ func ExampleFloat_Add() {
 	fmt.Printf("y = %s (%s, prec = %d, acc = %s)\n", &y, y.Format('p', 0), y.Prec(), y.Acc())
 	fmt.Printf("z = %s (%s, prec = %d, acc = %s)\n", &z, z.Format('p', 0), z.Prec(), z.Acc())
 	// Output:
-	// x = 1000 (0x.fap10, prec = 64, acc = Exact)
-	// y = 2.718281828 (0x.adf85458248cd8p2, prec = 53, acc = Exact)
-	// z = 1002.718282 (0x.faadf854p10, prec = 32, acc = Below)
+	// x = 1000 (0x.fap+10, prec = 64, acc = Exact)
+	// y = 2.718281828 (0x.adf85458248cd8p+2, prec = 53, acc = Exact)
+	// z = 1002.718282 (0x.faadf854p+10, prec = 32, acc = Below)
 }
 
 func Example_Shift() {

From cca39ff3b1a4da86e1115e0a1375f2f429d192c8 Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Fri, 15 May 2015 12:10:10 +0900
Subject: [PATCH 196/232] net: don't show verbose information when
 -test.v=false

Updates #10845.

Change-Id: I4cec670c7db88c50a6e5619e611744e161d73b3c
Reviewed-on: https://go-review.googlesource.com/10131
Reviewed-by: Ian Lance Taylor 
---
 src/net/main_test.go | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/net/main_test.go b/src/net/main_test.go
index ceec08911e..62b8997091 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -50,9 +50,9 @@ func TestMain(m *testing.M) {
 	st := m.Run()
 
 	testHookUninstaller.Do(uninstallTestHooks)
-	if !testing.Short() {
-		printLeakedGoroutines()
-		printLeakedSockets()
+	if testing.Verbose() {
+		printRunningGoroutines()
+		printInflightSockets()
 		printSocketStats()
 	}
 	forceCloseSockets()
@@ -98,8 +98,8 @@ func setupTestData() {
 	}
 }
 
-func printLeakedGoroutines() {
-	gss := leakedGoroutines()
+func printRunningGoroutines() {
+	gss := runningGoroutines()
 	if len(gss) == 0 {
 		return
 	}
@@ -110,9 +110,8 @@ func printLeakedGoroutines() {
 	fmt.Fprintf(os.Stderr, "\n")
 }
 
-// leakedGoroutines returns a list of remaining goroutines used in
-// test cases.
-func leakedGoroutines() []string {
+// runningGoroutines returns a list of remaining goroutines.
+func runningGoroutines() []string {
 	var gss []string
 	b := make([]byte, 2<<20)
 	b = b[:runtime.Stack(b, true)]
@@ -131,7 +130,7 @@ func leakedGoroutines() []string {
 	return gss
 }
 
-func printLeakedSockets() {
+func printInflightSockets() {
 	sos := sw.Sockets()
 	if len(sos) == 0 {
 		return

From eeb64b7fef7b593c64ea127ab4c1052162638099 Mon Sep 17 00:00:00 2001
From: Mikio Hara 
Date: Thu, 14 May 2015 10:18:10 +0900
Subject: [PATCH 197/232] net: adjust dual stack support on dragonfly

As mentioned in
http://gitweb.dragonflybsd.org/dragonfly.git/commit/727ccde8cce813911d885b7f6ed749dcea68a886,
DragonFly BSD is dropping support for IPv6 IPv4-mapped address.
Unfortunately, on some released versions we see the kernels pretend to
support the feature but actually not (unless tweaking some kernel states
via sysctl.)

To avoid unpredictable behavior, the net package assumes that all
DragonFly BSD kernels don't support IPv6 IPv4-mapped address.

Fixes #10764.

Change-Id: Ic7af3651e0372ec03774432fbb6b2eb0c455e994
Reviewed-on: https://go-review.googlesource.com/10071
Reviewed-by: Ian Lance Taylor 
---
 src/net/ipsock_posix.go  | 29 +++++++++++++++++--
 src/net/listen_test.go   |  6 ++--
 src/net/main_test.go     | 51 ++++++++++++++++++++++++++++++++-
 src/net/sockopt_bsd.go   |  2 +-
 src/net/tcp_test.go      | 36 +++---------------------
 src/net/tcpsock_posix.go |  5 ----
 src/net/udp_test.go      | 61 +++++++++++++---------------------------
 7 files changed, 102 insertions(+), 88 deletions(-)

diff --git a/src/net/ipsock_posix.go b/src/net/ipsock_posix.go
index 56b9872fd1..83eaf855b4 100644
--- a/src/net/ipsock_posix.go
+++ b/src/net/ipsock_posix.go
@@ -9,10 +9,18 @@
 package net
 
 import (
+	"runtime"
 	"syscall"
 	"time"
 )
 
+// BUG(rsc,mikio): On DragonFly BSD and OpenBSD, listening on the
+// "tcp" and "udp" networks does not listen for both IPv4 and IPv6
+// connections. This is due to the fact that IPv4 traffic will not be
+// routed to an IPv6 socket - two separate sockets are required if
+// both address families are to be supported.
+// See inet6(4) for details.
+
 func probeIPv4Stack() bool {
 	s, err := socketFunc(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
 	switch err {
@@ -41,13 +49,28 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
 	var probes = []struct {
 		laddr TCPAddr
 		value int
-		ok    bool
 	}{
 		// IPv6 communication capability
 		{laddr: TCPAddr{IP: ParseIP("::1")}, value: 1},
 		// IPv6 IPv4-mapped address communication capability
 		{laddr: TCPAddr{IP: IPv4(127, 0, 0, 1)}, value: 0},
 	}
+	var supps [2]bool
+	switch runtime.GOOS {
+	case "dragonfly", "openbsd":
+		// Some released versions of DragonFly BSD pretend to
+		// accept IPV6_V6ONLY=0 successfully, but the state
+		// still stays IPV6_V6ONLY=1. Eventually DragonFly BSD
+		// stops preteding, but the transition period would
+		// cause unpredictable behavior and we need to avoid
+		// it.
+		//
+		// OpenBSD also doesn't support IPV6_V6ONLY=0 but it
+		// never pretends to accept IPV6_V6OLY=0. It always
+		// returns an error and we don't need to probe the
+		// capability.
+		probes = probes[:1]
+	}
 
 	for i := range probes {
 		s, err := socketFunc(syscall.AF_INET6, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
@@ -63,10 +86,10 @@ func probeIPv6Stack() (supportsIPv6, supportsIPv4map bool) {
 		if err := syscall.Bind(s, sa); err != nil {
 			continue
 		}
-		probes[i].ok = true
+		supps[i] = true
 	}
 
-	return probes[0].ok, probes[1].ok
+	return supps[0], supps[1]
 }
 
 // favoriteAddrFamily returns the appropriate address family to
diff --git a/src/net/listen_test.go b/src/net/listen_test.go
index 8f43c846d9..89d4d7e0de 100644
--- a/src/net/listen_test.go
+++ b/src/net/listen_test.go
@@ -218,8 +218,6 @@ var dualStackTCPListenerTests = []struct {
 // listening address and same port.
 func TestDualStackTCPListener(t *testing.T) {
 	switch runtime.GOOS {
-	case "dragonfly":
-		t.Skip("not supported on DragonFly, see golang.org/issue/10729")
 	case "nacl", "plan9":
 		t.Skipf("not supported on %s", runtime.GOOS)
 	}
@@ -233,7 +231,7 @@ func TestDualStackTCPListener(t *testing.T) {
 			continue
 		}
 
-		if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+		if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
 			tt.xerr = nil
 		}
 		var firstErr, secondErr error
@@ -320,7 +318,7 @@ func TestDualStackUDPListener(t *testing.T) {
 			continue
 		}
 
-		if runtime.GOOS == "openbsd" && differentWildcardAddr(tt.address1, tt.address2) {
+		if !supportsIPv4map && differentWildcardAddr(tt.address1, tt.address2) {
 			tt.xerr = nil
 		}
 		var firstErr, secondErr error
diff --git a/src/net/main_test.go b/src/net/main_test.go
index 62b8997091..f3f8b1a900 100644
--- a/src/net/main_test.go
+++ b/src/net/main_test.go
@@ -59,6 +59,16 @@ func TestMain(m *testing.M) {
 	os.Exit(st)
 }
 
+type ipv6LinkLocalUnicastTest struct {
+	network, address string
+	nameLookup       bool
+}
+
+var (
+	ipv6LinkLocalUnicastTCPTests []ipv6LinkLocalUnicastTest
+	ipv6LinkLocalUnicastUDPTests []ipv6LinkLocalUnicastTest
+)
+
 func setupTestData() {
 	if supportsIPv4 {
 		resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
@@ -81,7 +91,8 @@ func setupTestData() {
 		resolveIPAddrTests = append(resolveIPAddrTests, resolveIPAddrTest{"ip6", "localhost", &IPAddr{IP: IPv6loopback}, nil})
 	}
 
-	if ifi := loopbackInterface(); ifi != nil {
+	ifi := loopbackInterface()
+	if ifi != nil {
 		index := fmt.Sprintf("%v", ifi.Index)
 		resolveTCPAddrTests = append(resolveTCPAddrTests, []resolveTCPAddrTest{
 			{"tcp6", "[fe80::1%" + ifi.Name + "]:1", &TCPAddr{IP: ParseIP("fe80::1"), Port: 1, Zone: zoneToString(ifi.Index)}, nil},
@@ -96,6 +107,44 @@ func setupTestData() {
 			{"ip6", "fe80::1%" + index, &IPAddr{IP: ParseIP("fe80::1"), Zone: index}, nil},
 		}...)
 	}
+
+	addr := ipv6LinkLocalUnicastAddr(ifi)
+	if addr != "" {
+		if runtime.GOOS != "dragonfly" {
+			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+				{"tcp", "[" + addr + "%" + ifi.Name + "]:0", false},
+			}...)
+			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+				{"udp", "[" + addr + "%" + ifi.Name + "]:0", false},
+			}...)
+		}
+		ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+			{"tcp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+		}...)
+		ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+			{"udp6", "[" + addr + "%" + ifi.Name + "]:0", false},
+		}...)
+		switch runtime.GOOS {
+		case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
+			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+				{"tcp", "[localhost%" + ifi.Name + "]:0", true},
+				{"tcp6", "[localhost%" + ifi.Name + "]:0", true},
+			}...)
+			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+				{"udp", "[localhost%" + ifi.Name + "]:0", true},
+				{"udp6", "[localhost%" + ifi.Name + "]:0", true},
+			}...)
+		case "linux":
+			ipv6LinkLocalUnicastTCPTests = append(ipv6LinkLocalUnicastTCPTests, []ipv6LinkLocalUnicastTest{
+				{"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+				{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+			}...)
+			ipv6LinkLocalUnicastUDPTests = append(ipv6LinkLocalUnicastUDPTests, []ipv6LinkLocalUnicastTest{
+				{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
+				{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
+			}...)
+		}
+	}
 }
 
 func printRunningGoroutines() {
diff --git a/src/net/sockopt_bsd.go b/src/net/sockopt_bsd.go
index 00e4dbf376..1b4a586a7e 100644
--- a/src/net/sockopt_bsd.go
+++ b/src/net/sockopt_bsd.go
@@ -25,7 +25,7 @@ func setDefaultSockopts(s, family, sotype int, ipv6only bool) error {
 			syscall.SetsockoptInt(s, syscall.IPPROTO_IPV6, syscall.IPV6_PORTRANGE, syscall.IPV6_PORTRANGE_HIGH)
 		}
 	}
-	if family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
+	if supportsIPv4map && family == syscall.AF_INET6 && sotype != syscall.SOCK_RAW {
 		// Allow both IP versions even if the OS default
 		// is otherwise.  Note that some operating systems
 		// never admit this option.
diff --git a/src/net/tcp_test.go b/src/net/tcp_test.go
index 64117449bd..2191c91fa3 100644
--- a/src/net/tcp_test.go
+++ b/src/net/tcp_test.go
@@ -367,39 +367,11 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
 		t.Skip("avoid external network")
 	}
 	if !supportsIPv6 {
-		t.Skip("ipv6 is not supported")
-	}
-	ifi := loopbackInterface()
-	if ifi == nil {
-		t.Skip("loopback interface not found")
-	}
-	laddr := ipv6LinkLocalUnicastAddr(ifi)
-	if laddr == "" {
-		t.Skip("ipv6 unicast address on loopback not found")
+		t.Skip("IPv6 is not supported")
 	}
 
-	type test struct {
-		net, addr  string
-		nameLookup bool
-	}
-	var tests = []test{
-		{"tcp", "[" + laddr + "%" + ifi.Name + "]:0", false},
-		{"tcp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
-	}
-	switch runtime.GOOS {
-	case "darwin", "freebsd", "openbsd", "netbsd":
-		tests = append(tests, []test{
-			{"tcp", "[localhost%" + ifi.Name + "]:0", true},
-			{"tcp6", "[localhost%" + ifi.Name + "]:0", true},
-		}...)
-	case "linux":
-		tests = append(tests, []test{
-			{"tcp", "[ip6-localhost%" + ifi.Name + "]:0", true},
-			{"tcp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
-		}...)
-	}
-	for i, tt := range tests {
-		ln, err := Listen(tt.net, tt.addr)
+	for i, tt := range ipv6LinkLocalUnicastTCPTests {
+		ln, err := Listen(tt.network, tt.address)
 		if err != nil {
 			// It might return "LookupHost returned no
 			// suitable address" error on some platforms.
@@ -420,7 +392,7 @@ func TestIPv6LinkLocalUnicastTCP(t *testing.T) {
 			t.Fatalf("got %v; expected a proper address with zone identifier", la)
 		}
 
-		c, err := Dial(tt.net, ls.Listener.Addr().String())
+		c, err := Dial(tt.network, ls.Listener.Addr().String())
 		if err != nil {
 			t.Fatal(err)
 		}
diff --git a/src/net/tcpsock_posix.go b/src/net/tcpsock_posix.go
index 1f43521a9e..51a8e97915 100644
--- a/src/net/tcpsock_posix.go
+++ b/src/net/tcpsock_posix.go
@@ -13,11 +13,6 @@ import (
 	"time"
 )
 
-// BUG(rsc): On OpenBSD, listening on the "tcp" network does not listen for
-// both IPv4 and IPv6 connections. This is due to the fact that IPv4 traffic
-// will not be routed to an IPv6 socket - two separate sockets are required
-// if both AFs are to be supported. See inet6(4) on OpenBSD for details.
-
 func sockaddrToTCP(sa syscall.Sockaddr) Addr {
 	switch sa := sa.(type) {
 	case *syscall.SockaddrInet4:
diff --git a/src/net/udp_test.go b/src/net/udp_test.go
index 2213468e79..b25f96a3fd 100644
--- a/src/net/udp_test.go
+++ b/src/net/udp_test.go
@@ -238,55 +238,32 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
 		t.Skip("avoid external network")
 	}
 	if !supportsIPv6 {
-		t.Skip("ipv6 is not supported")
-	}
-	ifi := loopbackInterface()
-	if ifi == nil {
-		t.Skip("loopback interface not found")
-	}
-	laddr := ipv6LinkLocalUnicastAddr(ifi)
-	if laddr == "" {
-		t.Skip("ipv6 unicast address on loopback not found")
+		t.Skip("IPv6 is not supported")
 	}
 
-	type test struct {
-		net, addr  string
-		nameLookup bool
-	}
-	var tests = []test{
-		{"udp", "[" + laddr + "%" + ifi.Name + "]:0", false},
-		{"udp6", "[" + laddr + "%" + ifi.Name + "]:0", false},
-	}
-	// The first udp test fails on DragonFly - see issue 7473.
-	if runtime.GOOS == "dragonfly" {
-		tests = tests[1:]
-	}
-	switch runtime.GOOS {
-	case "darwin", "dragonfly", "freebsd", "openbsd", "netbsd":
-		tests = append(tests, []test{
-			{"udp", "[localhost%" + ifi.Name + "]:0", true},
-			{"udp6", "[localhost%" + ifi.Name + "]:0", true},
-		}...)
-	case "linux":
-		tests = append(tests, []test{
-			{"udp", "[ip6-localhost%" + ifi.Name + "]:0", true},
-			{"udp6", "[ip6-localhost%" + ifi.Name + "]:0", true},
-		}...)
-	}
-	for _, tt := range tests {
-		c1, err := ListenPacket(tt.net, tt.addr)
+	for i, tt := range ipv6LinkLocalUnicastUDPTests {
+		c1, err := ListenPacket(tt.network, tt.address)
 		if err != nil {
 			// It might return "LookupHost returned no
 			// suitable address" error on some platforms.
 			t.Log(err)
 			continue
 		}
-		defer c1.Close()
+		ls, err := (&packetListener{PacketConn: c1}).newLocalServer()
+		if err != nil {
+			t.Fatal(err)
+		}
+		defer ls.teardown()
+		ch := make(chan error, 1)
+		handler := func(ls *localPacketServer, c PacketConn) { packetTransponder(c, ch) }
+		if err := ls.buildup(handler); err != nil {
+			t.Fatal(err)
+		}
 		if la, ok := c1.LocalAddr().(*UDPAddr); !ok || !tt.nameLookup && la.Zone == "" {
 			t.Fatalf("got %v; expected a proper address with zone identifier", la)
 		}
 
-		c2, err := Dial(tt.net, c1.LocalAddr().String())
+		c2, err := Dial(tt.network, ls.PacketConn.LocalAddr().String())
 		if err != nil {
 			t.Fatal(err)
 		}
@@ -302,12 +279,12 @@ func TestIPv6LinkLocalUnicastUDP(t *testing.T) {
 			t.Fatal(err)
 		}
 		b := make([]byte, 32)
-		if _, from, err := c1.ReadFrom(b); err != nil {
+		if _, err := c2.Read(b); err != nil {
 			t.Fatal(err)
-		} else {
-			if ra, ok := from.(*UDPAddr); !ok || !tt.nameLookup && ra.Zone == "" {
-				t.Fatalf("got %v; expected a proper address with zone identifier", ra)
-			}
+		}
+
+		for err := range ch {
+			t.Errorf("#%d: %v", i, err)
 		}
 	}
 }

From 8017ace496f5a21bcd55377e250e325f8ba11d45 Mon Sep 17 00:00:00 2001
From: Elias Naur 
Date: Sat, 23 May 2015 11:26:22 +0200
Subject: [PATCH 198/232] runtime: don't always block all signals on OpenBSD

Implement the changes from CL 10173 on OpenBSD.

Change-Id: I2db1cd8141fd392a34753a1b8113e2e0401173b9
Reviewed-on: https://go-review.googlesource.com/10342
Run-TryBot: Ian Lance Taylor 
TryBot-Result: Gobot Gobot 
Reviewed-by: Ian Lance Taylor 
---
 src/runtime/os1_openbsd.go | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/runtime/os1_openbsd.go b/src/runtime/os1_openbsd.go
index 98af545f7f..5ccf642468 100644
--- a/src/runtime/os1_openbsd.go
+++ b/src/runtime/os1_openbsd.go
@@ -149,6 +149,11 @@ func mpreinit(mp *m) {
 }
 
 func msigsave(mp *m) {
+	smask := (*uint32)(unsafe.Pointer(&mp.sigmask))
+	if unsafe.Sizeof(*smask) > unsafe.Sizeof(mp.sigmask) {
+		throw("insufficient storage for signal mask")
+	}
+	*smask = sigprocmask(_SIG_BLOCK, 0)
 }
 
 // Called to initialize a new m (including the bootstrap m).
@@ -161,11 +166,22 @@ func minit() {
 
 	// Initialize signal handling
 	signalstack((*byte)(unsafe.Pointer(_g_.m.gsignal.stack.lo)), 32*1024)
-	sigprocmask(_SIG_SETMASK, sigset_none)
+
+	// restore signal mask from m.sigmask and unblock essential signals
+	nmask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+	for i := range sigtable {
+		if sigtable[i].flags&_SigUnblock != 0 {
+			nmask &^= 1 << (uint32(i) - 1)
+		}
+	}
+	sigprocmask(_SIG_SETMASK, nmask)
 }
 
 // Called from dropm to undo the effect of an minit.
 func unminit() {
+	_g_ := getg()
+	smask := *(*uint32)(unsafe.Pointer(&_g_.m.sigmask))
+	sigprocmask(_SIG_SETMASK, smask)
 	signalstack(nil, 0)
 }
 

From 9c37a23bcb178351d70c2d90e8915b9e413d59b8 Mon Sep 17 00:00:00 2001
From: David Crawshaw 
Date: Mon, 25 May 2015 07:20:07 -0400
Subject: [PATCH 199/232] misc/android: cleaner to remove stale GOROOT files

Updates #10806

Change-Id: I734d6db026cc7c2e3099a76dc8db8e42b2b90aa7
Reviewed-on: https://go-review.googlesource.com/10390
Reviewed-by: Brad Fitzpatrick 
---
 misc/android/cleaner.go | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)
 create mode 100644 misc/android/cleaner.go

diff --git a/misc/android/cleaner.go b/misc/android/cleaner.go
new file mode 100644
index 0000000000..dafb162697
--- /dev/null
+++ b/misc/android/cleaner.go
@@ -0,0 +1,39 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Cleaner removes anything from /data/local/tmp/goroot not on a builtin list.
+// Used by androidtest.bash.
+package main
+
+import (
+	"log"
+	"os"
+	"path/filepath"
+	"strings"
+)
+
+func main() {
+	const goroot = "/data/local/tmp/goroot"
+	expect := make(map[string]bool)
+	for _, f := range strings.Split(files, "\n") {
+		expect[filepath.Join(goroot, f)] = true
+	}
+
+	err := filepath.Walk(goroot, func(path string, info os.FileInfo, err error) error {
+		if expect[path] {
+			return nil
+		}
+		log.Printf("removing %s", path)
+		if err := os.RemoveAll(path); err != nil {
+			return err
+		}
+		if info.IsDir() {
+			return filepath.SkipDir
+		}
+		return nil
+	})
+	if err != nil {
+		log.Fatal(err)
+	}
+}

From 0b36e1272de75038578bf9a870165228818e6b9a Mon Sep 17 00:00:00 2001
From: David Crawshaw 
Date: Mon, 25 May 2015 07:17:27 -0400
Subject: [PATCH 200/232] androidtest.bash: clean up stale GOROOT

Fixes #10806

Change-Id: I1be1f28ad60c913105d8417c42ec1b262f101f72
Reviewed-on: https://go-review.googlesource.com/10391
Reviewed-by: Brad Fitzpatrick 
---
 src/androidtest.bash | 25 +++++++++++++++++++------
 1 file changed, 19 insertions(+), 6 deletions(-)

diff --git a/src/androidtest.bash b/src/androidtest.bash
index aad1f7ec8d..39e73c350b 100755
--- a/src/androidtest.bash
+++ b/src/androidtest.bash
@@ -24,10 +24,11 @@ if [ "$GOOS" != "android" ]; then
 fi
 
 export CGO_ENABLED=1
+unset GOBIN
 
-# Run the build for the host bootstrap, so we can build go_android_exec.
+# Do the build first, so we can build go_android_exec and cleaner.
 # Also lets us fail early before the (slow) adb push if the build is broken.
-./make.bash
+. ./make.bash --no-banner
 export GOROOT=$(dirname $(pwd))
 export PATH=$GOROOT/bin:$PATH
 GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH go build \
@@ -50,9 +51,21 @@ cp -a "${GOROOT}/test" "${FAKE_GOROOT}/"
 cp -a "${GOROOT}/lib" "${FAKE_GOROOT}/"
 cp -a "${GOROOT}/pkg/android_$GOARCH" "${FAKE_GOROOT}/pkg/"
 echo '# Syncing test files to android device'
+adb shell mkdir -p /data/local/tmp/goroot
 time adb sync data &> /dev/null
-echo ''
-rm -rf "$ANDROID_PRODUCT_OUT"
 
-# Run standard build and tests.
-./all.bash --no-clean
+export CLEANER=/tmp/androidcleaner-$$
+cp ../misc/android/cleaner.go $CLEANER.go
+echo 'var files = `' >> $CLEANER.go
+(cd $ANDROID_PRODUCT_OUT/data/local/tmp/goroot; find . >> $CLEANER.go)
+echo '`' >> $CLEANER.go
+go build -o $CLEANER $CLEANER.go
+adb push $CLEANER /data/local/tmp/cleaner
+rm $CLEANER $CLEANER.go
+adb shell /data/local/tmp/cleaner
+
+rm -rf "$ANDROID_PRODUCT_OUT"
+echo ''
+
+# Run standard tests.
+bash run.bash --no-rebuild

From ac7ffdfa1006a40768d11ed7e2e20dab57799df0 Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor 
Date: Fri, 15 May 2015 17:12:20 -0700
Subject: [PATCH 201/232] cmd/go: permit C files if the package uses SWIG

They will be compiled and added to the archive, just as though the
package used cgo.  In effect all SWIG packages now use cgo anyhow.

Change-Id: I5d5a28ed0ec4295f24036b2834218bc980f080d0
Reviewed-on: https://go-review.googlesource.com/10146
Reviewed-by: Minux Ma 
---
 src/cmd/go/pkg.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 7a71471340..601c30362f 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -673,10 +673,10 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 	p.Target = p.target
 
 	// The gc toolchain only permits C source files with cgo.
-	if len(p.CFiles) > 0 && !p.usesCgo() && buildContext.Compiler == "gc" {
+	if len(p.CFiles) > 0 && !p.usesCgo() && !p.usesSwig() && buildContext.Compiler == "gc" {
 		p.Error = &PackageError{
 			ImportStack: stk.copy(),
-			Err:         fmt.Sprintf("C source files not allowed when not using cgo: %s", strings.Join(p.CFiles, " ")),
+			Err:         fmt.Sprintf("C source files not allowed when not using cgo or SWIG: %s", strings.Join(p.CFiles, " ")),
 		}
 		return p
 	}

From 714291f2d80bab1599a866f266a4fc6546e61632 Mon Sep 17 00:00:00 2001
From: Ian Lance Taylor 
Date: Sun, 24 May 2015 11:29:38 -0700
Subject: [PATCH 202/232] cmd/link/internal/ld: if -v, display external linker
 output

It can be useful when debugging to be able to see what the external
linker is doing even when it succeeds.  In particular this permits
passing -v to the external linker to see precisely what it is doing.

Change-Id: Ifed441912d97bbebea20303fdb899e140b380215
Reviewed-on: https://go-review.googlesource.com/10363
Reviewed-by: Minux Ma 
---
 src/cmd/link/internal/ld/lib.go | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go
index 6cf0b525e5..80c01538ac 100644
--- a/src/cmd/link/internal/ld/lib.go
+++ b/src/cmd/link/internal/ld/lib.go
@@ -1028,6 +1028,9 @@ func hostlink() {
 
 	if out, err := exec.Command(argv[0], argv[1:]...).CombinedOutput(); err != nil {
 		Exitf("running %s failed: %v\n%s", argv[0], err, out)
+	} else if Debug['v'] != 0 && len(out) > 0 {
+		fmt.Fprintf(&Bso, "%s", out)
+		Bso.Flush()
 	}
 
 	if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin {

From 972a478ddf689a375dffc1a9923742b896b63bd0 Mon Sep 17 00:00:00 2001
From: Russ Cox 
Date: Thu, 21 May 2015 15:00:06 -0400
Subject: [PATCH 203/232] cmd/compile: don't cater to race detector in write
 barriers

The new lower-level barriers work fine and don't need special handling,
because they appear to the race detector as (visible) ordinary assignments.

Change-Id: I7477d73a3deecbebf68716580678c595cc4151e3
Reviewed-on: https://go-review.googlesource.com/10316
Reviewed-by: Austin Clements 
Run-TryBot: Austin Clements 
TryBot-Result: Gobot Gobot 
---
 src/cmd/compile/internal/gc/racewalk.go | 25 ----------
 src/cmd/compile/internal/gc/walk.go     | 62 ++-----------------------
 2 files changed, 4 insertions(+), 83 deletions(-)

diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go
index 446ec038c8..6e384682fd 100644
--- a/src/cmd/compile/internal/gc/racewalk.go
+++ b/src/cmd/compile/internal/gc/racewalk.go
@@ -186,31 +186,6 @@ func racewalknode(np **Node, init **NodeList, wr int, skip int) {
 	// as we do not instrument runtime code.
 	// typedslicecopy is instrumented in runtime.
 	case OCALLFUNC:
-		if n.Left.Sym != nil && n.Left.Sym.Pkg == Runtimepkg && (strings.HasPrefix(n.Left.Sym.Name, "writebarrier") || n.Left.Sym.Name == "typedmemmove") {
-			// Find the dst argument.
-			// The list can be reordered, so it's not necessary just the first or the second element.
-			var l *NodeList
-			for l = n.List; l != nil; l = l.Next {
-				if n.Left.Sym.Name == "typedmemmove" {
-					if l.N.Left.Xoffset == int64(Widthptr) {
-						break
-					}
-				} else {
-					if l.N.Left.Xoffset == 0 {
-						break
-					}
-				}
-			}
-
-			if l == nil {
-				Fatal("racewalk: writebarrier no arg")
-			}
-			if l.N.Right.Op != OADDR {
-				Fatal("racewalk: writebarrier bad arg")
-			}
-			callinstr(&l.N.Right.Left, init, 1, 0)
-		}
-
 		racewalknode(&n.Left, init, 0, 0)
 		goto ret
 
diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go
index 11117666c7..04f0491c15 100644
--- a/src/cmd/compile/internal/gc/walk.go
+++ b/src/cmd/compile/internal/gc/walk.go
@@ -2226,65 +2226,11 @@ var applywritebarrier_bv Bvec
 
 func applywritebarrier(n *Node, init **NodeList) *Node {
 	if n.Left != nil && n.Right != nil && needwritebarrier(n.Left, n.Right) {
-		if flag_race == 0 {
-			if Debug_wb > 1 {
-				Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
-			}
-			n.Op = OASWB
-			return n
-		}
-		// Use slow path always for race detector.
-		if Curfn != nil && Curfn.Func.Nowritebarrier {
-			Yyerror("write barrier prohibited")
-		}
-		if Debug_wb > 0 {
-			Warnl(int(n.Lineno), "write barrier")
-		}
-		t := n.Left.Type
-		l := Nod(OADDR, n.Left, nil)
-		l.Etype = 1 // addr does not escape
-		if t.Width == int64(Widthptr) {
-			n = mkcall1(writebarrierfn("writebarrierptr", t, n.Right.Type), nil, init, l, n.Right)
-		} else if t.Etype == TSTRING {
-			n = mkcall1(writebarrierfn("writebarrierstring", t, n.Right.Type), nil, init, l, n.Right)
-		} else if Isslice(t) {
-			n = mkcall1(writebarrierfn("writebarrierslice", t, n.Right.Type), nil, init, l, n.Right)
-		} else if Isinter(t) {
-			n = mkcall1(writebarrierfn("writebarrieriface", t, n.Right.Type), nil, init, l, n.Right)
-		} else if t.Width <= int64(4*Widthptr) {
-			x := int64(0)
-			if applywritebarrier_bv.b == nil {
-				applywritebarrier_bv = bvalloc(4)
-			}
-			bvresetall(applywritebarrier_bv)
-			onebitwalktype1(t, &x, applywritebarrier_bv)
-			var name string
-			switch t.Width / int64(Widthptr) {
-			default:
-				Fatal("found writebarrierfat for %d-byte object of type %v", int(t.Width), t)
-
-			case 2:
-				name = fmt.Sprintf("writebarrierfat%d%d", bvget(applywritebarrier_bv, 0), bvget(applywritebarrier_bv, 1))
-
-			case 3:
-				name = fmt.Sprintf("writebarrierfat%d%d%d", bvget(applywritebarrier_bv, 0), bvget(applywritebarrier_bv, 1), bvget(applywritebarrier_bv, 2))
-
-			case 4:
-				name = fmt.Sprintf("writebarrierfat%d%d%d%d", bvget(applywritebarrier_bv, 0), bvget(applywritebarrier_bv, 1), bvget(applywritebarrier_bv, 2), bvget(applywritebarrier_bv, 3))
-			}
-
-			n = mkcall1(writebarrierfn(name, t, n.Right.Type), nil, init, l, Nodintconst(0), n.Right)
-		} else {
-			r := n.Right
-			for r.Op == OCONVNOP {
-				r = r.Left
-			}
-			r = Nod(OADDR, r, nil)
-			r.Etype = 1 // addr does not escape
-
-			//warnl(n->lineno, "typedmemmove %T %N", t, r);
-			n = mkcall1(writebarrierfn("typedmemmove", t, r.Left.Type), nil, init, typename(t), l, r)
+		if Debug_wb > 1 {
+			Warnl(int(n.Lineno), "marking %v for barrier", Nconv(n.Left, 0))
 		}
+		n.Op = OASWB
+		return n
 	}
 	return n
 }

From 1defd227bd464d13ce32df08594d6642a15ca0b4 Mon Sep 17 00:00:00 2001
From: Alexandre Cesaro 
Date: Mon, 25 May 2015 18:58:19 +0200
Subject: [PATCH 204/232] net/mail: add AddressParser type

Add the AddressParser type to allow decoding any charset in
mail addresses.

Fixes #7079

Change-Id: Ic34efb3e3d804a4e17149a6c38cfd73f5f275ab7
Reviewed-on: https://go-review.googlesource.com/10392
Reviewed-by: Brad Fitzpatrick 
---
 src/net/mail/message.go      |  64 ++++++++-----
 src/net/mail/message_test.go | 171 +++++++++++++++++++++++++++++++++++
 2 files changed, 213 insertions(+), 22 deletions(-)

diff --git a/src/net/mail/message.go b/src/net/mail/message.go
index 77c9578196..04cbfd3e8b 100644
--- a/src/net/mail/message.go
+++ b/src/net/mail/message.go
@@ -138,12 +138,30 @@ type Address struct {
 
 // Parses a single RFC 5322 address, e.g. "Barry Gibbs "
 func ParseAddress(address string) (*Address, error) {
-	return newAddrParser(address).parseAddress()
+	return (&addrParser{s: address}).parseAddress()
 }
 
 // ParseAddressList parses the given string as a list of addresses.
 func ParseAddressList(list string) ([]*Address, error) {
-	return newAddrParser(list).parseAddressList()
+	return (&addrParser{s: list}).parseAddressList()
+}
+
+// An AddressParser is an RFC 5322 address parser.
+type AddressParser struct {
+	// WordDecoder optionally specifies a decoder for RFC 2047 encoded-words.
+	WordDecoder *mime.WordDecoder
+}
+
+// Parse parses a single RFC 5322 address of the
+// form "Gogh Fir " or "foo@example.com".
+func (p *AddressParser) Parse(address string) (*Address, error) {
+	return (&addrParser{s: address, dec: p.WordDecoder}).parseAddress()
+}
+
+// ParseList parses the given string as a list of comma-separated addresses
+// of the form "Gogh Fir " or "foo@example.com".
+func (p *AddressParser) ParseList(list string) ([]*Address, error) {
+	return (&addrParser{s: list, dec: p.WordDecoder}).parseAddressList()
 }
 
 // String formats the address as a valid RFC 5322 address.
@@ -180,11 +198,9 @@ func (a *Address) String() string {
 	return mime.QEncoding.Encode("utf-8", a.Name) + " " + s
 }
 
-type addrParser []byte
-
-func newAddrParser(s string) *addrParser {
-	p := addrParser(s)
-	return &p
+type addrParser struct {
+	s   string
+	dec *mime.WordDecoder // may be nil
 }
 
 func (p *addrParser) parseAddressList() ([]*Address, error) {
@@ -210,7 +226,7 @@ func (p *addrParser) parseAddressList() ([]*Address, error) {
 
 // parseAddress parses a single RFC 5322 address at the start of p.
 func (p *addrParser) parseAddress() (addr *Address, err error) {
-	debug.Printf("parseAddress: %q", *p)
+	debug.Printf("parseAddress: %q", p.s)
 	p.skipSpace()
 	if p.empty() {
 		return nil, errors.New("mail: no address")
@@ -229,7 +245,7 @@ func (p *addrParser) parseAddress() (addr *Address, err error) {
 		}, err
 	}
 	debug.Printf("parseAddress: not an addr-spec: %v", err)
-	debug.Printf("parseAddress: state is now %q", *p)
+	debug.Printf("parseAddress: state is now %q", p.s)
 
 	// display-name
 	var displayName string
@@ -263,7 +279,7 @@ func (p *addrParser) parseAddress() (addr *Address, err error) {
 
 // consumeAddrSpec parses a single RFC 5322 addr-spec at the start of p.
 func (p *addrParser) consumeAddrSpec() (spec string, err error) {
-	debug.Printf("consumeAddrSpec: %q", *p)
+	debug.Printf("consumeAddrSpec: %q", p.s)
 
 	orig := *p
 	defer func() {
@@ -313,7 +329,7 @@ func (p *addrParser) consumeAddrSpec() (spec string, err error) {
 
 // consumePhrase parses the RFC 5322 phrase at the start of p.
 func (p *addrParser) consumePhrase() (phrase string, err error) {
-	debug.Printf("consumePhrase: [%s]", *p)
+	debug.Printf("consumePhrase: [%s]", p.s)
 	// phrase = 1*word
 	var words []string
 	for {
@@ -334,7 +350,7 @@ func (p *addrParser) consumePhrase() (phrase string, err error) {
 		}
 
 		if err == nil {
-			word, err = decodeRFC2047Word(word)
+			word, err = p.decodeRFC2047Word(word)
 		}
 
 		if err != nil {
@@ -362,14 +378,14 @@ Loop:
 		if i >= p.len() {
 			return "", errors.New("mail: unclosed quoted-string")
 		}
-		switch c := (*p)[i]; {
+		switch c := p.s[i]; {
 		case c == '"':
 			break Loop
 		case c == '\\':
 			if i+1 == p.len() {
 				return "", errors.New("mail: unclosed quoted-string")
 			}
-			qsb = append(qsb, (*p)[i+1])
+			qsb = append(qsb, p.s[i+1])
 			i += 2
 		case isQtext(c), c == ' ' || c == '\t':
 			// qtext (printable US-ASCII excluding " and \), or
@@ -380,7 +396,7 @@ Loop:
 			return "", fmt.Errorf("mail: bad character in quoted-string: %q", c)
 		}
 	}
-	*p = (*p)[i+1:]
+	p.s = p.s[i+1:]
 	return string(qsb), nil
 }
 
@@ -391,9 +407,9 @@ func (p *addrParser) consumeAtom(dot bool) (atom string, err error) {
 		return "", errors.New("mail: invalid string")
 	}
 	i := 1
-	for ; i < p.len() && isAtext((*p)[i], dot); i++ {
+	for ; i < p.len() && isAtext(p.s[i], dot); i++ {
 	}
-	atom, *p = string((*p)[:i]), (*p)[i:]
+	atom, p.s = string(p.s[:i]), p.s[i:]
 	return atom, nil
 }
 
@@ -401,17 +417,17 @@ func (p *addrParser) consume(c byte) bool {
 	if p.empty() || p.peek() != c {
 		return false
 	}
-	*p = (*p)[1:]
+	p.s = p.s[1:]
 	return true
 }
 
 // skipSpace skips the leading space and tab characters.
 func (p *addrParser) skipSpace() {
-	*p = bytes.TrimLeft(*p, " \t")
+	p.s = strings.TrimLeft(p.s, " \t")
 }
 
 func (p *addrParser) peek() byte {
-	return (*p)[0]
+	return p.s[0]
 }
 
 func (p *addrParser) empty() bool {
@@ -419,10 +435,14 @@ func (p *addrParser) empty() bool {
 }
 
 func (p *addrParser) len() int {
-	return len(*p)
+	return len(p.s)
 }
 
-func decodeRFC2047Word(s string) (string, error) {
+func (p *addrParser) decodeRFC2047Word(s string) (string, error) {
+	if p.dec != nil {
+		return p.dec.DecodeHeader(s)
+	}
+
 	dec, err := rfc2047Decoder.Decode(s)
 	if err == nil {
 		return dec, nil
diff --git a/src/net/mail/message_test.go b/src/net/mail/message_test.go
index 6ba48be04f..43574c6188 100644
--- a/src/net/mail/message_test.go
+++ b/src/net/mail/message_test.go
@@ -6,7 +6,9 @@ package mail
 
 import (
 	"bytes"
+	"io"
 	"io/ioutil"
+	"mime"
 	"reflect"
 	"strings"
 	"testing"
@@ -278,6 +280,175 @@ func TestAddressParsing(t *testing.T) {
 	}
 }
 
+func TestAddressParser(t *testing.T) {
+	tests := []struct {
+		addrsStr string
+		exp      []*Address
+	}{
+		// Bare address
+		{
+			`jdoe@machine.example`,
+			[]*Address{{
+				Address: "jdoe@machine.example",
+			}},
+		},
+		// RFC 5322, Appendix A.1.1
+		{
+			`John Doe `,
+			[]*Address{{
+				Name:    "John Doe",
+				Address: "jdoe@machine.example",
+			}},
+		},
+		// RFC 5322, Appendix A.1.2
+		{
+			`"Joe Q. Public" `,
+			[]*Address{{
+				Name:    "Joe Q. Public",
+				Address: "john.q.public@example.com",
+			}},
+		},
+		{
+			`Mary Smith , jdoe@example.org, Who? `,
+			[]*Address{
+				{
+					Name:    "Mary Smith",
+					Address: "mary@x.test",
+				},
+				{
+					Address: "jdoe@example.org",
+				},
+				{
+					Name:    "Who?",
+					Address: "one@y.test",
+				},
+			},
+		},
+		{
+			`, "Giant; \"Big\" Box" `,
+			[]*Address{
+				{
+					Address: "boss@nil.test",
+				},
+				{
+					Name:    `Giant; "Big" Box`,
+					Address: "sysservices@example.net",
+				},
+			},
+		},
+		// RFC 2047 "Q"-encoded ISO-8859-1 address.
+		{
+			`=?iso-8859-1?q?J=F6rg_Doe?= `,
+			[]*Address{
+				{
+					Name:    `Jörg Doe`,
+					Address: "joerg@example.com",
+				},
+			},
+		},
+		// RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal.
+		{
+			`=?us-ascii?q?J=6Frg_Doe?= `,
+			[]*Address{
+				{
+					Name:    `Jorg Doe`,
+					Address: "joerg@example.com",
+				},
+			},
+		},
+		// RFC 2047 "Q"-encoded ISO-8859-15 address.
+		{
+			`=?ISO-8859-15?Q?J=F6rg_Doe?= `,
+			[]*Address{
+				{
+					Name:    `Jörg Doe`,
+					Address: "joerg@example.com",
+				},
+			},
+		},
+		// RFC 2047 "B"-encoded windows-1252 address.
+		{
+			`=?windows-1252?q?Andr=E9?= Pirard `,
+			[]*Address{
+				{
+					Name:    `André Pirard`,
+					Address: "PIRARD@vm1.ulg.ac.be",
+				},
+			},
+		},
+		// Custom example of RFC 2047 "B"-encoded ISO-8859-15 address.
+		{
+			`=?ISO-8859-15?B?SvZyZw==?= `,
+			[]*Address{
+				{
+					Name:    `Jörg`,
+					Address: "joerg@example.com",
+				},
+			},
+		},
+		// Custom example of RFC 2047 "B"-encoded UTF-8 address.
+		{
+			`=?UTF-8?B?SsO2cmc=?= `,
+			[]*Address{
+				{
+					Name:    `Jörg`,
+					Address: "joerg@example.com",
+				},
+			},
+		},
+		// Custom example with "." in name. For issue 4938
+		{
+			`Asem H. `,
+			[]*Address{
+				{
+					Name:    `Asem H.`,
+					Address: "noreply@example.com",
+				},
+			},
+		},
+	}
+
+	ap := AddressParser{WordDecoder: &mime.WordDecoder{
+		CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
+			in, err := ioutil.ReadAll(input)
+			if err != nil {
+				return nil, err
+			}
+
+			switch charset {
+			case "iso-8859-15":
+				in = bytes.Replace(in, []byte("\xf6"), []byte("ö"), -1)
+			case "windows-1252":
+				in = bytes.Replace(in, []byte("\xe9"), []byte("é"), -1)
+			}
+
+			return bytes.NewReader(in), nil
+		},
+	}}
+
+	for _, test := range tests {
+		if len(test.exp) == 1 {
+			addr, err := ap.Parse(test.addrsStr)
+			if err != nil {
+				t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
+				continue
+			}
+			if !reflect.DeepEqual([]*Address{addr}, test.exp) {
+				t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
+			}
+		}
+
+		addrs, err := ap.ParseList(test.addrsStr)
+		if err != nil {
+			t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
+			continue
+		}
+		if !reflect.DeepEqual(addrs, test.exp) {
+			t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
+		}
+	}
+}
+
 func TestAddressFormatting(t *testing.T) {
 	tests := []struct {
 		addr *Address

From 310fb9e8087471f6ab65d8c464df10572c0d38ed Mon Sep 17 00:00:00 2001
From: Robert Griesemer 
Date: Tue, 26 May 2015 11:44:18 -0700
Subject: [PATCH 205/232] spec: removed TODOs (invisible html comment) in favor
 of issues

- no "visible" change to spec but for updated date
- retired several outdated TODO items
- filed non-urgent issues 10953, 10954, 10955 for current TODOs

Change-Id: If87ad0fb546c6955a6d4b5801e06e5c7d5695ea2
Reviewed-on: https://go-review.googlesource.com/10382
Reviewed-by: Ian Lance Taylor 
---
 doc/go_spec.html | 17 +----------------
 1 file changed, 1 insertion(+), 16 deletions(-)

diff --git a/doc/go_spec.html b/doc/go_spec.html
index cdcca6be57..b5f18f3a02 100644
--- a/doc/go_spec.html
+++ b/doc/go_spec.html
@@ -1,24 +1,9 @@
 
 
-
-
-
 

Introduction

From b2f95a167aacc11177d66d1a3235d84a2b1f3538 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Tue, 26 May 2015 13:49:59 -0700 Subject: [PATCH 206/232] cmd/link/internal/amd64: -buildmode=c-archive forces external link mode At some point this code should probably move to cmd/link/internal/ld, but at least for now just handle c-archive like c-shared. Change-Id: Ic17656529cb0fe189a37f15e670350ab13bb5276 Reviewed-on: https://go-review.googlesource.com/10385 Reviewed-by: Andrew Gerrand --- src/cmd/link/internal/amd64/obj.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go index e489bb75a9..bb65067e87 100644 --- a/src/cmd/link/internal/amd64/obj.go +++ b/src/cmd/link/internal/amd64/obj.go @@ -90,7 +90,7 @@ func archinit() { ld.Linkmode = ld.LinkInternal } - if ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() { + if ld.Buildmode == ld.BuildmodeCArchive || ld.Buildmode == ld.BuildmodeCShared || ld.DynlinkingGo() { ld.Linkmode = ld.LinkExternal } From fad25c29a1c8c3cffdd18ff8ebff3ead3bdfed1f Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 26 May 2015 13:49:55 -0700 Subject: [PATCH 207/232] archive/zip: verify number of File bytes read at EOF Fixes #10957 Change-Id: I75fe25133dfcebd1682a8058b1c354ec894cc997 Reviewed-on: https://go-review.googlesource.com/10384 Run-TryBot: Brad Fitzpatrick Reviewed-by: Andrew Gerrand TryBot-Result: Gobot Gobot --- src/archive/zip/reader.go | 22 +++++++++++++----- src/archive/zip/reader_test.go | 42 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go index 8136b840d4..10d9d5e5bf 100644 --- a/src/archive/zip/reader.go +++ b/src/archive/zip/reader.go @@ -146,16 +146,22 @@ func (f *File) Open() (rc io.ReadCloser, err error) { if f.hasDataDescriptor() { desr = io.NewSectionReader(f.zipr, f.headerOffset+bodyOffset+size, dataDescriptorLen) } - rc = &checksumReader{rc, crc32.NewIEEE(), f, desr, nil} + rc = &checksumReader{ + rc: rc, + hash: crc32.NewIEEE(), + f: f, + desr: desr, + } return } type checksumReader struct { - rc io.ReadCloser - hash hash.Hash32 - f *File - desr io.Reader // if non-nil, where to read the data descriptor - err error // sticky error + rc io.ReadCloser + hash hash.Hash32 + nread uint64 // number of bytes read so far + f *File + desr io.Reader // if non-nil, where to read the data descriptor + err error // sticky error } func (r *checksumReader) Read(b []byte) (n int, err error) { @@ -164,10 +170,14 @@ func (r *checksumReader) Read(b []byte) (n int, err error) { } n, err = r.rc.Read(b) r.hash.Write(b[:n]) + r.nread += uint64(n) if err == nil { return } if err == io.EOF { + if r.nread != r.f.UncompressedSize64 { + return 0, io.ErrUnexpectedEOF + } if r.desr != nil { if err1 := readDataDescriptor(r.desr, r.f); err1 != nil { err = err1 diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go index 29d0652dcc..6a8cab34cd 100644 --- a/src/archive/zip/reader_test.go +++ b/src/archive/zip/reader_test.go @@ -531,3 +531,45 @@ func TestIssue8186(t *testing.T) { } } } + +// Verify we return ErrUnexpectedEOF when length is short. +func TestIssue10957(t *testing.T) { + data := []byte("PK\x03\x040000000PK\x01\x0200000" + + "0000000000000000000\x00" + + "\x00\x00\x00\x00\x00000000000000PK\x01" + + "\x020000000000000000000" + + "00000\v\x00\x00\x00\x00\x00000000000" + + "00000000000000PK\x01\x0200" + + "00000000000000000000" + + "00\v\x00\x00\x00\x00\x00000000000000" + + "00000000000PK\x01\x020000<" + + "0\x00\x0000000000000000\v\x00\v" + + "\x00\x00\x00\x00\x0000000000\x00\x00\x00\x00000" + + "00000000PK\x01\x0200000000" + + "0000000000000000\v\x00\x00\x00" + + "\x00\x0000PK\x05\x06000000\x05\x000000" + + "\v\x00\x00\x00\x00\x00") + z, err := NewReader(bytes.NewReader(data), int64(len(data))) + if err != nil { + if z != nil { + panic("non nil z") + } + return + } + for i, f := range z.File { + r, err := f.Open() + if err != nil { + continue + } + if f.UncompressedSize64 < 1e6 { + n, err := io.Copy(ioutil.Discard, r) + if i == 3 && err != io.ErrUnexpectedEOF { + t.Errorf("File[3] error = %v; want io.ErrUnexpectedEOF", err) + } + if err == nil && uint64(n) != f.UncompressedSize64 { + t.Errorf("file %d: bad size: copied=%d; want=%d", i, n, f.UncompressedSize64) + } + } + r.Close() + } +} From bc89ad598e51b1f4935c2574edbabafff4dc1dea Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Wed, 8 Apr 2015 12:55:34 -0700 Subject: [PATCH 208/232] cmd/internal/objfile: Skip mach-o debug symbols. This allows objdump to disassemble gcc generated binaries on OS X 10.6. Change-Id: I1a5bfbf7c252e78215ef1f122520689d5ce6ddca Reviewed-on: https://go-review.googlesource.com/10383 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- src/cmd/internal/objfile/macho.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/cmd/internal/objfile/macho.go b/src/cmd/internal/objfile/macho.go index a6cd02b930..7371c0d9d1 100644 --- a/src/cmd/internal/objfile/macho.go +++ b/src/cmd/internal/objfile/macho.go @@ -13,6 +13,8 @@ import ( "sort" ) +const stabTypeMask = 0xe0 + type machoFile struct { macho *macho.File } @@ -34,12 +36,19 @@ func (f *machoFile) symbols() ([]Sym, error) { // We infer the size of a symbol by looking at where the next symbol begins. var addrs []uint64 for _, s := range f.macho.Symtab.Syms { - addrs = append(addrs, s.Value) + // Skip stab debug info. + if s.Type&stabTypeMask == 0 { + addrs = append(addrs, s.Value) + } } sort.Sort(uint64s(addrs)) var syms []Sym for _, s := range f.macho.Symtab.Syms { + if s.Type&stabTypeMask != 0 { + // Skip stab debug info. + continue + } sym := Sym{Name: s.Name, Addr: s.Value, Code: '?'} i := sort.Search(len(addrs), func(x int) bool { return addrs[x] > s.Value }) if i < len(addrs) { From cf2736c4c5bbb24b9a4ff9690887a0cb287ba34c Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 27 May 2015 12:04:25 +1200 Subject: [PATCH 209/232] cmd/link: replace interface{} fields with concrete types The LSym.Section and Section.Elfsect fields were defined as interface{} but always had the same concrete type (*Section and *ElfShdr respectively) so just define them with that type. Reduces size of LSym from 328 to 320 bytes and reduces best-of-10 maxresident size from 246028k to 238036k when linking libstd.so. Change-Id: Ie7112c53e4c2c7ce5fe233b81372aa5633f572e8 Reviewed-on: https://go-review.googlesource.com/10410 Reviewed-by: Minux Ma Reviewed-by: Ian Lance Taylor --- src/cmd/link/internal/amd64/asm.go | 4 ++-- src/cmd/link/internal/arm/asm.go | 4 ++-- src/cmd/link/internal/arm64/asm.go | 4 ++-- src/cmd/link/internal/ld/data.go | 6 +++--- src/cmd/link/internal/ld/elf.go | 2 +- src/cmd/link/internal/ld/lib.go | 2 +- src/cmd/link/internal/ld/link.go | 2 +- src/cmd/link/internal/ld/macho.go | 2 +- src/cmd/link/internal/ld/symtab.go | 6 +++--- src/cmd/link/internal/x86/asm.go | 4 ++-- 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index a4883f1a33..74ec9dd3ea 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -405,9 +405,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int { v = uint32(rs.Dynid) v |= 1 << 27 // external relocation } else { - v = uint32((rs.Sect.(*ld.Section)).Extnum) + v = uint32(rs.Sect.Extnum) if v == 0 { - ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type) + ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type) return -1 } } diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index e310d29e9a..39d4550917 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -279,9 +279,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int { v = uint32(rs.Dynid) v |= 1 << 27 // external relocation } else { - v = uint32((rs.Sect.(*ld.Section)).Extnum) + v = uint32(rs.Sect.Extnum) if v == 0 { - ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type) + ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type) return -1 } } diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 9d76f0e0c3..3aebd8a223 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -107,9 +107,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int { v = uint32(rs.Dynid) v |= 1 << 27 // external relocation } else { - v = uint32((rs.Sect.(*ld.Section)).Extnum) + v = uint32(rs.Sect.Extnum) if v == 0 { - ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type) + ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type) return -1 } } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index f9aacf0e19..cf28e7b384 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -522,7 +522,7 @@ func relocsym(s *LSym) { } else if HEADTYPE == obj.Hdarwin { if r.Type == obj.R_CALL { if rs.Type != obj.SHOSTOBJ { - o += int64(uint64(Symaddr(rs)) - (rs.Sect.(*Section)).Vaddr) + o += int64(uint64(Symaddr(rs)) - rs.Sect.Vaddr) } o -= int64(r.Off) // relative to section offset, not symbol } else { @@ -534,7 +534,7 @@ func relocsym(s *LSym) { o += int64(r.Siz) // GNU ld always add VirtualAddress of the .text section to the // relocated address, compensate that. - o -= int64(s.Sect.(*Section).Vaddr - PEBASE) + o -= int64(s.Sect.Vaddr - PEBASE) } else { Diag("unhandled pcrel relocation for %s", headstring) } @@ -1681,7 +1681,7 @@ func address() { for sym := datap; sym != nil; sym = sym.Next { Ctxt.Cursym = sym if sym.Sect != nil { - sym.Value += int64((sym.Sect.(*Section)).Vaddr) + sym.Value += int64(sym.Sect.Vaddr) } for sub = sym.Sub; sub != nil; sub = sub.Sub { sub.Value += sym.Value diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index b73a75b59b..d26a82e64a 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -1517,7 +1517,7 @@ func elfshreloc(sect *Section) *ElfShdr { sh.entsize += uint64(Thearch.Regsize) } sh.link = uint32(elfshname(".symtab").shnum) - sh.info = uint32((sect.Elfsect.(*ElfShdr)).shnum) + sh.info = uint32(sect.Elfsect.shnum) sh.off = sect.Reloff sh.size = sect.Rellen sh.addralign = uint64(Thearch.Regsize) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 80c01538ac..ea82ea5995 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -162,7 +162,7 @@ type Section struct { Length uint64 Next *Section Seg *Segment - Elfsect interface{} + Elfsect *ElfShdr Reloff uint64 Rellen uint64 } diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 3098147819..4b034a4e81 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -78,7 +78,7 @@ type LSym struct { File string Dynimplib string Dynimpvers string - Sect interface{} + Sect *Section Autom *Auto Pcln *Pcln P []byte diff --git a/src/cmd/link/internal/ld/macho.go b/src/cmd/link/internal/ld/macho.go index 0258aff104..3a8a881d97 100644 --- a/src/cmd/link/internal/ld/macho.go +++ b/src/cmd/link/internal/ld/macho.go @@ -706,7 +706,7 @@ func machosymtab() { Diag("missing section for %s", s.Name) Adduint8(Ctxt, symtab, 0) } else { - Adduint8(Ctxt, symtab, uint8((o.Sect.(*Section)).Extnum)) + Adduint8(Ctxt, symtab, uint8(o.Sect.Extnum)) } Adduint16(Ctxt, symtab, 0) // desc adduintxx(Ctxt, symtab, uint64(Symaddr(s)), Thearch.Ptrsize) diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index ca66541935..12476f79a2 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -132,12 +132,12 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L Diag("missing section in putelfsym") return } - if xo.Sect.(*Section).Elfsect == nil { + if xo.Sect.Elfsect == nil { Ctxt.Cursym = x Diag("missing ELF section in putelfsym") return } - elfshnum = xo.Sect.(*Section).Elfsect.(*ElfShdr).shnum + elfshnum = xo.Sect.Elfsect.shnum } // One pass for each binding: STB_LOCAL, STB_GLOBAL, @@ -163,7 +163,7 @@ func putelfsym(x *LSym, s string, t int, addr int64, size int64, ver int, go_ *L off := putelfstr(s) if Linkmode == LinkExternal && elfshnum != SHN_UNDEF { - addr -= int64(xo.Sect.(*Section).Vaddr) + addr -= int64(xo.Sect.Vaddr) } other := STV_DEFAULT if x.Type&obj.SHIDDEN != 0 { diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 7bb99ca8b5..d30bd48b4e 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -276,9 +276,9 @@ func machoreloc1(r *ld.Reloc, sectoff int64) int { v = uint32(rs.Dynid) v |= 1 << 27 // external relocation } else { - v = uint32((rs.Sect.(*ld.Section)).Extnum) + v = uint32(rs.Sect.Extnum) if v == 0 { - ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, (rs.Sect.(*ld.Section)).Name, rs.Type) + ld.Diag("reloc %d to symbol %s in non-macho section %s type=%d", r.Type, rs.Name, rs.Sect.Name, rs.Type) return -1 } } From 9262e2183bd1a6d8489f0d597053af76194ede00 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Wed, 27 May 2015 10:30:55 +1200 Subject: [PATCH 210/232] misc/cgo/testshared: do not capture output of go commands in verbose mode Change-Id: I8694ee5e5642c31815ae63cd414a3b1fcd9c95b0 Reviewed-on: https://go-review.googlesource.com/10411 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- misc/cgo/testshared/shared_test.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index fd577b03b5..c7f998c5c0 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -47,10 +47,17 @@ func goCmd(t *testing.T, args ...string) { } newargs = append(newargs, args[1:]...) c := exec.Command("go", newargs...) + var output []byte + var err error if testing.Verbose() { fmt.Printf("+ go %s\n", strings.Join(newargs, " ")) + c.Stdout = os.Stdout + c.Stderr = os.Stderr + err = c.Run() + } else { + output, err = c.CombinedOutput() } - if output, err := c.CombinedOutput(); err != nil { + if err != nil { if t != nil { t.Errorf("executing %s failed %v:\n%s", strings.Join(c.Args, " "), err, output) } else { From 65518032b946bbb640cf1e3d5e66f537642503ed Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 25 May 2015 13:59:08 +1200 Subject: [PATCH 211/232] cmd/link/internal/ld: put abi hash into a note This makes for a more stable API for tools (including cmd/link itself) to extract the abi hash from a shared library and makes it possible at all for a library that has had the local symbol table removed. The existing note-writing code only supports writing notes into the very start of the object file so they are easy to find in core dumps. This doesn't apply to the "go" notes and means that all notes have to fit into a fixed size budget. That's annoying now we have more notes (and the next CL will add another one) so this does a little bit of work to make adding notes that do not have to go at the start of the file easier and moves the writing of the package list note over to that mechanism, which lets me revert a hack that increased the size budget mentioned above for -buildmode=shared builds. Change-Id: I6077a68d395c8a2bc43dec8506e73c71ef77d9b9 Reviewed-on: https://go-review.googlesource.com/10375 Reviewed-by: Ian Lance Taylor --- misc/cgo/testshared/shared_test.go | 176 +++++++++++++++++++++++++++++ src/cmd/link/internal/amd64/obj.go | 8 -- src/cmd/link/internal/ld/data.go | 7 ++ src/cmd/link/internal/ld/elf.go | 112 ++++++++++++------ src/cmd/link/internal/ld/lib.go | 65 ++++++++++- src/cmd/link/internal/ld/symtab.go | 12 +- 6 files changed, 322 insertions(+), 58 deletions(-) diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index c7f998c5c0..1d731af2a2 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -8,10 +8,12 @@ import ( "bufio" "bytes" "debug/elf" + "encoding/binary" "errors" "flag" "fmt" "go/build" + "io" "io/ioutil" "log" "math/rand" @@ -176,6 +178,89 @@ func TestShlibnameFiles(t *testing.T) { } } +// Is a given offset into the file contained in a loaded segment? +func isOffsetLoaded(f *elf.File, offset uint64) bool { + for _, prog := range f.Progs { + if prog.Type == elf.PT_LOAD { + if prog.Off <= offset && offset < prog.Off+prog.Filesz { + return true + } + } + } + return false +} + +func rnd(v int32, r int32) int32 { + if r <= 0 { + return v + } + v += r - 1 + c := v % r + if c < 0 { + c += r + } + v -= c + return v +} + +func readwithpad(r io.Reader, sz int32) ([]byte, error) { + data := make([]byte, rnd(sz, 4)) + _, err := io.ReadFull(r, data) + if err != nil { + return nil, err + } + data = data[:sz] + return data, nil +} + +type note struct { + name string + tag int32 + desc string + section *elf.Section +} + +// Read all notes from f. As ELF section names are not supposed to be special, one +// looks for a particular note by scanning all SHT_NOTE sections looking for a note +// with a particular "name" and "tag". +func readNotes(f *elf.File) ([]*note, error) { + var notes []*note + for _, sect := range f.Sections { + if sect.Type != elf.SHT_NOTE { + continue + } + r := sect.Open() + for { + var namesize, descsize, tag int32 + err := binary.Read(r, f.ByteOrder, &namesize) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("read namesize failed:", err) + } + err = binary.Read(r, f.ByteOrder, &descsize) + if err != nil { + return nil, fmt.Errorf("read descsize failed:", err) + } + err = binary.Read(r, f.ByteOrder, &tag) + if err != nil { + return nil, fmt.Errorf("read type failed:", err) + } + name, err := readwithpad(r, namesize) + if err != nil { + return nil, fmt.Errorf("read name failed:", err) + } + desc, err := readwithpad(r, descsize) + if err != nil { + return nil, fmt.Errorf("read desc failed:", err) + } + notes = append(notes, ¬e{name: string(name), tag: tag, desc: string(desc), section: sect}) + } + } + return notes, nil +} + func dynStrings(path string, flag elf.DynTag) []string { f, err := elf.Open(path) defer f.Close() @@ -233,6 +318,97 @@ func TestGOPathShlib(t *testing.T) { run(t, "executable linked to GOPATH library", "./bin/exe") } +// The shared library contains a note listing the packages it contains in a section +// that is not mapped into memory. +func testPkgListNote(t *testing.T, f *elf.File, note *note) { + if note.section.Flags != 0 { + t.Errorf("package list section has flags %v", note.section.Flags) + } + if isOffsetLoaded(f, note.section.Offset) { + t.Errorf("package list section contained in PT_LOAD segment") + } + if note.desc != "dep\n" { + t.Errorf("incorrect package list %q", note.desc) + } +} + +// The shared library contains a note containing the ABI hash that is mapped into +// memory and there is a local symbol called go.link.abihashbytes that points 16 +// bytes into it. +func testABIHashNote(t *testing.T, f *elf.File, note *note) { + if note.section.Flags != elf.SHF_ALLOC { + t.Errorf("abi hash section has flags %v", note.section.Flags) + } + if !isOffsetLoaded(f, note.section.Offset) { + t.Errorf("abihash section not contained in PT_LOAD segment") + } + var hashbytes elf.Symbol + symbols, err := f.Symbols() + if err != nil { + t.Errorf("error reading symbols %v", err) + return + } + for _, sym := range symbols { + if sym.Name == "go.link.abihashbytes" { + hashbytes = sym + } + } + if hashbytes.Name == "" { + t.Errorf("no symbol called go.link.abihashbytes") + return + } + if elf.ST_BIND(hashbytes.Info) != elf.STB_LOCAL { + t.Errorf("%s has incorrect binding %v", hashbytes.Name, elf.ST_BIND(hashbytes.Info)) + } + if f.Sections[hashbytes.Section] != note.section { + t.Errorf("%s has incorrect section %v", hashbytes.Name, f.Sections[hashbytes.Section].Name) + } + if hashbytes.Value-note.section.Addr != 16 { + t.Errorf("%s has incorrect offset into section %d", hashbytes.Name, hashbytes.Value-note.section.Addr) + } +} + +// The shared library contains notes with defined contents; see above. +func TestNotes(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep") + f, err := elf.Open(filepath.Join(gopathInstallDir, "libdep.so")) + if err != nil { + t.Fatal(err) + } + defer f.Close() + notes, err := readNotes(f) + if err != nil { + t.Fatal(err) + } + pkgListNoteFound := false + abiHashNoteFound := false + for _, note := range notes { + if note.name != "GO\x00\x00" { + continue + } + switch note.tag { + case 1: // ELF_NOTE_GOPKGLIST_TAG + if pkgListNoteFound { + t.Error("multiple package list notes") + } + testPkgListNote(t, f, note) + pkgListNoteFound = true + case 2: // ELF_NOTE_GOABIHASH_TAG + if abiHashNoteFound { + t.Error("multiple abi hash notes") + } + testABIHashNote(t, f, note) + abiHashNoteFound = true + } + } + if !pkgListNoteFound { + t.Error("package list note not found") + } + if !abiHashNoteFound { + t.Error("abi hash note not found") + } +} + // Testing rebuilding of shared libraries when they are stale is a bit more // complicated that it seems like it should be. First, we make everything "old": but // only a few seconds old, or it might be older than 6g (or the runtime source) and diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go index bb65067e87..1aa4422ed9 100644 --- a/src/cmd/link/internal/amd64/obj.go +++ b/src/cmd/link/internal/amd64/obj.go @@ -168,14 +168,6 @@ func archinit() { ld.Elfinit() ld.HEADR = ld.ELFRESERVE - if ld.Buildmode == ld.BuildmodeShared { - // When building a shared library we write a package list - // note that can get quite large. The external linker will - // re-layout all the sections anyway, so making this larger - // just wastes a little space in the intermediate object - // file, not the final shared library. - ld.HEADR *= 3 - } if ld.INITTEXT == -1 { ld.INITTEXT = (1 << 22) + int64(ld.HEADR) } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index cf28e7b384..e8d30f6a89 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1688,6 +1688,13 @@ func address() { } } + if Buildmode == BuildmodeShared { + s := Linklookup(Ctxt, "go.link.abihashbytes", 0) + sectSym := Linklookup(Ctxt, ".note.go.abihash", 0) + s.Sect = sectSym.Sect + s.Value = int64(sectSym.Sect.Vaddr + 16) + } + xdefine("runtime.text", obj.STEXT, int64(text.Vaddr)) xdefine("runtime.etext", obj.STEXT, int64(text.Vaddr+text.Length)) xdefine("runtime.rodata", obj.SRODATA, int64(rodata.Vaddr)) diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index d26a82e64a..83f10d39f6 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -6,8 +6,10 @@ package ld import ( "cmd/internal/obj" + "crypto/sha1" "encoding/binary" "fmt" + "sort" ) /* @@ -1199,32 +1201,14 @@ func elfwritebuildinfo() int { return int(sh.size) } -// Go package list note +// Go specific notes const ( ELF_NOTE_GOPKGLIST_TAG = 1 + ELF_NOTE_GOABIHASH_TAG = 2 ) var ELF_NOTE_GO_NAME = []byte("GO\x00\x00") -func elfgopkgnote(sh *ElfShdr, startva uint64, resoff uint64) int { - n := len(ELF_NOTE_GO_NAME) + int(Rnd(int64(len(pkglistfornote)), 4)) - return elfnote(sh, startva, resoff, n, false) -} - -func elfwritegopkgnote() int { - sh := elfwritenotehdr(".note.go.pkg-list", uint32(len(ELF_NOTE_GO_NAME)), uint32(len(pkglistfornote)), ELF_NOTE_GOPKGLIST_TAG) - if sh == nil { - return 0 - } - - Cwrite(ELF_NOTE_GO_NAME) - Cwrite(pkglistfornote) - var zero = make([]byte, 4) - Cwrite(zero[:int(Rnd(int64(len(pkglistfornote)), 4)-int64(len(pkglistfornote)))]) - - return int(sh.size) -} - var elfverneed int type Elfaux struct { @@ -1455,6 +1439,24 @@ func elfshalloc(sect *Section) *ElfShdr { func elfshbits(sect *Section) *ElfShdr { sh := elfshalloc(sect) + // If this section has already been set up as a note, we assume type_ and + // flags are already correct, but the other fields still need filling in. + if sh.type_ == SHT_NOTE { + if Linkmode != LinkExternal { + // TODO(mwhudson): the approach here will work OK when + // linking internally for notes that we want to be included + // in a loadable segment (e.g. the abihash note) but not for + // notes that we do not want to be mapped (e.g. the package + // list note). The real fix is probably to define new values + // for LSym.Type corresponding to mapped and unmapped notes + // and handle them in dodata(). + Diag("sh.type_ == SHT_NOTE in elfshbits when linking internally") + } + sh.addralign = uint64(sect.Align) + sh.size = sect.Length + sh.off = sect.Seg.Fileoff + sect.Vaddr - sect.Seg.Vaddr + return sh + } if sh.type_ > 0 { return sh } @@ -1490,13 +1492,16 @@ func elfshbits(sect *Section) *ElfShdr { func elfshreloc(sect *Section) *ElfShdr { // If main section is SHT_NOBITS, nothing to relocate. - // Also nothing to relocate in .shstrtab. + // Also nothing to relocate in .shstrtab or notes. if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen { return nil } if sect.Name == ".shstrtab" || sect.Name == ".tbss" { return nil } + if sect.Elfsect.type_ == SHT_NOTE { + return nil + } var prefix string var typ int @@ -1596,6 +1601,29 @@ func Elfemitreloc() { } } +func addgonote(sectionName string, tag uint32, desc []byte) { + s := Linklookup(Ctxt, sectionName, 0) + s.Reachable = true + s.Type = obj.SELFROSECT + // namesz + Adduint32(Ctxt, s, uint32(len(ELF_NOTE_GO_NAME))) + // descsz + Adduint32(Ctxt, s, uint32(len(desc))) + // tag + Adduint32(Ctxt, s, tag) + // name + padding + s.P = append(s.P, ELF_NOTE_GO_NAME...) + for len(s.P)%4 != 0 { + s.P = append(s.P, 0) + } + // desc + padding + s.P = append(s.P, desc...) + for len(s.P)%4 != 0 { + s.P = append(s.P, 0) + } + s.Size = int64(len(s.P)) +} + func doelf() { if !Iself { return @@ -1632,9 +1660,6 @@ func doelf() { if len(buildinfo) > 0 { Addstring(shstrtab, ".note.gnu.build-id") } - if Buildmode == BuildmodeShared { - Addstring(shstrtab, ".note.go.pkg-list") - } Addstring(shstrtab, ".elfdata") Addstring(shstrtab, ".rodata") Addstring(shstrtab, ".typelink") @@ -1668,6 +1693,11 @@ func doelf() { // add a .note.GNU-stack section to mark the stack as non-executable Addstring(shstrtab, ".note.GNU-stack") + + if Buildmode == BuildmodeShared { + Addstring(shstrtab, ".note.go.abihash") + Addstring(shstrtab, ".note.go.pkg-list") + } } hasinitarr := Linkshared @@ -1856,6 +1886,25 @@ func doelf() { // size of .rel(a).plt section. Elfwritedynent(s, DT_DEBUG, 0) } + + if Buildmode == BuildmodeShared { + // The go.link.abihashbytes symbol will be pointed at the appropriate + // part of the .note.go.abihash section in data.go:func address(). + s := Linklookup(Ctxt, "go.link.abihashbytes", 0) + s.Local = true + s.Type = obj.SRODATA + s.Special = 1 + s.Reachable = true + s.Size = int64(sha1.Size) + + sort.Sort(byPkg(Ctxt.Library)) + h := sha1.New() + for _, l := range Ctxt.Library { + h.Write(l.hash) + } + addgonote(".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{})) + addgonote(".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, []byte(pkglistfornote)) + } } // Do not write DT_NULL. elfdynhash will finish it. @@ -1922,15 +1971,11 @@ func Asmbelf(symo int64) { eh.phentsize = 0 if Buildmode == BuildmodeShared { - // The package list note we make space for here can get quite - // large. The external linker will re-layout all the sections - // anyway, so making this larger just wastes a little space - // in the intermediate object file, not the final shared - // library. - elfreserve *= 3 - resoff = elfreserve sh := elfshname(".note.go.pkg-list") - resoff -= int64(elfgopkgnote(sh, uint64(startva), uint64(resoff))) + sh.type_ = SHT_NOTE + sh = elfshname(".note.go.abihash") + sh.type_ = SHT_NOTE + sh.flags = SHF_ALLOC } goto elfobj } @@ -2340,9 +2385,6 @@ elfobj: a += int64(elfwritebuildinfo()) } } - if Buildmode == BuildmodeShared { - a += int64(elfwritegopkgnote()) - } if a > elfreserve { Diag("ELFRESERVE too small: %d > %d", a, elfreserve) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index ea82ea5995..26d722911b 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -36,6 +36,7 @@ import ( "cmd/internal/obj" "crypto/sha1" "debug/elf" + "encoding/binary" "fmt" "io" "io/ioutil" @@ -1154,8 +1155,8 @@ func ldobj(f *obj.Biobuf, pkg string, length int64, pn string, file string, when func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte { data := make([]byte, sym.Size) sect := f.Sections[sym.Section] - if sect.Type != elf.SHT_PROGBITS { - Diag("reading %s from non-PROGBITS section", sym.Name) + if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { + Diag("reading %s from non-data section", sym.Name) } n, err := sect.ReadAt(data, int64(sym.Value-sect.Offset)) if uint64(n) != sym.Size { @@ -1164,6 +1165,55 @@ func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte { return data } +func readwithpad(r io.Reader, sz int32) ([]byte, error) { + data := make([]byte, Rnd(int64(sz), 4)) + _, err := io.ReadFull(r, data) + if err != nil { + return nil, err + } + data = data[:sz] + return data, nil +} + +func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) { + for _, sect := range f.Sections { + if sect.Type != elf.SHT_NOTE { + continue + } + r := sect.Open() + for { + var namesize, descsize, noteType int32 + err := binary.Read(r, f.ByteOrder, &namesize) + if err != nil { + if err == io.EOF { + break + } + return nil, fmt.Errorf("read namesize failed:", err) + } + err = binary.Read(r, f.ByteOrder, &descsize) + if err != nil { + return nil, fmt.Errorf("read descsize failed:", err) + } + err = binary.Read(r, f.ByteOrder, ¬eType) + if err != nil { + return nil, fmt.Errorf("read type failed:", err) + } + noteName, err := readwithpad(r, namesize) + if err != nil { + return nil, fmt.Errorf("read name failed:", err) + } + desc, err := readwithpad(r, descsize) + if err != nil { + return nil, fmt.Errorf("read desc failed:", err) + } + if string(name) == string(noteName) && typ == noteType { + return desc, nil + } + } + } + return nil, nil +} + func ldshlibsyms(shlib string) { found := false libpath := "" @@ -1194,6 +1244,13 @@ func ldshlibsyms(shlib string) { return } defer f.Close() + + hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) + if err != nil { + Diag("cannot read ABI hash from shared library %s: %v", libpath, err) + return + } + syms, err := f.Symbols() if err != nil { Diag("cannot read symbols from shared library: %s", libpath) @@ -1211,7 +1268,6 @@ func ldshlibsyms(shlib string) { // table removed. gcmasks := make(map[uint64][]byte) types := []*LSym{} - var hash []byte for _, s := range syms { if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION { continue @@ -1225,9 +1281,6 @@ func ldshlibsyms(shlib string) { if strings.HasPrefix(s.Name, "runtime.gcbits.") { gcmasks[s.Value] = readelfsymboldata(f, &s) } - if s.Name == "go.link.abihashbytes" { - hash = readelfsymboldata(f, &s) - } if elf.ST_BIND(s.Info) != elf.STB_GLOBAL { continue } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index 12476f79a2..7ceb64f941 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -32,10 +32,8 @@ package ld import ( "cmd/internal/obj" - "crypto/sha1" "fmt" "path/filepath" - "sort" "strings" ) @@ -429,16 +427,12 @@ func symtab() { } if Buildmode == BuildmodeShared { - sort.Sort(byPkg(Ctxt.Library)) - h := sha1.New() - for _, l := range Ctxt.Library { - h.Write(l.hash) - } abihashgostr := Linklookup(Ctxt, "go.link.abihash."+filepath.Base(outfile), 0) abihashgostr.Reachable = true abihashgostr.Type = obj.SRODATA - var hashbytes []byte - addgostring(abihashgostr, "go.link.abihashbytes", string(h.Sum(hashbytes))) + hashsym := Linklookup(Ctxt, "go.link.abihashbytes", 0) + Addaddr(Ctxt, abihashgostr, hashsym) + adduint(Ctxt, abihashgostr, uint64(hashsym.Size)) } // Information about the layout of the executable image for the From 02f40842990c6f5e219f2ecd3f91041a820210e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5vard=20Haugen?= Date: Wed, 27 May 2015 10:44:44 +0200 Subject: [PATCH 212/232] archive/tar: don't panic on negative file size Fixes #10959. Fixes #10960. Change-Id: I9a81a0e2b8275338d0d1c3f7f7265e0fd91f3de2 Reviewed-on: https://go-review.googlesource.com/10402 TryBot-Result: Gobot Gobot Reviewed-by: David Symonds --- src/archive/tar/reader.go | 4 ++++ src/archive/tar/reader_test.go | 16 ++++++++++++++++ src/archive/tar/testdata/neg-size.tar | Bin 0 -> 512 bytes 3 files changed, 20 insertions(+) create mode 100644 src/archive/tar/testdata/neg-size.tar diff --git a/src/archive/tar/reader.go b/src/archive/tar/reader.go index abd8f148a7..cd23fb57d6 100644 --- a/src/archive/tar/reader.go +++ b/src/archive/tar/reader.go @@ -463,6 +463,10 @@ func (tr *Reader) readHeader() *Header { hdr.Uid = int(tr.octal(s.next(8))) hdr.Gid = int(tr.octal(s.next(8))) hdr.Size = tr.octal(s.next(12)) + if hdr.Size < 0 { + tr.err = ErrHeader + return nil + } hdr.ModTime = time.Unix(tr.octal(s.next(12)), 0) s.next(8) // chksum hdr.Typeflag = s.next(1)[0] diff --git a/src/archive/tar/reader_test.go b/src/archive/tar/reader_test.go index 9601ffe459..ab1e8445a4 100644 --- a/src/archive/tar/reader_test.go +++ b/src/archive/tar/reader_test.go @@ -741,3 +741,19 @@ func TestUninitializedRead(t *testing.T) { } } + +// Negative header size should not cause panic. +// Issues 10959 and 10960. +func TestNegativeHdrSize(t *testing.T) { + f, err := os.Open("testdata/neg-size.tar") + if err != nil { + t.Fatal(err) + } + defer f.Close() + r := NewReader(f) + _, err = r.Next() + if err != ErrHeader { + t.Error("want ErrHeader, got", err) + } + io.Copy(ioutil.Discard, r) +} diff --git a/src/archive/tar/testdata/neg-size.tar b/src/archive/tar/testdata/neg-size.tar new file mode 100644 index 0000000000000000000000000000000000000000..5deea3d05c4da5a4ddda34ef7ad781088464e71b GIT binary patch literal 512 zcma)(!3}~i7=@d#07(~c0h9N)Na`Hy;GL8N4j!7YfmcUy4Hj^R-s|6LkswAdq{%Dq zeeYEkfGqY#(eVIvtUD=3dmgO>+WvmJu?!(Z2_k7dfq;C)3dnfX`l0#IeAe0qQ-^2= z|8jB*JI{8kO)-jdf^$wdJ_vEWkPIQXm^d{2NhUoLEEza^mVV&QNE^63LaKtd9rtDs zE>me;9Em{+eZ^Y>snQ&s!17bkhjcz=H=J(=e>|P(sS8Gxj+6N@Z7t^ E15rerKmY&$ literal 0 HcmV?d00001 From bcc1870fcfaf121ed69096c21a9d82f69a8e6a18 Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 25 May 2015 14:51:02 +1200 Subject: [PATCH 213/232] cmd/internal/ld: store the libraries a shared library was linked against in a note The motivation for this is the innocuous looking test case that is added. This creates a stack exe -> libdep2.so -> libdep.so -> libruntime.so. The problem comes from the fact that a function from libdep.so gets inlined all the way into exe. This (unsurprisingly) means that the object file for exe references symbols from libdep.so, which means that -ldep needs to be passed when linking exe and it isn't. The fix is simply to pass it -- there is no harm in passing it when it's not needed. The thing is, it's not clear at all in the current code to see how the linker can know that libdep2 is linked against libdep. It could look through the DT_NEEDED entries in libdep2 and try to guess which are Go libraries, but it feels better to be explicit. So this adds another SHT_NOTE section that lists the shared libraries a shared library was linked against, and makes sure the complete set of depended upon shared libraries is passed to the external linker. Change-Id: I79aa6f98b4db4721d657a7eb7b7f062269bf49e2 Reviewed-on: https://go-review.googlesource.com/10376 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- misc/cgo/testshared/shared_test.go | 35 +++++++++++++++ misc/cgo/testshared/src/dep2/dep2.go | 9 ++++ misc/cgo/testshared/src/exe2/exe2.go | 7 +++ src/cmd/link/internal/ld/elf.go | 11 +++++ src/cmd/link/internal/ld/go.go | 9 ++-- src/cmd/link/internal/ld/lib.go | 67 +++++++++++++++++++--------- src/cmd/link/internal/ld/link.go | 1 + 7 files changed, 112 insertions(+), 27 deletions(-) create mode 100644 misc/cgo/testshared/src/dep2/dep2.go create mode 100644 misc/cgo/testshared/src/exe2/exe2.go diff --git a/misc/cgo/testshared/shared_test.go b/misc/cgo/testshared/shared_test.go index 1d731af2a2..f7a99afce4 100644 --- a/misc/cgo/testshared/shared_test.go +++ b/misc/cgo/testshared/shared_test.go @@ -368,6 +368,21 @@ func testABIHashNote(t *testing.T, f *elf.File, note *note) { } } +// A Go shared library contains a note indicating which other Go shared libraries it +// was linked against in an unmapped section. +func testDepsNote(t *testing.T, f *elf.File, note *note) { + if note.section.Flags != 0 { + t.Errorf("package list section has flags %v", note.section.Flags) + } + if isOffsetLoaded(f, note.section.Offset) { + t.Errorf("package list section contained in PT_LOAD segment") + } + // libdep.so just links against the lib containing the runtime. + if note.desc != soname { + t.Errorf("incorrect dependency list %q", note.desc) + } +} + // The shared library contains notes with defined contents; see above. func TestNotes(t *testing.T) { goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep") @@ -382,6 +397,7 @@ func TestNotes(t *testing.T) { } pkgListNoteFound := false abiHashNoteFound := false + depsNoteFound := false for _, note := range notes { if note.name != "GO\x00\x00" { continue @@ -399,6 +415,12 @@ func TestNotes(t *testing.T) { } testABIHashNote(t, f, note) abiHashNoteFound = true + case 3: // ELF_NOTE_GODEPS_TAG + if depsNoteFound { + t.Error("multiple abi hash notes") + } + testDepsNote(t, f, note) + depsNoteFound = true } } if !pkgListNoteFound { @@ -407,6 +429,19 @@ func TestNotes(t *testing.T) { if !abiHashNoteFound { t.Error("abi hash note not found") } + if !depsNoteFound { + t.Error("deps note not found") + } +} + +// Build a GOPATH package (dep) into a shared library that links against the goroot +// runtime, another package (dep2) that links against the first, and and an +// executable that links against dep2. +func TestTwoGOPathShlibs(t *testing.T) { + goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep") + goCmd(t, "install", "-buildmode=shared", "-linkshared", "dep2") + goCmd(t, "install", "-linkshared", "exe2") + run(t, "executable linked to GOPATH library", "./bin/exe2") } // Testing rebuilding of shared libraries when they are stale is a bit more diff --git a/misc/cgo/testshared/src/dep2/dep2.go b/misc/cgo/testshared/src/dep2/dep2.go new file mode 100644 index 0000000000..af8ad5e756 --- /dev/null +++ b/misc/cgo/testshared/src/dep2/dep2.go @@ -0,0 +1,9 @@ +package dep2 + +import "dep" + +var W int = 1 + +func G() int { + return dep.F() + 1 +} diff --git a/misc/cgo/testshared/src/exe2/exe2.go b/misc/cgo/testshared/src/exe2/exe2.go new file mode 100644 index 0000000000..acdb4ddcc5 --- /dev/null +++ b/misc/cgo/testshared/src/exe2/exe2.go @@ -0,0 +1,7 @@ +package main + +import "dep2" + +func main() { + dep2.W = dep2.G() + 1 +} diff --git a/src/cmd/link/internal/ld/elf.go b/src/cmd/link/internal/ld/elf.go index 83f10d39f6..68d21f415c 100644 --- a/src/cmd/link/internal/ld/elf.go +++ b/src/cmd/link/internal/ld/elf.go @@ -9,7 +9,9 @@ import ( "crypto/sha1" "encoding/binary" "fmt" + "path/filepath" "sort" + "strings" ) /* @@ -1205,6 +1207,7 @@ func elfwritebuildinfo() int { const ( ELF_NOTE_GOPKGLIST_TAG = 1 ELF_NOTE_GOABIHASH_TAG = 2 + ELF_NOTE_GODEPS_TAG = 3 ) var ELF_NOTE_GO_NAME = []byte("GO\x00\x00") @@ -1697,6 +1700,7 @@ func doelf() { if Buildmode == BuildmodeShared { Addstring(shstrtab, ".note.go.abihash") Addstring(shstrtab, ".note.go.pkg-list") + Addstring(shstrtab, ".note.go.deps") } } @@ -1904,6 +1908,11 @@ func doelf() { } addgonote(".note.go.abihash", ELF_NOTE_GOABIHASH_TAG, h.Sum([]byte{})) addgonote(".note.go.pkg-list", ELF_NOTE_GOPKGLIST_TAG, []byte(pkglistfornote)) + var deplist []string + for _, shlib := range Ctxt.Shlibs { + deplist = append(deplist, filepath.Base(shlib.Path)) + } + addgonote(".note.go.deps", ELF_NOTE_GODEPS_TAG, []byte(strings.Join(deplist, "\n"))) } } @@ -1976,6 +1985,8 @@ func Asmbelf(symo int64) { sh = elfshname(".note.go.abihash") sh.type_ = SHT_NOTE sh.flags = SHF_ALLOC + sh = elfshname(".note.go.deps") + sh.type_ = SHT_NOTE } goto elfobj } diff --git a/src/cmd/link/internal/ld/go.go b/src/cmd/link/internal/ld/go.go index 875b8d2e17..80a6c6ed7d 100644 --- a/src/cmd/link/internal/ld/go.go +++ b/src/cmd/link/internal/ld/go.go @@ -653,15 +653,14 @@ func deadcode() { } if Buildmode == BuildmodeShared { - // Mark all symbols as reachable when building a - // shared library. + // Mark all symbols defined in this library as reachable when + // building a shared library. for s := Ctxt.Allsym; s != nil; s = s.Allsym { - if s.Type != 0 { + if s.Type != 0 && s.Type != obj.SDYNIMPORT { mark(s) } } - mark(Linkrlookup(Ctxt, "main.main", 0)) - mark(Linkrlookup(Ctxt, "main.init", 0)) + markflood() } else { mark(Linklookup(Ctxt, INITENTRY, 0)) if Linkshared && Buildmode == BuildmodeExe { diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 26d722911b..8caac0f89c 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -983,15 +983,35 @@ func hostlink() { argv = append(argv, fmt.Sprintf("%s/go.o", tmpdir)) if Linkshared { - for _, shlib := range Ctxt.Shlibs { - dir, base := filepath.Split(shlib.Path) - argv = append(argv, "-L"+dir) - if !rpath.set { - argv = append(argv, "-Wl,-rpath="+dir) + seenDirs := make(map[string]bool) + seenLibs := make(map[string]bool) + addshlib := func(path string) { + dir, base := filepath.Split(path) + if !seenDirs[dir] { + argv = append(argv, "-L"+dir) + if !rpath.set { + argv = append(argv, "-Wl,-rpath="+dir) + } + seenDirs[dir] = true } base = strings.TrimSuffix(base, ".so") base = strings.TrimPrefix(base, "lib") - argv = append(argv, "-l"+base) + if !seenLibs[base] { + argv = append(argv, "-l"+base) + seenLibs[base] = true + } + } + for _, shlib := range Ctxt.Shlibs { + addshlib(shlib.Path) + for _, dep := range shlib.Deps { + if dep == "" { + continue + } + libpath := findshlib(dep) + if libpath != "" { + addshlib(libpath) + } + } } } @@ -1214,18 +1234,20 @@ func readnote(f *elf.File, name []byte, typ int32) ([]byte, error) { return nil, nil } -func ldshlibsyms(shlib string) { - found := false - libpath := "" +func findshlib(shlib string) string { for _, libdir := range Ctxt.Libdir { - libpath = filepath.Join(libdir, shlib) + libpath := filepath.Join(libdir, shlib) if _, err := os.Stat(libpath); err == nil { - found = true - break + return libpath } } - if !found { - Diag("cannot find shared library: %s", shlib) + Diag("cannot find shared library: %s", shlib) + return "" +} + +func ldshlibsyms(shlib string) { + libpath := findshlib(shlib) + if libpath == "" { return } for _, processedlib := range Ctxt.Shlibs { @@ -1251,6 +1273,13 @@ func ldshlibsyms(shlib string) { return } + depsbytes, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GODEPS_TAG) + if err != nil { + Diag("cannot read dep list from shared library %s: %v", libpath, err) + return + } + deps := strings.Split(string(depsbytes), "\n") + syms, err := f.Symbols() if err != nil { Diag("cannot read symbols from shared library: %s", libpath) @@ -1272,12 +1301,6 @@ func ldshlibsyms(shlib string) { if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION { continue } - if s.Section == elf.SHN_UNDEF { - continue - } - if strings.HasPrefix(s.Name, "_") { - continue - } if strings.HasPrefix(s.Name, "runtime.gcbits.") { gcmasks[s.Value] = readelfsymboldata(f, &s) } @@ -1285,7 +1308,7 @@ func ldshlibsyms(shlib string) { continue } lsym := Linklookup(Ctxt, s.Name, 0) - if lsym.Type != 0 && lsym.Dupok == 0 { + if lsym.Type != 0 && lsym.Type != obj.SDYNIMPORT && lsym.Dupok == 0 { Diag( "Found duplicate symbol %s reading from %s, first found in %s", s.Name, shlib, lsym.File) @@ -1342,7 +1365,7 @@ func ldshlibsyms(shlib string) { Ctxt.Etextp = last } - Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash}) + Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps}) } func mywhatsys() { diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index 4b034a4e81..a288148a5a 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -117,6 +117,7 @@ type Auto struct { type Shlib struct { Path string Hash []byte + Deps []string } type Link struct { From c949cff6a755aacd7407a803d304f1aba057bf5c Mon Sep 17 00:00:00 2001 From: Michael Hudson-Doyle Date: Mon, 25 May 2015 16:13:50 +1200 Subject: [PATCH 214/232] cmd/internal/ld: do not depend on local symbols to read a type's gcdata We already read the address of a gcmask/gcprog out of the type data, but I didn't know how many bytes to read. But it turns out that it's easy to calculate, so change to do that. This means that we no longer depend on the local symbols being present, allowing me to strip the shared libraries for distribution and make them a lot smaller. As a bonus, this makes LSym another 24 bytes smaller, down to 296 bytes now. Change-Id: I379d359e28d63afae6753efd23efdf1fbb716992 Reviewed-on: https://go-review.googlesource.com/10377 Reviewed-by: Ian Lance Taylor Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot --- misc/cgo/testshared/src/dep/dep.go | 6 +++ misc/cgo/testshared/src/dep2/dep2.go | 2 + src/cmd/link/internal/ld/data.go | 2 +- src/cmd/link/internal/ld/decodesym.go | 56 +++++++++++++++++++++------ src/cmd/link/internal/ld/lib.go | 49 +++++------------------ src/cmd/link/internal/ld/link.go | 2 +- 6 files changed, 64 insertions(+), 53 deletions(-) diff --git a/misc/cgo/testshared/src/dep/dep.go b/misc/cgo/testshared/src/dep/dep.go index fb112cdb82..d3bed3f8ff 100644 --- a/misc/cgo/testshared/src/dep/dep.go +++ b/misc/cgo/testshared/src/dep/dep.go @@ -2,6 +2,12 @@ package dep var V int = 1 +var HasMask []string = []string{"hi"} + +type HasProg struct { + array [1024]*byte +} + func F() int { return V } diff --git a/misc/cgo/testshared/src/dep2/dep2.go b/misc/cgo/testshared/src/dep2/dep2.go index af8ad5e756..bac1086a4a 100644 --- a/misc/cgo/testshared/src/dep2/dep2.go +++ b/misc/cgo/testshared/src/dep2/dep2.go @@ -4,6 +4,8 @@ import "dep" var W int = 1 +var hasProg dep.HasProg + func G() int { return dep.F() + 1 } diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index e8d30f6a89..fd1cdd64bb 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1104,7 +1104,7 @@ func (p *GCProg) AddSym(s *LSym) { // Copy program. prog := decodetype_gcprog(typ) p.w.ZeroUntil(s.Value / ptrsize) - p.w.Append(prog.P[4:prog.Size], nptr) + p.w.Append(prog[4:], nptr) } func growdatsize(datsizep *int64, s *LSym) { diff --git a/src/cmd/link/internal/ld/decodesym.go b/src/cmd/link/internal/ld/decodesym.go index fcc664dde7..c1cf4d7181 100644 --- a/src/cmd/link/internal/ld/decodesym.go +++ b/src/cmd/link/internal/ld/decodesym.go @@ -4,7 +4,10 @@ package ld -import "cmd/internal/obj" +import ( + "cmd/internal/obj" + "debug/elf" +) // Decoding the type.* symbols. This has to be in sync with // ../../runtime/type.go, or more specifically, with what @@ -72,14 +75,38 @@ func decodetype_ptrdata(s *LSym) int64 { return int64(decode_inuxi(s.P[Thearch.Ptrsize:], Thearch.Ptrsize)) // 0x8 / 0x10 } -// Type.commonType.gc -func decodetype_gcprog(s *LSym) *LSym { - if s.Type == obj.SDYNIMPORT { - // The gcprog for "type.$name" is calle "type..gcprog.$name". - x := "type..gcprog." + s.Name[5:] - return Linklookup(Ctxt, x, 0) +// Find the elf.Section of a given shared library that contains a given address. +func findShlibSection(path string, addr uint64) *elf.Section { + for _, shlib := range Ctxt.Shlibs { + if shlib.Path == path { + for _, sect := range shlib.File.Sections { + if sect.Addr <= addr && addr <= sect.Addr+sect.Size { + return sect + } + } + } } - return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) + return nil +} + +// Type.commonType.gc +func decodetype_gcprog(s *LSym) []byte { + if s.Type == obj.SDYNIMPORT { + addr := decodetype_gcprog_shlib(s) + sect := findShlibSection(s.File, addr) + if sect != nil { + // A gcprog is a 4-byte uint32 indicating length, followed by + // the actual program. + progsize := make([]byte, 4) + sect.ReadAt(progsize, int64(addr-sect.Addr)) + progbytes := make([]byte, Ctxt.Arch.ByteOrder.Uint32(progsize)) + sect.ReadAt(progbytes, int64(addr-sect.Addr+4)) + return append(progsize, progbytes...) + } + Exitf("cannot find gcprog for %s", s.Name) + return nil + } + return decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)).P } func decodetype_gcprog_shlib(s *LSym) uint64 { @@ -88,9 +115,16 @@ func decodetype_gcprog_shlib(s *LSym) uint64 { func decodetype_gcmask(s *LSym) []byte { if s.Type == obj.SDYNIMPORT { - // ldshlibsyms makes special efforts to read the value - // of gcmask for types defined in that shared library. - return s.gcmask + addr := decodetype_gcprog_shlib(s) + ptrdata := decodetype_ptrdata(s) + sect := findShlibSection(s.File, addr) + if sect != nil { + r := make([]byte, ptrdata/int64(Thearch.Ptrsize)) + sect.ReadAt(r, int64(addr-sect.Addr)) + return r + } + Exitf("cannot find gcmask for %s", s.Name) + return nil } mask := decode_reloc_sym(s, 2*int32(Thearch.Ptrsize)+8+1*int32(Thearch.Ptrsize)) return mask.P diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 8caac0f89c..32ee45bcec 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1178,7 +1178,7 @@ func readelfsymboldata(f *elf.File, sym *elf.Symbol) []byte { if sect.Type != elf.SHT_PROGBITS && sect.Type != elf.SHT_NOTE { Diag("reading %s from non-data section", sym.Name) } - n, err := sect.ReadAt(data, int64(sym.Value-sect.Offset)) + n, err := sect.ReadAt(data, int64(sym.Value-sect.Addr)) if uint64(n) != sym.Size { Diag("reading contents of %s: %v", sym.Name, err) } @@ -1265,7 +1265,6 @@ func ldshlibsyms(shlib string) { Diag("cannot open shared library: %s", libpath) return } - defer f.Close() hash, err := readnote(f, ELF_NOTE_GO_NAME, ELF_NOTE_GOABIHASH_TAG) if err != nil { @@ -1280,33 +1279,15 @@ func ldshlibsyms(shlib string) { } deps := strings.Split(string(depsbytes), "\n") - syms, err := f.Symbols() + syms, err := f.DynamicSymbols() if err != nil { Diag("cannot read symbols from shared library: %s", libpath) return } - // If a package has a global variable of a type defined in another shared - // library, we need to know the gcmask used by the type, if any. To support - // this, we read all the runtime.gcbits.* symbols, keep a map of address to - // gcmask, and after we're read all the symbols, read the addresses of the - // gcmasks symbols out of the type data to look up the gcmask for each type. - // This depends on the fact that the runtime.gcbits.* symbols are local (so - // the address is actually present in the type data and we don't have to - // search all relocations to find the ones which correspond to gcmasks) and - // also that the shared library we are linking against has not had the symbol - // table removed. - gcmasks := make(map[uint64][]byte) - types := []*LSym{} for _, s := range syms { if elf.ST_TYPE(s.Info) == elf.STT_NOTYPE || elf.ST_TYPE(s.Info) == elf.STT_SECTION { continue } - if strings.HasPrefix(s.Name, "runtime.gcbits.") { - gcmasks[s.Value] = readelfsymboldata(f, &s) - } - if elf.ST_BIND(s.Info) != elf.STB_GLOBAL { - continue - } lsym := Linklookup(Ctxt, s.Name, 0) if lsym.Type != 0 && lsym.Type != obj.SDYNIMPORT && lsym.Dupok == 0 { Diag( @@ -1315,29 +1296,17 @@ func ldshlibsyms(shlib string) { } lsym.Type = obj.SDYNIMPORT lsym.ElfType = elf.ST_TYPE(s.Info) - lsym.File = libpath - if strings.HasPrefix(lsym.Name, "type.") { - if f.Sections[s.Section].Type == elf.SHT_PROGBITS { + if s.Section != elf.SHN_UNDEF { + // Set .File for the library that actually defines the symbol. + lsym.File = libpath + // The decodetype_* functions in decodetype.go need access to + // the type data. + if strings.HasPrefix(lsym.Name, "type.") && !strings.HasPrefix(lsym.Name, "type..") { lsym.P = readelfsymboldata(f, &s) } - if !strings.HasPrefix(lsym.Name, "type..") { - types = append(types, lsym) - } } } - for _, t := range types { - if decodetype_noptr(t) != 0 || decodetype_usegcprog(t) != 0 { - continue - } - addr := decodetype_gcprog_shlib(t) - tgcmask, ok := gcmasks[addr] - if !ok { - Diag("bits not found for %s at %d", t.Name, addr) - } - t.gcmask = tgcmask - } - // We might have overwritten some functions above (this tends to happen for the // autogenerated type equality/hashing functions) and we don't want to generated // pcln table entries for these any more so unstitch them from the Textp linked @@ -1365,7 +1334,7 @@ func ldshlibsyms(shlib string) { Ctxt.Etextp = last } - Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps}) + Ctxt.Shlibs = append(Ctxt.Shlibs, Shlib{Path: libpath, Hash: hash, Deps: deps, File: f}) } func mywhatsys() { diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index a288148a5a..33b17c5985 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -84,7 +84,6 @@ type LSym struct { P []byte R []Reloc Local bool - gcmask []byte } func (s *LSym) String() string { @@ -118,6 +117,7 @@ type Shlib struct { Path string Hash []byte Deps []string + File *elf.File } type Link struct { From 21500012c18adb985876507c0495c4d7990ff642 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 27 May 2015 09:56:28 -0700 Subject: [PATCH 215/232] doc: update go1.5.txt Change-Id: I48b5f10d703dba48ec8e67c58d4276befafb5524 Reviewed-on: https://go-review.googlesource.com/10420 Reviewed-by: Brad Fitzpatrick --- doc/go1.5.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/go1.5.txt b/doc/go1.5.txt index 171c1601f0..f2ceb1d56b 100644 --- a/doc/go1.5.txt +++ b/doc/go1.5.txt @@ -58,9 +58,11 @@ mime/quotedprintable: new package (https://golang.org/cl/5940 + others) net: add Source field to OpError (https://go-review.googlesource.com/9231) net: fix inconsistent errors (https://golang.org/cl/9236) net: add SocketConn, SocketPacketConn (https://golang.org/cl/9275) +net: use Go's DNS resolver when system configuration permits (https://golang.org/cl/8945) net/http: support for setting trailers from a server Handler (https://golang.org/cl/2157) net/http: ignore the Unix epoch time in ServeContent (https://golang.org/cl/7915) net/http/cgi: fix REMOTE_ADDR, REMOTE_HOST, add REMOTE_PORT (https://golang.org/cl/4933) +net/mail: adds AddressParser type (https://golang.org/cl/10392) net/smtp: add TLSConnectionState accessor (https://golang.org/cl/2151) os: add LookupEnv (https://golang.org/cl/9791) os/signal: add Ignore and Reset (https://golang.org/cl/3580) From 05d8f1d1664e6413756ed4e18392310b75501ff4 Mon Sep 17 00:00:00 2001 From: David Chase Date: Fri, 22 May 2015 22:01:01 -0400 Subject: [PATCH 216/232] cmd/compile: propagate correct line numbers in treecopy Added a lineno parameter to treecopy and listtreecopy (ignored if = 0). When nodes are copied the copy is assigned the non-zero lineno (normally this would be the destination). Fixes #8183 Change-Id: Iffb767a745093fb89aa08bf8a7692c2f0122be98 Reviewed-on: https://go-review.googlesource.com/10334 Reviewed-by: Russ Cox --- src/cmd/compile/internal/gc/dcl.go | 5 +++-- src/cmd/compile/internal/gc/order.go | 2 +- src/cmd/compile/internal/gc/racewalk.go | 2 +- src/cmd/compile/internal/gc/subr.go | 23 +++++++++++++++++------ test/fixedbugs/issue8183.go | 23 +++++++++++++++++++++++ 5 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 test/fixedbugs/issue8183.go diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 85a33bec3f..346b3beaeb 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -313,18 +313,19 @@ func variter(vl *NodeList, t *Node, el *NodeList) *NodeList { * new_name_list [[type] = expr_list] */ func constiter(vl *NodeList, t *Node, cl *NodeList) *NodeList { + lno := int32(0) // default is to leave line number alone in listtreecopy if cl == nil { if t != nil { Yyerror("const declaration cannot have type without expression") } cl = lastconst t = lasttype + lno = vl.N.Lineno } else { lastconst = cl lasttype = t } - - cl = listtreecopy(cl) + cl = listtreecopy(cl, lno) var v *Node var c *Node diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index b3fd282a68..8b99ed0895 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -498,7 +498,7 @@ func orderstmt(n *Node, order *Order) { orderexpr(&n.Left, order, nil) n.Left = ordersafeexpr(n.Left, order) - tmp1 := treecopy(n.Left) + tmp1 := treecopy(n.Left, 0) if tmp1.Op == OINDEXMAP { tmp1.Etype = 0 // now an rvalue not an lvalue } diff --git a/src/cmd/compile/internal/gc/racewalk.go b/src/cmd/compile/internal/gc/racewalk.go index 6e384682fd..05a902e8c1 100644 --- a/src/cmd/compile/internal/gc/racewalk.go +++ b/src/cmd/compile/internal/gc/racewalk.go @@ -483,7 +483,7 @@ func callinstr(np **Node, init **NodeList, wr int, skip int) bool { *np = n } - n = treecopy(n) + n = treecopy(n, 0) makeaddable(n) var f *Node if t.Etype == TSTRUCT || Isfixedarray(t) { diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index b10a6b3d3d..08fafa819f 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -736,7 +736,12 @@ func aindex(b *Node, t *Type) *Type { return r } -func treecopy(n *Node) *Node { +// treecopy recursively copies n, with the exception of +// ONAME, OLITERAL, OTYPE, and non-iota ONONAME leaves. +// Copies of iota ONONAME nodes are assigned the current +// value of iota_. If lineno != 0, it sets the line number +// of newly allocated nodes to lineno. +func treecopy(n *Node, lineno int32) *Node { if n == nil { return nil } @@ -747,9 +752,12 @@ func treecopy(n *Node) *Node { m = Nod(OXXX, nil, nil) *m = *n m.Orig = m - m.Left = treecopy(n.Left) - m.Right = treecopy(n.Right) - m.List = listtreecopy(n.List) + m.Left = treecopy(n.Left, lineno) + m.Right = treecopy(n.Right, lineno) + m.List = listtreecopy(n.List, lineno) + if lineno != -1 { + m.Lineno = lineno + } if m.Defn != nil { panic("abort") } @@ -764,6 +772,9 @@ func treecopy(n *Node) *Node { *m = *n m.Iota = iota_ + if lineno != 0 { + m.Lineno = lineno + } break } fallthrough @@ -3092,10 +3103,10 @@ func Simsimtype(t *Type) int { return et } -func listtreecopy(l *NodeList) *NodeList { +func listtreecopy(l *NodeList, lineno int32) *NodeList { var out *NodeList for ; l != nil; l = l.Next { - out = list(out, treecopy(l.N)) + out = list(out, treecopy(l.N, lineno)) } return out } diff --git a/test/fixedbugs/issue8183.go b/test/fixedbugs/issue8183.go new file mode 100644 index 0000000000..7104f1e00c --- /dev/null +++ b/test/fixedbugs/issue8183.go @@ -0,0 +1,23 @@ +// errorcheck + +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Tests correct reporting of line numbers for errors involving iota, +// Issue #8183. +package foo + +const ( + ok = byte(iota + 253) + bad + barn + bard // ERROR "constant 256 overflows byte" +) + +const ( + c = len([1 - iota]int{}) + d + e // ERROR "array bound must be non-negative" "const initializer len\(composite literal\) is not a constant" + f // ERROR "array bound must be non-negative" "const initializer len\(composite literal\) is not a constant" +) From 553f45a61e062abe1c3459adc288929fc8693746 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 27 May 2015 10:49:25 -0700 Subject: [PATCH 217/232] archive/zip: sanity check the TOC's declared number of files Fixes #10956 Change-Id: If8517094f04250c4f722e1e899a237eb6e170eb9 Reviewed-on: https://go-review.googlesource.com/10421 Run-TryBot: Brad Fitzpatrick Reviewed-by: Andrew Gerrand --- src/archive/zip/reader.go | 4 ++++ src/archive/zip/reader_test.go | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/archive/zip/reader.go b/src/archive/zip/reader.go index 10d9d5e5bf..f68ab09723 100644 --- a/src/archive/zip/reader.go +++ b/src/archive/zip/reader.go @@ -8,6 +8,7 @@ import ( "bufio" "encoding/binary" "errors" + "fmt" "hash" "hash/crc32" "io" @@ -77,6 +78,9 @@ func (z *Reader) init(r io.ReaderAt, size int64) error { if err != nil { return err } + if end.directoryRecords > uint64(size)/fileHeaderLen { + return fmt.Errorf("archive/zip: TOC declares impossible %d files in %d byte zip", end.directoryRecords, size) + } z.r = r z.File = make([]*File, 0, end.directoryRecords) z.Comment = end.comment diff --git a/src/archive/zip/reader_test.go b/src/archive/zip/reader_test.go index 6a8cab34cd..4806b89458 100644 --- a/src/archive/zip/reader_test.go +++ b/src/archive/zip/reader_test.go @@ -551,10 +551,7 @@ func TestIssue10957(t *testing.T) { "\v\x00\x00\x00\x00\x00") z, err := NewReader(bytes.NewReader(data), int64(len(data))) if err != nil { - if z != nil { - panic("non nil z") - } - return + t.Fatal(err) } for i, f := range z.File { r, err := f.Open() @@ -573,3 +570,15 @@ func TestIssue10957(t *testing.T) { r.Close() } } + +// Verify the number of files is sane. +func TestIssue10956(t *testing.T) { + data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" + + "0000PK\x05\x06000000000000" + + "0000\v\x00000\x00\x00\x00\x00\x00\x00\x000") + _, err := NewReader(bytes.NewReader(data), int64(len(data))) + const want = "TOC declares impossible 3472328296227680304 files in 57 byte" + if err == nil && !strings.Contains(err.Error(), want) { + t.Errorf("error = %v; want %q", err, want) + } +} From acd82d5017a6d30781d7b8caa6033694353fa7a8 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 27 May 2015 12:08:38 -0700 Subject: [PATCH 218/232] strconv: minor internal comment fix Change-Id: I590ac9e976d4044d1f4f280137ea9b38851a9fc2 Reviewed-on: https://go-review.googlesource.com/10424 Reviewed-by: Alan Donovan --- src/strconv/ftoa.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/strconv/ftoa.go b/src/strconv/ftoa.go index d59c78e493..468c37fafb 100644 --- a/src/strconv/ftoa.go +++ b/src/strconv/ftoa.go @@ -223,9 +223,8 @@ func formatDigits(dst []byte, shortest bool, neg bool, digs decimalSlice, prec i return append(dst, '%', fmt) } -// Round d (= mant * 2^exp) to the shortest number of digits -// that will let the original floating point value be precisely -// reconstructed. Size is original floating point size (64 or 32). +// roundShortest rounds d (= mant * 2^exp) to the shortest number of digits +// that will let the original floating point value be precisely reconstructed. func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) { // If mantissa is zero, the number is zero; stop now. if mant == 0 { From 0858d8847d0cdcc03ecb336c208706516bc0ab40 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 26 May 2015 14:57:05 -0700 Subject: [PATCH 219/232] math/big: removed TODO, cleanups - factor out handling of sign - rename bstring, pstring to fmtB, fmtP consistent with fmtE, fmtF - move all float-to-string conversion functions into ftoa.go - no functional changes Change-Id: I5970ecb874dc9c387630b59147d90bda16a5d8e6 Reviewed-on: https://go-review.googlesource.com/10387 Reviewed-by: Alan Donovan --- src/math/big/floatconv.go | 136 +----------------------- src/math/big/floatconv_test.go | 1 + src/math/big/ftoa.go | 185 ++++++++++++++++++++++++++------- 3 files changed, 152 insertions(+), 170 deletions(-) diff --git a/src/math/big/floatconv.go b/src/math/big/floatconv.go index 5ab75e9031..dc62b450db 100644 --- a/src/math/big/floatconv.go +++ b/src/math/big/floatconv.go @@ -2,14 +2,13 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements float-to-string conversion functions. +// This file implements string-to-Float conversion functions. package big import ( "fmt" "io" - "strconv" "strings" ) @@ -244,136 +243,3 @@ func ScanFloat(r io.ByteScanner, base int, prec uint, mode RoundingMode) (f *Flo func ParseFloat(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error) { return new(Float).SetPrec(prec).SetMode(mode).Parse(s, base) } - -// Format converts the floating-point number x to a string according -// to the given format and precision prec. The format is one of: -// -// 'e' -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits -// 'E' -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits -// 'f' -ddddd.dddd, no exponent -// 'g' like 'e' for large exponents, like 'f' otherwise -// 'G' like 'E' for large exponents, like 'f' otherwise -// 'b' -ddddddp±dd, binary exponent -// 'p' -0x.dddp±dd, binary exponent, hexadecimal mantissa -// -// For the binary exponent formats, the mantissa is printed in normalized form: -// -// 'b' decimal integer mantissa using x.Prec() bits, or -0 -// 'p' hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0 -// -// The precision prec controls the number of digits (excluding the exponent) -// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f' -// it is the number of digits after the decimal point. For 'g' and 'G' it is -// the total number of digits. A negative precision selects the smallest -// number of digits necessary such that ParseFloat will return f exactly. -// The prec value is ignored for the 'b' or 'p' format. -// -// BUG(gri) Float.Format does not accept negative precisions. -// BUG(gri) The Float.Format signature conflicts with Format(f fmt.State, c rune). -// (https://github.com/golang/go/issues/10938) -func (x *Float) Format(format byte, prec int) string { - const extra = 10 // TODO(gri) determine a good/better value here - return string(x.Append(make([]byte, 0, prec+extra), format, prec)) -} - -// Append appends the string form of the floating-point number x, -// as generated by x.Format, to buf and returns the extended buffer. -func (x *Float) Append(buf []byte, format byte, prec int) []byte { - // TODO(gri) factor out handling of sign? - - // Inf - if x.IsInf() { - var ch byte = '+' - if x.neg { - ch = '-' - } - buf = append(buf, ch) - return append(buf, "Inf"...) - } - - // easy formats - switch format { - case 'b': - return x.bstring(buf) - case 'p': - return x.pstring(buf) - } - - return x.bigFtoa(buf, format, prec) -} - -// BUG(gri): Float.String uses x.Format('g', 10) rather than x.Format('g', -1). -func (x *Float) String() string { - return x.Format('g', 10) -} - -// bstring appends the string of x in the format ["-"] mantissa "p" exponent -// with a decimal mantissa and a binary exponent, or ["-"] "0" if x is zero, -// and returns the extended buffer. -// The mantissa is normalized such that is uses x.Prec() bits in binary -// representation. -func (x *Float) bstring(buf []byte) []byte { - if x.neg { - buf = append(buf, '-') - } - if x.form == zero { - return append(buf, '0') - } - - if debugFloat && x.form != finite { - panic("non-finite float") - } - // x != 0 - - // adjust mantissa to use exactly x.prec bits - m := x.mant - switch w := uint32(len(x.mant)) * _W; { - case w < x.prec: - m = nat(nil).shl(m, uint(x.prec-w)) - case w > x.prec: - m = nat(nil).shr(m, uint(w-x.prec)) - } - - buf = append(buf, m.decimalString()...) - buf = append(buf, 'p') - e := int64(x.exp) - int64(x.prec) - if e >= 0 { - buf = append(buf, '+') - } - return strconv.AppendInt(buf, e, 10) -} - -// pstring appends the string of x in the format ["-"] "0x." mantissa "p" exponent -// with a hexadecimal mantissa and a binary exponent, or ["-"] "0" if x is zero, -// ad returns the extended buffer. -// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0. -func (x *Float) pstring(buf []byte) []byte { - if x.neg { - buf = append(buf, '-') - } - if x.form == zero { - return append(buf, '0') - } - - if debugFloat && x.form != finite { - panic("non-finite float") - } - // x != 0 - - // remove trailing 0 words early - // (no need to convert to hex 0's and trim later) - m := x.mant - i := 0 - for i < len(m) && m[i] == 0 { - i++ - } - m = m[i:] - - buf = append(buf, "0x."...) - buf = append(buf, strings.TrimRight(x.mant.hexString(), "0")...) - buf = append(buf, 'p') - if x.exp >= 0 { - buf = append(buf, '+') - } - return strconv.AppendInt(buf, int64(x.exp), 10) -} diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go index 6ba15754e5..db300314f1 100644 --- a/src/math/big/floatconv_test.go +++ b/src/math/big/floatconv_test.go @@ -366,6 +366,7 @@ func TestFloatFormat(t *testing.T) { // unsupported format {"3.14", 64, 'x', 0, "%x"}, + {"-3.14", 64, 'x', 0, "%x"}, } { f, _, err := ParseFloat(test.x, 0, test.prec, ToNearestEven) if err != nil { diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go index 0a9edfd7b2..0f943e1ff2 100644 --- a/src/math/big/ftoa.go +++ b/src/math/big/ftoa.go @@ -2,34 +2,89 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -// This file implements the 'e', 'f', 'g' floating-point formats. -// It is closely following the corresponding implementation in -// strconv/ftoa.go, but modified and simplified for big.Float. - -// Algorithm: -// 1) convert Float to multiprecision decimal -// 2) round to desired precision -// 3) read digits out and format +// This file implements Float-to-string conversion functions. +// It is closely following the corresponding implementation +// in strconv/ftoa.go, but modified and simplified for Float. package big -import "strconv" +import ( + "strconv" + "strings" +) -// TODO(gri) Consider moving sign into decimal - could make the signatures below cleaner. +// Format converts the floating-point number x to a string according +// to the given format and precision prec. The format is one of: +// +// 'e' -d.dddde±dd, decimal exponent, at least two (possibly 0) exponent digits +// 'E' -d.ddddE±dd, decimal exponent, at least two (possibly 0) exponent digits +// 'f' -ddddd.dddd, no exponent +// 'g' like 'e' for large exponents, like 'f' otherwise +// 'G' like 'E' for large exponents, like 'f' otherwise +// 'b' -ddddddp±dd, binary exponent +// 'p' -0x.dddp±dd, binary exponent, hexadecimal mantissa +// +// For the binary exponent formats, the mantissa is printed in normalized form: +// +// 'b' decimal integer mantissa using x.Prec() bits, or -0 +// 'p' hexadecimal fraction with 0.5 <= 0.mantissa < 1.0, or -0 +// +// The precision prec controls the number of digits (excluding the exponent) +// printed by the 'e', 'E', 'f', 'g', and 'G' formats. For 'e', 'E', and 'f' +// it is the number of digits after the decimal point. For 'g' and 'G' it is +// the total number of digits. A negative precision selects the smallest +// number of digits necessary such that ParseFloat will return f exactly. +// The prec value is ignored for the 'b' or 'p' format. +// +// BUG(gri) Float.Format does not accept negative precisions. +// BUG(gri) The Float.Format signature conflicts with Format(f fmt.State, c rune). +// (https://github.com/golang/go/issues/10938) +func (x *Float) Format(format byte, prec int) string { + const extra = 10 // TODO(gri) determine a good/better value here + return string(x.Append(make([]byte, 0, prec+extra), format, prec)) +} -// bigFtoa formats a float for the %e, %E, %f, %g, and %G formats. -func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte { - if debugFloat && f.IsInf() { - panic("non-finite float") +// String formats x like x.Format('g', 10). +func (x *Float) String() string { + return x.Format('g', 10) +} + +// Append appends to buf the string form of the floating-point number x, +// as generated by x.Format, and returns the extended buffer. +func (x *Float) Append(buf []byte, fmt byte, prec int) []byte { + // sign + if x.neg { + buf = append(buf, '-') } + // Inf + if x.IsInf() { + if !x.neg { + buf = append(buf, '+') + } + return append(buf, "Inf"...) + } + + // pick off easy formats + switch fmt { + case 'b': + return x.fmtB(buf) + case 'p': + return x.fmtP(buf) + } + + // Algorithm: + // 1) convert Float to multiprecision decimal + // 2) round to desired precision + // 3) read digits out and format + // 1) convert Float to multiprecision decimal var mant nat - if f.form == finite { - mant = f.mant + if x.form == finite { + mant = x.mant } var d decimal - d.init(mant, int(f.exp)-f.mant.bitLen()) + d.init(mant, int(x.exp)-x.mant.bitLen()) // 2) round to desired precision shortest := false @@ -67,9 +122,9 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte { // 3) read digits out and format switch fmt { case 'e', 'E': - return fmtE(buf, fmt, prec, f.neg, d) + return fmtE(buf, fmt, prec, d) case 'f': - return fmtF(buf, prec, f.neg, d) + return fmtF(buf, prec, d) case 'g', 'G': // trim trailing fractional zeros in %e format eprec := prec @@ -88,25 +143,23 @@ func (f *Float) bigFtoa(buf []byte, fmt byte, prec int) []byte { if prec > len(d.mant) { prec = len(d.mant) } - return fmtE(buf, fmt+'e'-'g', prec-1, f.neg, d) + return fmtE(buf, fmt+'e'-'g', prec-1, d) } if prec > d.exp { prec = len(d.mant) } - return fmtF(buf, max(prec-d.exp, 0), f.neg, d) + return fmtF(buf, max(prec-d.exp, 0), d) } // unknown format + if x.neg { + buf = buf[:len(buf)-1] // sign was added prematurely - remove it again + } return append(buf, '%', fmt) } -// %e: -d.ddddde±dd -func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte { - // sign - if neg { - buf = append(buf, '-') - } - +// %e: d.ddddde±dd +func fmtE(buf []byte, fmt byte, prec int, d decimal) []byte { // first digit ch := byte('0') if len(d.mant) > 0 { @@ -149,13 +202,8 @@ func fmtE(buf []byte, fmt byte, prec int, neg bool, d decimal) []byte { return strconv.AppendInt(buf, exp, 10) } -// %f: -ddddddd.ddddd -func fmtF(buf []byte, prec int, neg bool, d decimal) []byte { - // sign - if neg { - buf = append(buf, '-') - } - +// %f: ddddddd.ddddd +func fmtF(buf []byte, prec int, d decimal) []byte { // integer, padded with zeros as needed if d.exp > 0 { m := min(len(d.mant), d.exp) @@ -182,6 +230,73 @@ func fmtF(buf []byte, prec int, neg bool, d decimal) []byte { return buf } +// fmtB appends the string of x in the format mantissa "p" exponent +// with a decimal mantissa and a binary exponent, or 0" if x is zero, +// and returns the extended buffer. +// The mantissa is normalized such that is uses x.Prec() bits in binary +// representation. +// The sign of x is ignored, and x must not be an Inf. +func (x *Float) fmtB(buf []byte) []byte { + if x.form == zero { + return append(buf, '0') + } + + if debugFloat && x.form != finite { + panic("non-finite float") + } + // x != 0 + + // adjust mantissa to use exactly x.prec bits + m := x.mant + switch w := uint32(len(x.mant)) * _W; { + case w < x.prec: + m = nat(nil).shl(m, uint(x.prec-w)) + case w > x.prec: + m = nat(nil).shr(m, uint(w-x.prec)) + } + + buf = append(buf, m.decimalString()...) + buf = append(buf, 'p') + e := int64(x.exp) - int64(x.prec) + if e >= 0 { + buf = append(buf, '+') + } + return strconv.AppendInt(buf, e, 10) +} + +// fmtP appends the string of x in the format 0x." mantissa "p" exponent +// with a hexadecimal mantissa and a binary exponent, or 0" if x is zero, +// ad returns the extended buffer. +// The mantissa is normalized such that 0.5 <= 0.mantissa < 1.0. +// The sign of x is ignored, and x must not be an Inf. +func (x *Float) fmtP(buf []byte) []byte { + if x.form == zero { + return append(buf, '0') + } + + if debugFloat && x.form != finite { + panic("non-finite float") + } + // x != 0 + + // remove trailing 0 words early + // (no need to convert to hex 0's and trim later) + m := x.mant + i := 0 + for i < len(m) && m[i] == 0 { + i++ + } + m = m[i:] + + buf = append(buf, "0x."...) + buf = append(buf, strings.TrimRight(x.mant.hexString(), "0")...) + buf = append(buf, 'p') + if x.exp >= 0 { + buf = append(buf, '+') + } + return strconv.AppendInt(buf, int64(x.exp), 10) +} + func min(x, y int) int { if x < y { return x From 635cd91eb4c6f22e22b82c7cc831f64fba89581a Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Tue, 26 May 2015 16:42:24 -0700 Subject: [PATCH 220/232] math/big: more cleanups (msbxx, nlzxx functions) Change-Id: Ibace718452b6dc029c5af5240117f5fc794c38cf Reviewed-on: https://go-review.googlesource.com/10388 Reviewed-by: Alan Donovan --- src/math/big/arith.go | 21 ++++++++-- src/math/big/float.go | 82 ++++++++++++++++++++-------------------- src/math/big/nat.go | 4 +- src/math/big/nat_test.go | 6 +-- 4 files changed, 64 insertions(+), 49 deletions(-) diff --git a/src/math/big/arith.go b/src/math/big/arith.go index 1ff6349d9d..d7ea8381e7 100644 --- a/src/math/big/arith.go +++ b/src/math/big/arith.go @@ -107,11 +107,26 @@ func log2(x Word) int { return bitLen(x) - 1 } -// Number of leading zeros in x. -func leadingZeros(x Word) uint { +// nlz returns the number of leading zeros in x. +func nlz(x Word) uint { return uint(_W - bitLen(x)) } +// nlz64 returns the number of leading zeros in x. +func nlz64(x uint64) uint { + switch _W { + case 32: + w := x >> 32 + if w == 0 { + return 32 + nlz(Word(x)) + } + return nlz(Word(w)) + case 64: + return nlz(Word(x)) + } + panic("unreachable") +} + // q = (u1<<_W + u0 - r)/y // Adapted from Warren, Hacker's Delight, p. 152. func divWW_g(u1, u0, v Word) (q, r Word) { @@ -119,7 +134,7 @@ func divWW_g(u1, u0, v Word) (q, r Word) { return 1<<_W - 1, 1<<_W - 1 } - s := leadingZeros(v) + s := nlz(v) v <<= s vn1 := v >> _W2 diff --git a/src/math/big/float.go b/src/math/big/float.go index e663c1c6ac..1563528797 100644 --- a/src/math/big/float.go +++ b/src/math/big/float.go @@ -525,25 +525,6 @@ func (z *Float) round(sbit uint) { return } -// nlz returns the number of leading zero bits in x. -func nlz(x Word) uint { - return _W - uint(bitLen(x)) -} - -func nlz64(x uint64) uint { - // TODO(gri) this can be done more nicely - if _W == 32 { - if x>>32 == 0 { - return 32 + nlz(Word(x)) - } - return nlz(Word(x >> 32)) - } - if _W == 64 { - return nlz(Word(x)) - } - panic("unreachable") -} - func (z *Float) setBits64(neg bool, x uint64) *Float { if z.prec == 0 { z.prec = 64 @@ -732,25 +713,44 @@ func (z *Float) Copy(x *Float) *Float { return z } -func high32(x nat) uint32 { - // TODO(gri) This can be done more efficiently on 32bit platforms. - return uint32(high64(x) >> 32) -} - -func high64(x nat) uint64 { - i := len(x) - if i == 0 { +// msb32 returns the 32 most significant bits of x. +func msb32(x nat) uint32 { + i := len(x) - 1 + if i < 0 { return 0 } - // i > 0 - v := uint64(x[i-1]) - if _W == 32 { - v <<= 32 - if i > 1 { - v |= uint64(x[i-2]) - } + if debugFloat && x[i]&(1<<(_W-1)) == 0 { + panic("x not normalized") } - return v + switch _W { + case 32: + return uint32(x[i]) + case 64: + return uint32(x[i] >> 32) + } + panic("unreachable") +} + +// msb64 returns the 64 most significant bits of x. +func msb64(x nat) uint64 { + i := len(x) - 1 + if i < 0 { + return 0 + } + if debugFloat && x[i]&(1<<(_W-1)) == 0 { + panic("x not normalized") + } + switch _W { + case 32: + v := uint64(x[i]) << 32 + if i > 0 { + v |= uint64(x[i-1]) + } + return v + case 64: + return uint64(x[i]) + } + panic("unreachable") } // Uint64 returns the unsigned integer resulting from truncating x @@ -776,7 +776,7 @@ func (x *Float) Uint64() (uint64, Accuracy) { // 1 <= x < Inf if x.exp <= 64 { // u = trunc(x) fits into a uint64 - u := high64(x.mant) >> (64 - uint32(x.exp)) + u := msb64(x.mant) >> (64 - uint32(x.exp)) if x.MinPrec() <= 64 { return u, Exact } @@ -821,7 +821,7 @@ func (x *Float) Int64() (int64, Accuracy) { // 1 <= |x| < +Inf if x.exp <= 63 { // i = trunc(x) fits into an int64 (excluding math.MinInt64) - i := int64(high64(x.mant) >> (64 - uint32(x.exp))) + i := int64(msb64(x.mant) >> (64 - uint32(x.exp))) if x.neg { i = -i } @@ -934,11 +934,11 @@ func (x *Float) Float32() (float32, Accuracy) { return 0.0, Below } // bexp = 0 - mant = high32(r.mant) >> (fbits - r.prec) + mant = msb32(r.mant) >> (fbits - r.prec) } else { // normal number: emin <= e <= emax bexp = uint32(e+bias) << mbits - mant = high32(r.mant) >> ebits & (1<> ebits & (1<> (fbits - r.prec) + mant = msb64(r.mant) >> (fbits - r.prec) } else { // normal number: emin <= e <= emax bexp = uint64(e+bias) << mbits - mant = high64(r.mant) >> ebits & (1<> ebits & (1< 0 { // do not modify v, it may be used by another goroutine simultaneously v1 := make(nat, n) @@ -942,7 +942,7 @@ func (z nat) expNN(x, y, m nat) nat { } v := y[len(y)-1] // v > 0 because y is normalized and y > 0 - shift := leadingZeros(v) + 1 + shift := nlz(v) + 1 v <<= shift var q nat diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go index a15a2bcac0..7ac3cb8a84 100644 --- a/src/math/big/nat_test.go +++ b/src/math/big/nat_test.go @@ -205,11 +205,11 @@ func BenchmarkMul(b *testing.B) { } } -func TestLeadingZeros(t *testing.T) { +func TestNLZ(t *testing.T) { var x Word = _B >> 1 for i := 0; i <= _W; i++ { - if int(leadingZeros(x)) != i { - t.Errorf("failed at %x: got %d want %d", x, leadingZeros(x), i) + if int(nlz(x)) != i { + t.Errorf("failed at %x: got %d want %d", x, nlz(x), i) } x >>= 1 } From 4e4c1f9c4d101d5578656c06a42784856fec9f0d Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Wed, 27 May 2015 16:33:03 -0700 Subject: [PATCH 221/232] cmd/dist: don't run go list when running a specific test This speeds up sharded builds notably, by 1 second * the number of tests. Change-Id: Ib0295c31e4974f3003f72cb16c48949812b6f22b Reviewed-on: https://go-review.googlesource.com/10460 Reviewed-by: Andrew Gerrand Run-TryBot: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/dist/test.go | 138 +++++++++++++++++++++++++++++++------------ 1 file changed, 101 insertions(+), 37 deletions(-) diff --git a/src/cmd/dist/test.go b/src/cmd/dist/test.go index 0b6b592eef..f5a0dc50f1 100644 --- a/src/cmd/dist/test.go +++ b/src/cmd/dist/test.go @@ -201,45 +201,71 @@ func (t *tester) timeout(sec int) string { return "-timeout=" + fmt.Sprint(time.Duration(sec)*time.Second*time.Duration(t.timeoutScale)) } -func (t *tester) registerTests() { - // Register a separate logical test for each package in the standard library - // but actually group them together at execution time to share the cost of - // building packages shared between them. - all, err := exec.Command("go", "list", "std", "cmd").Output() - if err != nil { - log.Fatalf("Error running go list std cmd: %v", err) +// ranGoTest and stdMatches are state closed over by the stdlib +// testing func in registerStdTest below. The tests are run +// sequentially, so there's no need for locks. +var ( + ranGoTest bool + stdMatches []string +) + +func (t *tester) registerStdTest(pkg string) { + testName := "go_test:" + pkg + if t.runRx == nil || t.runRx.MatchString(testName) { + stdMatches = append(stdMatches, pkg) } - // ranGoTest and stdMatches are state closed over by the - // stdlib testing func below. The tests are run sequentially, - // so there's no need for locks. - var ( - ranGoTest bool - stdMatches []string - ) - for _, pkg := range strings.Fields(string(all)) { - testName := "go_test:" + pkg - if t.runRx == nil || t.runRx.MatchString(testName) { - stdMatches = append(stdMatches, pkg) + t.tests = append(t.tests, distTest{ + name: testName, + heading: "Testing packages.", + fn: func() error { + if ranGoTest { + return nil + } + ranGoTest = true + cmd := exec.Command("go", append([]string{ + "test", + "-short", + t.timeout(120), + "-gcflags=" + os.Getenv("GO_GCFLAGS"), + }, stdMatches...)...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return cmd.Run() + }, + }) +} + +// validStdPkg reports whether pkg looks like a standard library package name. +// Notably, it's not blank and doesn't contain regexp characters. +func validStdPkg(pkg string) bool { + if pkg == "" { + return false + } + for _, r := range pkg { + switch { + case 'a' <= r && r <= 'z': + case 'A' <= r && r <= 'Z': + case '0' <= r && r <= '9': + case r == '_': + case r == '/': + default: + return false + } + } + return true +} + +func (t *tester) registerTests() { + // Fast path to avoid the ~1 second of `go list std cmd` when + // the caller passed -run=^go_test:foo/bar$ (as the continuous + // build coordinator does). + if strings.HasPrefix(t.runRxStr, "^go_test:") && strings.HasSuffix(t.runRxStr, "$") { + pkg := strings.TrimPrefix(t.runRxStr, "^go_test:") + pkg = strings.TrimSuffix(pkg, "$") + if validStdPkg(pkg) { + t.registerStdTest(pkg) + return } - t.tests = append(t.tests, distTest{ - name: testName, - heading: "Testing packages.", - fn: func() error { - if ranGoTest { - return nil - } - ranGoTest = true - cmd := exec.Command("go", append([]string{ - "test", - "-short", - t.timeout(120), - "-gcflags=" + os.Getenv("GO_GCFLAGS"), - }, stdMatches...)...) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - return cmd.Run() - }, - }) } // Runtime CPU tests. @@ -365,6 +391,44 @@ func (t *tester) registerTests() { }) } + // Register the standard library tests lasts, to avoid the ~1 second latency + // of running `go list std cmd` if we're running a specific test. + // Now we know the names of all the other tests registered so far. + if !t.wantSpecificRegisteredTest() { + all, err := exec.Command("go", "list", "std", "cmd").Output() + if err != nil { + log.Fatalf("Error running go list std cmd: %v", err) + } + // Put the standard library tests first. + orig := t.tests + t.tests = nil + for _, pkg := range strings.Fields(string(all)) { + t.registerStdTest(pkg) + } + t.tests = append(t.tests, orig...) + } +} + +// wantSpecificRegisteredTest reports whether the caller is requesting a +// run of a specific test via the flag -run=^TESTNAME$ (as is done by the +// continuous build coordinator). +func (t *tester) wantSpecificRegisteredTest() bool { + if !strings.HasPrefix(t.runRxStr, "^") || !strings.HasSuffix(t.runRxStr, "$") { + return false + } + test := t.runRxStr[1 : len(t.runRxStr)-1] + return t.isRegisteredTestName(test) +} + +// isRegisteredTestName reports whether a test named testName has already +// been registered. +func (t *tester) isRegisteredTestName(testName string) bool { + for _, tt := range t.tests { + if tt.name == testName { + return true + } + } + return false } func (t *tester) registerTest(name, dirBanner, bin string, args ...string) { From 0f27b915228ff661aeaf823fa90db023765cbe5d Mon Sep 17 00:00:00 2001 From: Shenghou Ma Date: Thu, 21 May 2015 17:51:34 -0400 Subject: [PATCH 222/232] cmd/internal/obj: make arm64 use RegTo2 instead of a full fledged Addr To2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It shrinks Prog type from 448 bytes down to 376 bytes on amd64. It also makes sense, because I don't know of any modern architecture that have instructions which can write to two destinations, none of which is a register (even x86 doesn't have such instructions). Change-Id: I3061f1c9ac93d79ee2b92ecb9049641d0e0f6300 Reviewed-on: https://go-review.googlesource.com/10330 Reviewed-by: Aram Hăvărneanu Reviewed-by: Josh Bleecher Snyder Reviewed-by: Russ Cox --- src/cmd/asm/internal/asm/asm.go | 5 ++++- src/cmd/compile/internal/arm64/peep.go | 4 ++-- src/cmd/internal/obj/arm64/asm7.go | 4 ++-- src/cmd/internal/obj/link.go | 2 +- src/cmd/internal/obj/util.go | 4 ++-- src/cmd/internal/obj/x86/obj6.go | 3 --- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/cmd/asm/internal/asm/asm.go b/src/cmd/asm/internal/asm/asm.go index 725c6352cb..d5d2772ef3 100644 --- a/src/cmd/asm/internal/asm/asm.go +++ b/src/cmd/asm/internal/asm/asm.go @@ -493,7 +493,10 @@ func (p *Parser) asmInstruction(op int, cond string, a []obj.Addr) { if arch.IsARM64STLXR(op) { prog.From = a[0] prog.To = a[1] - prog.To2 = a[2] + if a[2].Type != obj.TYPE_REG { + p.errorf("invalid addressing modes for third operand to %s instruction, must be register", obj.Aconv(op)) + } + prog.RegTo2 = a[2].Reg break } prog.From = a[0] diff --git a/src/cmd/compile/internal/arm64/peep.go b/src/cmd/compile/internal/arm64/peep.go index 1c3b2891aa..3dbccb70b2 100644 --- a/src/cmd/compile/internal/arm64/peep.go +++ b/src/cmd/compile/internal/arm64/peep.go @@ -422,9 +422,9 @@ func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { // 7g never generates a from3 fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3)) } - if p.To2.Type != obj.TYPE_NONE { + if p.RegTo2 != obj.REG_NONE { // 7g never generates a to2 - fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2)) + fmt.Printf("copyu: RegTo2 (%v) not implemented\n", obj.Rconv(int(p.RegTo2))) } switch p.As { diff --git a/src/cmd/internal/obj/arm64/asm7.go b/src/cmd/internal/obj/arm64/asm7.go index 6e00cb55ab..9e643932be 100644 --- a/src/cmd/internal/obj/arm64/asm7.go +++ b/src/cmd/internal/obj/arm64/asm7.go @@ -2677,8 +2677,8 @@ func asmout(ctxt *obj.Link, p *obj.Prog, o *Optab, out []uint32) { case 59: /* stxr/stlxr */ o1 = opstore(ctxt, int(p.As)) - if p.To2.Type != obj.TYPE_NONE { - o1 |= uint32(p.To2.Reg&31) << 16 + if p.RegTo2 != obj.REG_NONE { + o1 |= uint32(p.RegTo2&31) << 16 } else { o1 |= 0x1F << 16 } diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index b0c7a55ca1..2fc12c1eb1 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -206,7 +206,6 @@ type Prog struct { From Addr From3 Addr To Addr - To2 Addr Opt interface{} Forwd *Prog Pcond *Prog @@ -217,6 +216,7 @@ type Prog struct { Spadj int32 As int16 Reg int16 + RegTo2 int16 // 2nd register output operand Mark uint16 Optab uint16 Scond uint8 diff --git a/src/cmd/internal/obj/util.go b/src/cmd/internal/obj/util.go index 317ee4f14d..efecae62ac 100644 --- a/src/cmd/internal/obj/util.go +++ b/src/cmd/internal/obj/util.go @@ -327,8 +327,8 @@ func (p *Prog) String() string { if p.To.Type != TYPE_NONE { fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To)) } - if p.To2.Type != TYPE_NONE { - fmt.Fprintf(&buf, "%s%v", sep, Dconv(p, &p.To2)) + if p.RegTo2 != REG_NONE { + fmt.Fprintf(&buf, "%s%v", sep, Rconv(int(p.RegTo2))) } return buf.String() } diff --git a/src/cmd/internal/obj/x86/obj6.go b/src/cmd/internal/obj/x86/obj6.go index 7a4fc128e6..4798c8f7fb 100644 --- a/src/cmd/internal/obj/x86/obj6.go +++ b/src/cmd/internal/obj/x86/obj6.go @@ -350,9 +350,6 @@ func progedit(ctxt *obj.Link, p *obj.Prog) { if p.From3.Name == obj.NAME_EXTERN { ctxt.Diag("don't know how to handle %v with -dynlink", p) } - if p.To2.Name == obj.NAME_EXTERN { - ctxt.Diag("don't know how to handle %v with -dynlink", p) - } var source *obj.Addr if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local { if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local { From 8b186df7311c53a06b98e375b987f0a3b3672798 Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Wed, 27 May 2015 12:33:43 -0700 Subject: [PATCH 223/232] test: remove arch char from nosplit This is dead code that was missed during the 'go tool compile' migration. Change-Id: Ice2af8a9ef72f8fd5f82225ee261854d93b659f1 Reviewed-on: https://go-review.googlesource.com/10430 Reviewed-by: Brad Fitzpatrick --- test/nosplit.go | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/test/nosplit.go b/test/nosplit.go index 0fc8dc47f0..8864137eb1 100644 --- a/test/nosplit.go +++ b/test/nosplit.go @@ -184,19 +184,10 @@ func main() { goarch = runtime.GOARCH } - thechar := "" - if gochar, err := exec.Command("go", "env", "GOCHAR").Output(); err != nil { - bug() - fmt.Printf("running go env GOCHAR: %v\n", err) - return - } else { - thechar = strings.TrimSpace(string(gochar)) - } - version, err := exec.Command("go", "tool", "compile", "-V").Output() if err != nil { bug() - fmt.Printf("running go tool %sg -V: %v\n", thechar, err) + fmt.Printf("running go tool compile -V: %v\n", err) return } if strings.Contains(string(version), "framepointer") { From 5ee552815ceaa5874b73125df07265bee34d1cc1 Mon Sep 17 00:00:00 2001 From: Ryan Brown Date: Wed, 8 Apr 2015 12:55:34 -0700 Subject: [PATCH 224/232] cmd/link/internal/ld: Skip combining dwarf for darwin/arm. Change-Id: I3a6df0a76d57db7cb6910f4179a6ce380f219a37 Reviewed-on: https://go-review.googlesource.com/10442 Reviewed-by: David Crawshaw Run-TryBot: David Crawshaw TryBot-Result: Gobot Gobot --- src/cmd/link/internal/ld/lib.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 32ee45bcec..d87f1801f0 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -1055,21 +1055,24 @@ func hostlink() { } if Debug['s'] == 0 && debug_s == 0 && HEADTYPE == obj.Hdarwin { - dsym := fmt.Sprintf("%s/go.dwarf", tmpdir) - if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil { - Ctxt.Cursym = nil - Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out) - } - combinedOutput := fmt.Sprintf("%s/go.combined", tmpdir) - if err := machoCombineDwarf(outfile, dsym, combinedOutput); err != nil { - Ctxt.Cursym = nil - Exitf("%s: combining dwarf failed: %v", os.Args[0], err) - } - origOutput := fmt.Sprintf("%s/go.orig", tmpdir) - os.Rename(outfile, origOutput) - if err := os.Rename(combinedOutput, outfile); err != nil { - Ctxt.Cursym = nil - Exitf("%s: rename(%s, %s) failed: %v", os.Args[0], combinedOutput, outfile, err) + // Skip combining dwarf on arm. + if Thearch.Thechar != '5' && Thearch.Thechar != '7' { + dsym := fmt.Sprintf("%s/go.dwarf", tmpdir) + if out, err := exec.Command("dsymutil", "-f", outfile, "-o", dsym).CombinedOutput(); err != nil { + Ctxt.Cursym = nil + Exitf("%s: running dsymutil failed: %v\n%s", os.Args[0], err, out) + } + combinedOutput := fmt.Sprintf("%s/go.combined", tmpdir) + if err := machoCombineDwarf(outfile, dsym, combinedOutput); err != nil { + Ctxt.Cursym = nil + Exitf("%s: combining dwarf failed: %v", os.Args[0], err) + } + origOutput := fmt.Sprintf("%s/go.orig", tmpdir) + os.Rename(outfile, origOutput) + if err := os.Rename(combinedOutput, outfile); err != nil { + Ctxt.Cursym = nil + Exitf("%s: rename(%s, %s) failed: %v", os.Args[0], combinedOutput, outfile, err) + } } } } From 0c02b33acda207964adde58b610b6f2c82ffde1d Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Wed, 27 May 2015 14:43:30 -0700 Subject: [PATCH 225/232] math/big: fix latent decimal conversion bug A decimal represented 0.0 with a 0-length mantissa and undefined exponent, but the formatting code assumes a valid zero exponent if the float value is 0.0. The code worked because we allocate a new decimal value each time and because there's no rounding that lead to 0.0. Change-Id: Ifd771d7709de83b87fdbf141786286b4c3e13d4f Reviewed-on: https://go-review.googlesource.com/10448 Reviewed-by: Alan Donovan --- src/math/big/decimal.go | 14 ++++++++++---- src/math/big/floatconv_test.go | 4 ++++ src/math/big/ftoa.go | 8 +++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/math/big/decimal.go b/src/math/big/decimal.go index 3d024dce68..2595e5f8c1 100644 --- a/src/math/big/decimal.go +++ b/src/math/big/decimal.go @@ -19,12 +19,14 @@ package big -// A decimal represents a floating-point number in decimal representation. -// The value of a decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1, -// with the most-significant mantissa digit at index 0. +// A decimal represents an unsigned floating-point number in decimal representation. +// The value of a non-zero decimal x is x.mant * 10 ** x.exp with 0.5 <= x.mant < 1, +// with the most-significant mantissa digit at index 0. For the zero decimal, the +// mantissa length and exponent are 0. +// The zero value for decimal represents a ready-to-use 0.0. type decimal struct { mant []byte // mantissa ASCII digits, big-endian - exp int // exponent, valid if len(mant) > 0 + exp int // exponent } // Maximum shift amount that can be done in one pass without overflow. @@ -46,6 +48,7 @@ func (x *decimal) init(m nat, shift int) { // special case 0 if len(m) == 0 { x.mant = x.mant[:0] + x.exp = 0 return } @@ -255,4 +258,7 @@ func trim(x *decimal) { i-- } x.mant = x.mant[:i] + if i == 0 { + x.exp = 0 + } } diff --git a/src/math/big/floatconv_test.go b/src/math/big/floatconv_test.go index db300314f1..9fc2b89fb9 100644 --- a/src/math/big/floatconv_test.go +++ b/src/math/big/floatconv_test.go @@ -125,12 +125,16 @@ func TestFloat64Format(t *testing.T) { {1, 'f', 0, "1"}, {-1, 'f', 0, "-1"}, + {0.001, 'e', 0, "1e-03"}, + {0.459, 'e', 0, "5e-01"}, {1.459, 'e', 0, "1e+00"}, {2.459, 'e', 1, "2.5e+00"}, {3.459, 'e', 2, "3.46e+00"}, {4.459, 'e', 3, "4.459e+00"}, {5.459, 'e', 4, "5.4590e+00"}, + {0.001, 'f', 0, "0"}, + {0.459, 'f', 0, "0"}, {1.459, 'f', 0, "1"}, {2.459, 'f', 1, "2.5"}, {3.459, 'f', 2, "3.46"}, diff --git a/src/math/big/ftoa.go b/src/math/big/ftoa.go index 0f943e1ff2..4c3e743d6c 100644 --- a/src/math/big/ftoa.go +++ b/src/math/big/ftoa.go @@ -58,7 +58,7 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte { } // Inf - if x.IsInf() { + if x.form == inf { if !x.neg { buf = append(buf, '+') } @@ -79,12 +79,10 @@ func (x *Float) Append(buf []byte, fmt byte, prec int) []byte { // 3) read digits out and format // 1) convert Float to multiprecision decimal - var mant nat + var d decimal // == 0.0 if x.form == finite { - mant = x.mant + d.init(x.mant, int(x.exp)-x.mant.bitLen()) } - var d decimal - d.init(mant, int(x.exp)-x.mant.bitLen()) // 2) round to desired precision shortest := false From 4a1957d0aaad9c6700b4bdab19af611e68468bf3 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 26 May 2015 15:21:18 -0400 Subject: [PATCH 226/232] runtime: use stripped test environment for TestGdbPython Most runtime tests that invoke the compiler to build a sub-test binary do so with a special environment constructed by testEnv that strips out environment variables that should apply to the test but not to the build. Fix TestGdbPython to use this test environment when invoking go build, like other tests do. Change-Id: Iafdf89d4765c587cbebc427a5d61cb8a7e71b326 Reviewed-on: https://go-review.googlesource.com/10455 Reviewed-by: Russ Cox --- src/runtime/runtime-gdb_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/runtime/runtime-gdb_test.go b/src/runtime/runtime-gdb_test.go index 8d04f6328c..f4014b2e05 100644 --- a/src/runtime/runtime-gdb_test.go +++ b/src/runtime/runtime-gdb_test.go @@ -59,7 +59,7 @@ func TestGdbPython(t *testing.T) { cmd := exec.Command("go", "build", "-o", "a.exe") cmd.Dir = dir - out, err := cmd.CombinedOutput() + out, err := testEnv(cmd).CombinedOutput() if err != nil { t.Fatalf("building source %v\n%s", err, out) } From f90d802b6102ced82377ddc16f81f299c039ce83 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Wed, 27 May 2015 15:20:49 -0400 Subject: [PATCH 227/232] cmd/compile: avoid temporary in race mode with slice and append Currently when the race detector is enabled, orderexpr always creates a temporary for slice and append operations. This used to be necessary because the race detector had a different code path for slice assignment that required this temporary. Unfortunately, creating this temporary inhibits the optimization that eliminates write barriers when a slice is assigned only to change its length or cap. For most code, this is bad for performance, and in go:nowritebarrier functions in the runtime, this can mean the difference between compiling and not compiling. Now the race detector uses the regular slice assignment code, so creating this temporary is no longer necessary. Change-Id: I296042e1edc571b77c407f709c2ff9091c4aa795 Reviewed-on: https://go-review.googlesource.com/10456 Reviewed-by: Russ Cox --- src/cmd/compile/internal/gc/order.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cmd/compile/internal/gc/order.go b/src/cmd/compile/internal/gc/order.go index 8b99ed0895..ee0ec52e7b 100644 --- a/src/cmd/compile/internal/gc/order.go +++ b/src/cmd/compile/internal/gc/order.go @@ -1090,7 +1090,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) { case OAPPEND: ordercallargs(&n.List, order) - if lhs == nil || flag_race != 0 || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.N) { + if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.List.N) { n = ordercopyexpr(n, n.Type, order, 0) } @@ -1100,7 +1100,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) { n.Right.Left = ordercheapexpr(n.Right.Left, order) orderexpr(&n.Right.Right, order, nil) n.Right.Right = ordercheapexpr(n.Right.Right, order) - if lhs == nil || flag_race != 0 || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) { + if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) { n = ordercopyexpr(n, n.Type, order, 0) } @@ -1112,7 +1112,7 @@ func orderexpr(np **Node, order *Order, lhs *Node) { n.Right.Right.Left = ordercheapexpr(n.Right.Right.Left, order) orderexpr(&n.Right.Right.Right, order, nil) n.Right.Right.Right = ordercheapexpr(n.Right.Right.Right, order) - if lhs == nil || flag_race != 0 || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) { + if lhs == nil || lhs.Op != ONAME && !samesafeexpr(lhs, n.Left) { n = ordercopyexpr(n, n.Type, order, 0) } From f2c3957ed8b44f29aaf9a1a7ddc5208ae2168dc9 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Tue, 26 May 2015 14:32:24 -0400 Subject: [PATCH 228/232] runtime: disable GC around TestGoroutineParallelism TestGoroutineParallelism can deadlock if the GC runs during the test. Currently it tries to prevent this by forcing a GC before the test, but this is best effort and fails completely if GOGC is very low for testing. This change replaces this best-effort fix with simply setting GOGC to off for the duration of the test. Change-Id: I8229310833f241b149ebcd32845870c1cb14e9f8 Reviewed-on: https://go-review.googlesource.com/10454 Reviewed-by: Russ Cox --- src/runtime/proc_test.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/runtime/proc_test.go b/src/runtime/proc_test.go index 4c5712d32f..4471ee5afb 100644 --- a/src/runtime/proc_test.go +++ b/src/runtime/proc_test.go @@ -7,6 +7,7 @@ package runtime_test import ( "math" "runtime" + "runtime/debug" "sync" "sync/atomic" "syscall" @@ -104,8 +105,8 @@ func TestGoroutineParallelism(t *testing.T) { defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P)) // If runtime triggers a forced GC during this test then it will deadlock, // since the goroutines can't be stopped/preempted. - // So give this test as much time as possible. - runtime.GC() + // Disable GC for this test (see issue #10958). + defer debug.SetGCPercent(debug.SetGCPercent(-1)) for try := 0; try < N; try++ { done := make(chan bool) x := uint32(0) From bd8bb6735765653b29c672d724eaf80a477d1eba Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Tue, 19 May 2015 15:25:35 -0700 Subject: [PATCH 229/232] cmd/internal/gc: unembed Param field This is an automated follow-up to CL 10210. It was generated with a combination of eg and gofmt -r. No functional changes. Passes toolstash -cmp. Change-Id: I35f5897948a270b472d8cf80612071b4b29e9a2b Reviewed-on: https://go-review.googlesource.com/10253 Reviewed-by: Brad Fitzpatrick Run-TryBot: Josh Bleecher Snyder TryBot-Result: Gobot Gobot --- src/cmd/compile/internal/gc/align.go | 4 +- src/cmd/compile/internal/gc/closure.go | 48 ++++++++++++------------ src/cmd/compile/internal/gc/dcl.go | 38 +++++++++---------- src/cmd/compile/internal/gc/esc.go | 14 +++---- src/cmd/compile/internal/gc/export.go | 2 +- src/cmd/compile/internal/gc/fmt.go | 10 ++--- src/cmd/compile/internal/gc/gen.go | 8 ++-- src/cmd/compile/internal/gc/go.y | 4 +- src/cmd/compile/internal/gc/init.go | 2 +- src/cmd/compile/internal/gc/lex.go | 4 +- src/cmd/compile/internal/gc/sinit.go | 2 +- src/cmd/compile/internal/gc/subr.go | 10 ++--- src/cmd/compile/internal/gc/swt.go | 4 +- src/cmd/compile/internal/gc/syntax.go | 2 +- src/cmd/compile/internal/gc/typecheck.go | 46 +++++++++++------------ src/cmd/compile/internal/gc/walk.go | 10 ++--- src/cmd/compile/internal/gc/y.go | 4 +- 17 files changed, 106 insertions(+), 106 deletions(-) diff --git a/src/cmd/compile/internal/gc/align.go b/src/cmd/compile/internal/gc/align.go index 789e59bfd0..892595a214 100644 --- a/src/cmd/compile/internal/gc/align.go +++ b/src/cmd/compile/internal/gc/align.go @@ -71,8 +71,8 @@ func widstruct(errtype *Type, t *Type, o int64, flag int) int64 { // in typecheck.c. usually addrescapes runs after // widstruct, in which case we could drop this, // but function closure functions are the exception. - if f.Nname.Stackparam != nil { - f.Nname.Stackparam.Xoffset = o + if f.Nname.Param.Stackparam != nil { + f.Nname.Param.Stackparam.Xoffset = o f.Nname.Xoffset = 0 } else { f.Nname.Xoffset = o diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go index b51e74b77d..64cd97206c 100644 --- a/src/cmd/compile/internal/gc/closure.go +++ b/src/cmd/compile/internal/gc/closure.go @@ -17,7 +17,7 @@ func closurehdr(ntype *Node) { var a *Node n := Nod(OCLOSURE, nil, nil) - n.Ntype = ntype + n.Param.Ntype = ntype n.Funcdepth = Funcdepth n.Func.Outerfunc = Curfn @@ -72,8 +72,8 @@ func closurebody(body *NodeList) *Node { var v *Node for l := func_.Func.Cvars; l != nil; l = l.Next { v = l.N - v.Closure.Closure = v.Outer - v.Outerexpr = oldname(v.Sym) + v.Param.Closure.Param.Closure = v.Param.Outer + v.Param.Outerexpr = oldname(v.Sym) } return func_ @@ -83,7 +83,7 @@ func typecheckclosure(func_ *Node, top int) { var n *Node for l := func_.Func.Cvars; l != nil; l = l.Next { - n = l.N.Closure + n = l.N.Param.Closure if !n.Name.Captured { n.Name.Captured = true if n.Name.Decldepth == 0 { @@ -105,9 +105,9 @@ func typecheckclosure(func_ *Node, top int) { } oldfn := Curfn - typecheck(&func_.Ntype, Etype) - func_.Type = func_.Ntype.Type - func_.Top = top + typecheck(&func_.Param.Ntype, Etype) + func_.Type = func_.Param.Ntype.Type + func_.Param.Top = top // Type check the body now, but only if we're inside a function. // At top level (in a variable initialization: curfn==nil) we're not @@ -193,7 +193,7 @@ func makeclosure(func_ *Node) *Node { xfunc.Nname = newfuncname(closurename(func_)) xfunc.Nname.Sym.Flags |= SymExported // disable export - xfunc.Nname.Ntype = xtype + xfunc.Nname.Param.Ntype = xtype xfunc.Nname.Defn = xfunc declare(xfunc.Nname, PFUNC) xfunc.Nname.Funcdepth = func_.Funcdepth @@ -207,8 +207,8 @@ func makeclosure(func_ *Node) *Node { } typecheck(&xfunc, Etop) - xfunc.Closure = func_ - func_.Closure = xfunc + xfunc.Param.Closure = func_ + func_.Param.Closure = xfunc func_.Nbody = nil func_.List = nil @@ -229,7 +229,7 @@ func capturevars(xfunc *Node) { lno := int(lineno) lineno = xfunc.Lineno - func_ := xfunc.Closure + func_ := xfunc.Param.Closure func_.Func.Enter = nil for l := func_.Func.Cvars; l != nil; l = l.Next { v = l.N @@ -249,14 +249,14 @@ func capturevars(xfunc *Node) { // so that the outer frame also grabs them and knows they escape. dowidth(v.Type) - outer = v.Outerexpr - v.Outerexpr = nil + outer = v.Param.Outerexpr + v.Param.Outerexpr = nil // out parameters will be assigned to implicitly upon return. - if outer.Class != PPARAMOUT && !v.Closure.Addrtaken && !v.Closure.Assigned && v.Type.Width <= 128 { + if outer.Class != PPARAMOUT && !v.Param.Closure.Addrtaken && !v.Param.Closure.Assigned && v.Type.Width <= 128 { v.Name.Byval = true } else { - v.Closure.Addrtaken = true + v.Param.Closure.Addrtaken = true outer = Nod(OADDR, outer, nil) } @@ -269,7 +269,7 @@ func capturevars(xfunc *Node) { if v.Name.Byval { how = "value" } - Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Closure.Addrtaken, v.Closure.Assigned, int32(v.Type.Width)) + Warnl(int(v.Lineno), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym, v.Param.Closure.Addrtaken, v.Param.Closure.Assigned, int32(v.Type.Width)) } typecheck(&outer, Erv) @@ -284,9 +284,9 @@ func capturevars(xfunc *Node) { func transformclosure(xfunc *Node) { lno := int(lineno) lineno = xfunc.Lineno - func_ := xfunc.Closure + func_ := xfunc.Param.Closure - if func_.Top&Ecall != 0 { + if func_.Param.Top&Ecall != 0 { // If the closure is directly called, we transform it to a plain function call // with variables passed as args. This avoids allocation of a closure object. // Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE) @@ -395,7 +395,7 @@ func transformclosure(xfunc *Node) { // Declare variable holding addresses taken from closure // and initialize in entry prologue. addr = newname(Lookupf("&%s", v.Sym.Name)) - addr.Ntype = Nod(OIND, typenod(v.Type), nil) + addr.Param.Ntype = Nod(OIND, typenod(v.Type), nil) addr.Class = PAUTO addr.Used = true addr.Curfn = xfunc @@ -420,7 +420,7 @@ func transformclosure(xfunc *Node) { func walkclosure(func_ *Node, init **NodeList) *Node { // If no closure vars, don't bother wrapping. if func_.Func.Cvars == nil { - return func_.Closure.Nname + return func_.Param.Closure.Nname } // Create closure in the form of a composite literal. @@ -457,7 +457,7 @@ func walkclosure(func_ *Node, init **NodeList) *Node { clos := Nod(OCOMPLIT, nil, Nod(OIND, typ, nil)) clos.Esc = func_.Esc clos.Right.Implicit = true - clos.List = concat(list1(Nod(OCFUNC, func_.Closure.Nname, nil)), func_.Func.Enter) + clos.List = concat(list1(Nod(OCFUNC, func_.Param.Closure.Nname, nil)), func_.Func.Enter) // Force type conversion from *struct to the func type. clos = Nod(OCONVNOP, clos, nil) @@ -583,7 +583,7 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { xfunc.Func.Dupok = true xfunc.Nname = newfuncname(sym) xfunc.Nname.Sym.Flags |= SymExported // disable export - xfunc.Nname.Ntype = xtype + xfunc.Nname.Param.Ntype = xtype xfunc.Nname.Defn = xfunc declare(xfunc.Nname, PFUNC) @@ -606,10 +606,10 @@ func makepartialcall(fn *Node, t0 *Type, meth *Node) *Node { xfunc.Func.Dcl = list(xfunc.Func.Dcl, ptr) var body *NodeList if Isptr[rcvrtype.Etype] || Isinter(rcvrtype) { - ptr.Ntype = typenod(rcvrtype) + ptr.Param.Ntype = typenod(rcvrtype) body = list(body, Nod(OAS, ptr, cv)) } else { - ptr.Ntype = typenod(Ptrto(rcvrtype)) + ptr.Param.Ntype = typenod(Ptrto(rcvrtype)) body = list(body, Nod(OAS, ptr, Nod(OADDR, cv, nil))) } diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 346b3beaeb..4a9cb295c8 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -260,7 +260,7 @@ func variter(vl *NodeList, t *Node, el *NodeList) *NodeList { v = vl.N v.Op = ONAME declare(v, dclcontext) - v.Ntype = t + v.Param.Ntype = t v.Defn = as2 if Funcdepth > 0 { init = list(init, Nod(ODCL, v, nil)) @@ -288,7 +288,7 @@ func variter(vl *NodeList, t *Node, el *NodeList) *NodeList { v = vl.N v.Op = ONAME declare(v, dclcontext) - v.Ntype = t + v.Param.Ntype = t if e != nil || Funcdepth > 0 || isblank(v) { if Funcdepth > 0 { @@ -343,7 +343,7 @@ func constiter(vl *NodeList, t *Node, cl *NodeList) *NodeList { v.Op = OLITERAL declare(v, dclcontext) - v.Ntype = t + v.Param.Ntype = t v.Defn = c vv = list(vv, Nod(ODCLCONST, v, nil)) @@ -431,7 +431,7 @@ func oldname(s *Sym) *Node { // are parsing x := 5 inside the closure, until we get to // the := it looks like a reference to the outer x so we'll // make x a closure variable unnecessarily. - if n.Closure == nil || n.Closure.Funcdepth != Funcdepth { + if n.Param.Closure == nil || n.Param.Closure.Funcdepth != Funcdepth { // create new closure var. c := Nod(ONAME, nil, nil) @@ -442,15 +442,15 @@ func oldname(s *Sym) *Node { c.Addable = false c.Ullman = 2 c.Funcdepth = Funcdepth - c.Outer = n.Closure - n.Closure = c - c.Closure = n + c.Param.Outer = n.Param.Closure + n.Param.Closure = c + c.Param.Closure = n c.Xoffset = 0 Curfn.Func.Cvars = list(Curfn.Func.Cvars, c) } // return ref to closure var, not original - return n.Closure + return n.Param.Closure } return n @@ -555,7 +555,7 @@ func ifacedcl(n *Node) { dclcontext = PPARAM markdcl() Funcdepth++ - n.Outer = Curfn + n.Param.Outer = Curfn Curfn = n funcargs(n.Right) @@ -584,13 +584,13 @@ func funchdr(n *Node) { markdcl() Funcdepth++ - n.Outer = Curfn + n.Param.Outer = Curfn Curfn = n if n.Nname != nil { - funcargs(n.Nname.Ntype) - } else if n.Ntype != nil { - funcargs(n.Ntype) + funcargs(n.Nname.Param.Ntype) + } else if n.Param.Ntype != nil { + funcargs(n.Param.Ntype) } else { funcargs2(n.Type) } @@ -616,7 +616,7 @@ func funcargs(nt *Node) { } if n.Left != nil { n.Left.Op = ONAME - n.Left.Ntype = n.Right + n.Left.Param.Ntype = n.Right declare(n.Left, PPARAM) if dclcontext == PAUTO { vargen++ @@ -633,7 +633,7 @@ func funcargs(nt *Node) { } if n.Left != nil { n.Left.Op = ONAME - n.Left.Ntype = n.Right + n.Left.Param.Ntype = n.Right declare(n.Left, PPARAM) if dclcontext == PAUTO { vargen++ @@ -680,7 +680,7 @@ func funcargs(nt *Node) { n.Left = nn } - n.Left.Ntype = n.Right + n.Left.Param.Ntype = n.Right declare(n.Left, PPARAMOUT) if dclcontext == PAUTO { i++ @@ -748,8 +748,8 @@ func funcbody(n *Node) { } popdcl() Funcdepth-- - Curfn = n.Outer - n.Outer = nil + Curfn = n.Param.Outer + n.Param.Outer = nil if Funcdepth == 0 { dclcontext = PEXTERN } @@ -771,7 +771,7 @@ func typedcl0(s *Sym) *Node { * return the ODCLTYPE node to use. */ func typedcl1(n *Node, t *Node, local bool) *Node { - n.Ntype = t + n.Param.Ntype = t n.Local = local return Nod(ODCLTYPE, n, nil) } diff --git a/src/cmd/compile/internal/gc/esc.go b/src/cmd/compile/internal/gc/esc.go index 578ce33a81..2c134933c4 100644 --- a/src/cmd/compile/internal/gc/esc.go +++ b/src/cmd/compile/internal/gc/esc.go @@ -154,7 +154,7 @@ func (v *bottomUpVisitor) visitcode(n *Node, min uint32) uint32 { } if n.Op == OCLOSURE { - m := v.visit(n.Closure) + m := v.visit(n.Param.Closure) if m < min { min = m } @@ -849,7 +849,7 @@ func esc(e *EscState, n *Node, up *Node) { if v.Op == OXXX { // unnamed out argument; see dcl.c:/^funcargs continue } - a = v.Closure + a = v.Param.Closure if !v.Name.Byval { a = Nod(OADDR, a, nil) a.Lineno = v.Lineno @@ -1363,7 +1363,7 @@ func esccall(e *EscState, n *Node, up *Node) { } if fn != nil && fn.Op == ONAME && fn.Class == PFUNC && - fn.Defn != nil && fn.Defn.Nbody != nil && fn.Ntype != nil && fn.Defn.Esc < EscFuncTagged { + fn.Defn != nil && fn.Defn.Nbody != nil && fn.Param.Ntype != nil && fn.Defn.Esc < EscFuncTagged { if Debug['m'] > 2 { fmt.Printf("%v::esccall:: %v in recursive group\n", Ctxt.Line(int(lineno)), Nconv(n, obj.FmtShort)) } @@ -1375,17 +1375,17 @@ func esccall(e *EscState, n *Node, up *Node) { } // set up out list on this call node - for lr := fn.Ntype.Rlist; lr != nil; lr = lr.Next { + for lr := fn.Param.Ntype.Rlist; lr != nil; lr = lr.Next { n.Escretval = list(n.Escretval, lr.N.Left) // type.rlist -> dclfield -> ONAME (PPARAMOUT) } // Receiver. if n.Op != OCALLFUNC { - escassign(e, fn.Ntype.Left.Left, n.Left.Left) + escassign(e, fn.Param.Ntype.Left.Left, n.Left.Left) } var src *Node - for lr := fn.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next { + for lr := fn.Param.Ntype.List; ll != nil && lr != nil; ll, lr = ll.Next, lr.Next { src = ll.N if lr.N.Isddd && !n.Isddd { // Introduce ODDDARG node to represent ... allocation. @@ -1653,7 +1653,7 @@ func escwalk(e *EscState, level Level, dst *Node, src *Node) { if leaks && Debug['m'] != 0 { Warnl(int(src.Lineno), "leaking closure reference %v", Nconv(src, obj.FmtShort)) } - escwalk(e, level, dst, src.Closure) + escwalk(e, level, dst, src.Param.Closure) } case OPTRLIT, OADDR: diff --git a/src/cmd/compile/internal/gc/export.go b/src/cmd/compile/internal/gc/export.go index 614de4e2ce..5117490ac8 100644 --- a/src/cmd/compile/internal/gc/export.go +++ b/src/cmd/compile/internal/gc/export.go @@ -64,7 +64,7 @@ func autoexport(n *Node, ctxt uint8) { if (ctxt != PEXTERN && ctxt != PFUNC) || dclcontext != PEXTERN { return } - if n.Param != nil && n.Ntype != nil && n.Ntype.Op == OTFUNC && n.Ntype.Left != nil { // method + if n.Param != nil && n.Param.Ntype != nil && n.Param.Ntype.Op == OTFUNC && n.Param.Ntype.Left != nil { // method return } diff --git a/src/cmd/compile/internal/gc/fmt.go b/src/cmd/compile/internal/gc/fmt.go index 9d8482bf76..4b93363c73 100644 --- a/src/cmd/compile/internal/gc/fmt.go +++ b/src/cmd/compile/internal/gc/fmt.go @@ -1199,7 +1199,7 @@ func exprfmt(n *Node, prec int) string { if n.Nbody != nil { return fmt.Sprintf("%v { %v }", n.Type, n.Nbody) } - return fmt.Sprintf("%v { %v }", n.Type, n.Closure.Nbody) + return fmt.Sprintf("%v { %v }", n.Type, n.Param.Closure.Nbody) case OCOMPLIT: ptrlit := n.Right != nil && n.Right.Implicit && n.Right.Type != nil && Isptr[n.Right.Type.Etype] @@ -1521,9 +1521,9 @@ func nodedump(n *Node, flag int) string { } else { fmt.Fprintf(&buf, "%v%v", Oconv(int(n.Op), 0), Jconv(n, 0)) } - if recur && n.Type == nil && n.Ntype != nil { + if recur && n.Type == nil && n.Param.Ntype != nil { indent(&buf) - fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype) + fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Param.Ntype) } case OASOP: @@ -1531,9 +1531,9 @@ func nodedump(n *Node, flag int) string { case OTYPE: fmt.Fprintf(&buf, "%v %v%v type=%v", Oconv(int(n.Op), 0), n.Sym, Jconv(n, 0), n.Type) - if recur && n.Type == nil && n.Ntype != nil { + if recur && n.Type == nil && n.Param.Ntype != nil { indent(&buf) - fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Ntype) + fmt.Fprintf(&buf, "%v-ntype%v", Oconv(int(n.Op), 0), n.Param.Ntype) } } diff --git a/src/cmd/compile/internal/gc/gen.go b/src/cmd/compile/internal/gc/gen.go index d3c6387a4e..c0dd9964ea 100644 --- a/src/cmd/compile/internal/gc/gen.go +++ b/src/cmd/compile/internal/gc/gen.go @@ -57,14 +57,14 @@ func addrescapes(n *Node) { // expression to refer to stack copy case PPARAM, PPARAMOUT: - n.Stackparam = Nod(OPARAM, n, nil) + n.Param.Stackparam = Nod(OPARAM, n, nil) - n.Stackparam.Type = n.Type - n.Stackparam.Addable = true + n.Param.Stackparam.Type = n.Type + n.Param.Stackparam.Addable = true if n.Xoffset == BADWIDTH { Fatal("addrescapes before param assignment") } - n.Stackparam.Xoffset = n.Xoffset + n.Param.Stackparam.Xoffset = n.Xoffset fallthrough case PAUTO: diff --git a/src/cmd/compile/internal/gc/go.y b/src/cmd/compile/internal/gc/go.y index 7d523ae7c0..ae2e7613ab 100644 --- a/src/cmd/compile/internal/gc/go.y +++ b/src/cmd/compile/internal/gc/go.y @@ -1422,7 +1422,7 @@ fndcl: $$ = Nod(ODCLFUNC, nil, nil); $$.Nname = newfuncname($1); $$.Nname.Defn = $$; - $$.Nname.Ntype = t; // TODO: check if nname already has an ntype + $$.Nname.Param.Ntype = t; // TODO: check if nname already has an ntype declare($$.Nname, PFUNC); funchdr($$); @@ -1457,7 +1457,7 @@ fndcl: $$.Func.Shortname = newfuncname($4); $$.Nname = methodname1($$.Func.Shortname, rcvr.Right); $$.Nname.Defn = $$; - $$.Nname.Ntype = t; + $$.Nname.Param.Ntype = t; $$.Nname.Nointerface = nointerface; declare($$.Nname, PFUNC); diff --git a/src/cmd/compile/internal/gc/init.go b/src/cmd/compile/internal/gc/init.go index b5d1e505a5..92bfeecdef 100644 --- a/src/cmd/compile/internal/gc/init.go +++ b/src/cmd/compile/internal/gc/init.go @@ -116,7 +116,7 @@ func fninit(n *NodeList) { initsym := Lookup("init") fn.Nname = newname(initsym) fn.Nname.Defn = fn - fn.Nname.Ntype = Nod(OTFUNC, nil, nil) + fn.Nname.Param.Ntype = Nod(OTFUNC, nil, nil) declare(fn.Nname, PFUNC) funchdr(fn) diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index 974ca9282e..d9ba9b210b 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -400,7 +400,7 @@ func Main() { // This needs to run before escape analysis, // because variables captured by value do not escape. for l := xtop; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC && l.N.Closure != nil { + if l.N.Op == ODCLFUNC && l.N.Param.Closure != nil { Curfn = l.N capturevars(l.N) } @@ -454,7 +454,7 @@ func Main() { // This needs to happen before walk, because closures must be transformed // before walk reaches a call of a closure. for l := xtop; l != nil; l = l.Next { - if l.N.Op == ODCLFUNC && l.N.Closure != nil { + if l.N.Op == ODCLFUNC && l.N.Param.Closure != nil { Curfn = l.N transformclosure(l.N) } diff --git a/src/cmd/compile/internal/gc/sinit.go b/src/cmd/compile/internal/gc/sinit.go index dfaec74de2..b5427a338c 100644 --- a/src/cmd/compile/internal/gc/sinit.go +++ b/src/cmd/compile/internal/gc/sinit.go @@ -221,7 +221,7 @@ func init2(n *Node, out **NodeList) { init2list(n.Nelse, out) if n.Op == OCLOSURE { - init2list(n.Closure.Nbody, out) + init2list(n.Param.Closure.Nbody, out) } if n.Op == ODOTMETH || n.Op == OCALLPART { init2(n.Type.Nname, out) diff --git a/src/cmd/compile/internal/gc/subr.go b/src/cmd/compile/internal/gc/subr.go index 08fafa819f..ed5001a983 100644 --- a/src/cmd/compile/internal/gc/subr.go +++ b/src/cmd/compile/internal/gc/subr.go @@ -2375,7 +2375,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) { markdcl() this := Nod(ODCLFIELD, newname(Lookup(".this")), typenod(rcvr)) - this.Left.Ntype = this.Right + this.Left.Param.Ntype = this.Right in := structargs(getinarg(method.Type), 1) out := structargs(Getoutarg(method.Type), 0) @@ -2401,7 +2401,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) { fn := Nod(ODCLFUNC, nil, nil) fn.Nname = newname(newnam) fn.Nname.Defn = fn - fn.Nname.Ntype = t + fn.Nname.Param.Ntype = t declare(fn.Nname, PFUNC) funchdr(fn) @@ -2575,7 +2575,7 @@ func genhash(sym *Sym, t *Type) { fn.Nname = newname(sym) fn.Nname.Class = PFUNC tfn := Nod(OTFUNC, nil, nil) - fn.Nname.Ntype = tfn + fn.Nname.Param.Ntype = tfn n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) tfn.List = list(tfn.List, n) @@ -2587,7 +2587,7 @@ func genhash(sym *Sym, t *Type) { tfn.Rlist = list(tfn.Rlist, n) funchdr(fn) - typecheck(&fn.Nname.Ntype, Etype) + typecheck(&fn.Nname.Param.Ntype, Etype) // genhash is only called for types that have equality but // cannot be handled by the standard algorithms, @@ -2827,7 +2827,7 @@ func geneq(sym *Sym, t *Type) { fn.Nname = newname(sym) fn.Nname.Class = PFUNC tfn := Nod(OTFUNC, nil, nil) - fn.Nname.Ntype = tfn + fn.Nname.Param.Ntype = tfn n := Nod(ODCLFIELD, newname(Lookup("p")), typenod(Ptrto(t))) tfn.List = list(tfn.List, n) diff --git a/src/cmd/compile/internal/gc/swt.go b/src/cmd/compile/internal/gc/swt.go index e8f15a5ce5..221b1f43eb 100644 --- a/src/cmd/compile/internal/gc/swt.go +++ b/src/cmd/compile/internal/gc/swt.go @@ -169,10 +169,10 @@ func typecheckswitch(n *Node) { if nvar != nil { if ll != nil && ll.Next == nil && ll.N.Type != nil && !Istype(ll.N.Type, TNIL) { // single entry type switch - nvar.Ntype = typenod(ll.N.Type) + nvar.Param.Ntype = typenod(ll.N.Type) } else { // multiple entry type switch or default - nvar.Ntype = typenod(n.Type) + nvar.Param.Ntype = typenod(n.Type) } typecheck(&nvar, Erv|Easgn) diff --git a/src/cmd/compile/internal/gc/syntax.go b/src/cmd/compile/internal/gc/syntax.go index 69348d1c2f..be4307690d 100644 --- a/src/cmd/compile/internal/gc/syntax.go +++ b/src/cmd/compile/internal/gc/syntax.go @@ -38,7 +38,7 @@ type Node struct { Curfn *Node // function for local variables Paramfld *Type // TFIELD for this PPARAM; also for ODOT, curfn Alloc *Node // allocation call - *Param + Param *Param // OPACK Pkg *Pkg diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 0395ec5f5b..2900da8be7 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -813,8 +813,8 @@ OpSwitch: var l *Node for l = n.Left; l != r; l = l.Left { l.Addrtaken = true - if l.Param != nil && l.Closure != nil { - l.Closure.Addrtaken = true + if l.Param != nil && l.Param.Closure != nil { + l.Param.Closure.Addrtaken = true } } @@ -822,8 +822,8 @@ OpSwitch: Fatal("found non-orig name node %v", l) } l.Addrtaken = true - if l.Param != nil && l.Closure != nil { - l.Closure.Addrtaken = true + if l.Param != nil && l.Param.Closure != nil { + l.Param.Closure.Addrtaken = true } defaultlit(&n.Left, nil) l = n.Left @@ -3273,14 +3273,14 @@ func checkassign(stmt *Node, n *Node) { var l *Node for l = n; l != r; l = l.Left { l.Assigned = true - if l.Param != nil && l.Closure != nil { - l.Closure.Assigned = true + if l.Param != nil && l.Param.Closure != nil { + l.Param.Closure.Assigned = true } } l.Assigned = true - if l.Param != nil && l.Closure != nil { - l.Closure.Assigned = true + if l.Param != nil && l.Param.Closure != nil { + l.Param.Closure.Assigned = true } } @@ -3345,7 +3345,7 @@ func typecheckas(n *Node) { // so that the conversion below happens). n.Left = resolve(n.Left) - if n.Left.Defn != n || n.Left.Ntype != nil { + if n.Left.Defn != n || n.Left.Param.Ntype != nil { typecheck(&n.Left, Erv|Easgn) } @@ -3357,7 +3357,7 @@ func typecheckas(n *Node) { } } - if n.Left.Defn == n && n.Left.Ntype == nil { + if n.Left.Defn == n && n.Left.Param.Ntype == nil { defaultlit(&n.Right, nil) n.Left.Type = n.Right.Type } @@ -3386,7 +3386,7 @@ func typecheckas2(n *Node) { // delicate little dance. ll.N = resolve(ll.N) - if ll.N.Defn != n || ll.N.Ntype != nil { + if ll.N.Defn != n || ll.N.Param.Ntype != nil { typecheck(&ll.N, Erv|Easgn) } } @@ -3410,7 +3410,7 @@ func typecheckas2(n *Node) { if ll.N.Type != nil && lr.N.Type != nil { lr.N = assignconv(lr.N, ll.N.Type, "assignment") } - if ll.N.Defn == n && ll.N.Ntype == nil { + if ll.N.Defn == n && ll.N.Param.Ntype == nil { defaultlit(&lr.N, nil) ll.N.Type = lr.N.Type } @@ -3443,7 +3443,7 @@ func typecheckas2(n *Node) { if t.Type != nil && ll.N.Type != nil { checkassignto(t.Type, ll.N) } - if ll.N.Defn == n && ll.N.Ntype == nil { + if ll.N.Defn == n && ll.N.Param.Ntype == nil { ll.N.Type = t.Type } t = structnext(&s) @@ -3482,7 +3482,7 @@ func typecheckas2(n *Node) { if l.Type != nil && l.Type.Etype != TBOOL { checkassignto(Types[TBOOL], l) } - if l.Defn == n && l.Ntype == nil { + if l.Defn == n && l.Param.Ntype == nil { l.Type = Types[TBOOL] } goto out @@ -3646,8 +3646,8 @@ func typecheckdeftype(n *Node) { setlineno(n) n.Type.Sym = n.Sym n.Typecheck = 1 - typecheck(&n.Ntype, Etype) - t := n.Ntype.Type + typecheck(&n.Param.Ntype, Etype) + t := n.Param.Ntype.Type if t == nil { n.Diag = 1 n.Type = nil @@ -3757,10 +3757,10 @@ func typecheckdef(n *Node) *Node { break case OLITERAL: - if n.Ntype != nil { - typecheck(&n.Ntype, Etype) - n.Type = n.Ntype.Type - n.Ntype = nil + if n.Param.Ntype != nil { + typecheck(&n.Param.Ntype, Etype) + n.Type = n.Param.Ntype.Type + n.Param.Ntype = nil if n.Type == nil { n.Diag = 1 goto ret @@ -3809,9 +3809,9 @@ func typecheckdef(n *Node) *Node { n.Type = e.Type case ONAME: - if n.Ntype != nil { - typecheck(&n.Ntype, Etype) - n.Type = n.Ntype.Type + if n.Param.Ntype != nil { + typecheck(&n.Param.Ntype, Etype) + n.Type = n.Param.Ntype.Type if n.Type == nil { n.Diag = 1 diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 04f0491c15..d5eb44c0bb 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -615,7 +615,7 @@ func walkexpr(np **Node, init **NodeList) { n.Left.Func.Enter = nil // Replace OCLOSURE with ONAME/PFUNC. - n.Left = n.Left.Closure.Nname + n.Left = n.Left.Param.Closure.Nname // Update type of OCALLFUNC node. // Output arguments had not changed, but their offsets could. @@ -2687,8 +2687,8 @@ func paramstoheap(argin **Type, out int) *NodeList { } nn = list(nn, Nod(OAS, v.Name.Heapaddr, v.Alloc)) if v.Class&^PHEAP != PPARAMOUT { - as = Nod(OAS, v, v.Stackparam) - v.Stackparam.Typecheck = 1 + as = Nod(OAS, v, v.Param.Stackparam) + v.Param.Stackparam.Typecheck = 1 typecheck(&as, Etop) as = applywritebarrier(as, &nn) nn = list(nn, as) @@ -2711,7 +2711,7 @@ func returnsfromheap(argin **Type) *NodeList { if v == nil || v.Class != PHEAP|PPARAMOUT { continue } - nn = list(nn, Nod(OAS, v.Stackparam, v)) + nn = list(nn, Nod(OAS, v.Param.Stackparam, v)) } return nn @@ -4026,7 +4026,7 @@ func walkprintfunc(np **Node, init **NodeList) { buf = fmt.Sprintf("print·%d", walkprintfunc_prgen) fn.Nname = newname(Lookup(buf)) fn.Nname.Defn = fn - fn.Nname.Ntype = t + fn.Nname.Param.Ntype = t declare(fn.Nname, PFUNC) oldfn := Curfn diff --git a/src/cmd/compile/internal/gc/y.go b/src/cmd/compile/internal/gc/y.go index 72bce9a465..56b9d04ecb 100644 --- a/src/cmd/compile/internal/gc/y.go +++ b/src/cmd/compile/internal/gc/y.go @@ -2560,7 +2560,7 @@ yydefault: yyVAL.node = Nod(ODCLFUNC, nil, nil) yyVAL.node.Nname = newfuncname(yyDollar[1].sym) yyVAL.node.Nname.Defn = yyVAL.node - yyVAL.node.Nname.Ntype = t // TODO: check if nname already has an ntype + yyVAL.node.Nname.Param.Ntype = t // TODO: check if nname already has an ntype declare(yyVAL.node.Nname, PFUNC) funchdr(yyVAL.node) @@ -2597,7 +2597,7 @@ yydefault: yyVAL.node.Func.Shortname = newfuncname(yyDollar[4].sym) yyVAL.node.Nname = methodname1(yyVAL.node.Func.Shortname, rcvr.Right) yyVAL.node.Nname.Defn = yyVAL.node - yyVAL.node.Nname.Ntype = t + yyVAL.node.Nname.Param.Ntype = t yyVAL.node.Nname.Nointerface = nointerface declare(yyVAL.node.Nname, PFUNC) From 596bb76248dd7844ba3bebcdeab9a7c42ef5855a Mon Sep 17 00:00:00 2001 From: David Chase Date: Wed, 27 May 2015 17:33:46 -0400 Subject: [PATCH 230/232] cmd/compile: reject p-notation floats in Go source files Use pkgimport == nil (or not) to distinguish between parsing .go source files where "p" exponent specifier is not allowed and parsing .a or .o export data where it is. Use that to control error when p-exponent is seen. Fixes #9036 Change-Id: I8924f09c91d4945ef3f20e80a6e544008a94a7e4 Reviewed-on: https://go-review.googlesource.com/10450 Reviewed-by: Russ Cox --- src/cmd/compile/internal/gc/lex.go | 7 ++++++- test/fixedbugs/issue9036.go | 29 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 test/fixedbugs/issue9036.go diff --git a/src/cmd/compile/internal/gc/lex.go b/src/cmd/compile/internal/gc/lex.go index d9ba9b210b..cf41c40964 100644 --- a/src/cmd/compile/internal/gc/lex.go +++ b/src/cmd/compile/internal/gc/lex.go @@ -1434,6 +1434,11 @@ casedot: } caseep: + if importpkg == nil && (c == 'p' || c == 'P') { + // p is allowed in .a/.o imports, + // but not in .go sources. See #9036. + Yyerror("malformed floating point constant") + } cp.WriteByte(byte(c)) c = getc() if c == '+' || c == '-' { @@ -1442,7 +1447,7 @@ caseep: } if !yy_isdigit(c) { - Yyerror("malformed fp constant exponent") + Yyerror("malformed floating point constant exponent") } for yy_isdigit(c) { cp.WriteByte(byte(c)) diff --git a/test/fixedbugs/issue9036.go b/test/fixedbugs/issue9036.go new file mode 100644 index 0000000000..283159e74a --- /dev/null +++ b/test/fixedbugs/issue9036.go @@ -0,0 +1,29 @@ +// errorcheck + +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Expects to see error messages on "p" exponents. + +package main + +import "fmt" + +const ( + x1 = 1.1 // float + x2 = 1e10 // float + x3 = 0x1e10 // integer (e is a hex digit) + x4 = 0x1p10 // ERROR "malformed floating point constant" + x5 = 1p10 // ERROR "malformed floating point constant" + x6 = 0p0 // ERROR "malformed floating point constant" +) + +func main() { + fmt.Printf("%g %T\n", x1, x1) + fmt.Printf("%g %T\n", x2, x2) + fmt.Printf("%g %T\n", x3, x3) + fmt.Printf("%g %T\n", x4, x4) + fmt.Printf("%g %T\n", x5, x5) + fmt.Printf("%g %T\n", x6, x6) +} From 3ba6387bd09d8dd67d04e16725a9a0ffab78756b Mon Sep 17 00:00:00 2001 From: Josh Bleecher Snyder Date: Mon, 18 May 2015 11:09:29 -0700 Subject: [PATCH 231/232] test: re-enable rotate tests Memory usage has been reduced. The tests are still slow, but that is issue #10571. /usr/bin/time shows significant variation in the peak memory usage compiling with tip. This is unsurprising, given GC. Using Go 1.4.2, memory is stable at 410mb. Using tip at d2ee09298, memory ranges from 470mb (+15%) to 534mb (+30%), with a mean of 504mb (+23%), with n=50. Fixes #9933. Change-Id: Id31f3ae086ec324abf70e8f1a8044c4a0c27e274 Reviewed-on: https://go-review.googlesource.com/10211 Reviewed-by: Brad Fitzpatrick --- test/rotate0.go | 2 -- test/rotate1.go | 2 -- test/rotate2.go | 2 -- test/rotate3.go | 2 -- 4 files changed, 8 deletions(-) diff --git a/test/rotate0.go b/test/rotate0.go index 9c4f560f74..400b225cf7 100644 --- a/test/rotate0.go +++ b/test/rotate0.go @@ -1,5 +1,3 @@ -// skip - // runoutput ./rotate.go // Copyright 2013 The Go Authors. All rights reserved. diff --git a/test/rotate1.go b/test/rotate1.go index 2d9b797922..98b0b1c849 100644 --- a/test/rotate1.go +++ b/test/rotate1.go @@ -1,5 +1,3 @@ -// skip - // runoutput ./rotate.go // Copyright 2013 The Go Authors. All rights reserved. diff --git a/test/rotate2.go b/test/rotate2.go index 9044625312..c50f8ce73b 100644 --- a/test/rotate2.go +++ b/test/rotate2.go @@ -1,5 +1,3 @@ -// skip - // runoutput ./rotate.go // Copyright 2013 The Go Authors. All rights reserved. diff --git a/test/rotate3.go b/test/rotate3.go index b6b71c8b47..73d47d8524 100644 --- a/test/rotate3.go +++ b/test/rotate3.go @@ -1,5 +1,3 @@ -// skip - // runoutput ./rotate.go // Copyright 2013 The Go Authors. All rights reserved. From ccc037699e2966b7c79ba84c67471cef5e67a3b8 Mon Sep 17 00:00:00 2001 From: Robert Griesemer Date: Thu, 28 May 2015 11:58:42 -0700 Subject: [PATCH 232/232] go/types: fix error message for embedded non-interface types in interfaces Fixes #10979. Change-Id: Iac25645ba8181a56a75ddfcd29ff6d64c15c4f57 Reviewed-on: https://go-review.googlesource.com/10466 Reviewed-by: Alan Donovan --- src/go/types/testdata/issues.src | 24 ++++++++++++++++++++++++ src/go/types/typexpr.go | 18 ++++++------------ 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/go/types/testdata/issues.src b/src/go/types/testdata/issues.src index d08e0fd878..595a6342b7 100644 --- a/src/go/types/testdata/issues.src +++ b/src/go/types/testdata/issues.src @@ -71,3 +71,27 @@ func issue9473(a []int, b ...int) { append_(f0(), f1()... /* ERROR cannot use */ ) append_(f0(), f2()... /* ERROR cannot use */ ) } + +// Check that embedding a non-interface type in an interface results in a good error message. +func issue10979() { + type _ interface { + int /* ERROR int is not an interface */ + } + type T struct{} + type _ interface { + T /* ERROR T is not an interface */ + } + type _ interface { + nosuchtype /* ERROR undeclared name: nosuchtype */ + } + type _ interface { + fmt /* ERROR Nosuchtype not declared by package fmt */ .Nosuchtype + } + type _ interface { + nosuchpkg /* ERROR undeclared name: nosuchpkg */ .Nosuchtype + } + type I interface { + I /* ERROR I\.m \(value of type func\(I\)\) is not a type */ .m + m() + } +} diff --git a/src/go/types/typexpr.go b/src/go/types/typexpr.go index f4e4dcb040..3fc1574e80 100644 --- a/src/go/types/typexpr.go +++ b/src/go/types/typexpr.go @@ -525,20 +525,14 @@ func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d for _, e := range embedded { pos := e.Pos() typ := check.typExpr(e, nil, path) + // Determine underlying embedded (possibly incomplete) type + // by following its forward chain. named, _ := typ.(*Named) - if named == nil { - if typ != Typ[Invalid] { - check.invalidAST(pos, "%s is not named type", typ) - } - continue - } - // determine underlying (possibly incomplete) type - // by following its forward chain - u := underlying(named) - embed, _ := u.(*Interface) + under := underlying(named) + embed, _ := under.(*Interface) if embed == nil { - if u != Typ[Invalid] { - check.errorf(pos, "%s is not an interface", named) + if typ != Typ[Invalid] { + check.errorf(pos, "%s is not an interface", typ) } continue }